WoWInterface SVN DressToKill

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /trunk
    from Rev 2 to Rev 3
    Reverse comparison

Rev 2 → Rev 3

fonts/VeraMono.ttf Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes : Added: svn:mime-type + application/octet-stream
fonts/COPYRIGHT.TXT New file
0,0 → 1,124
Bitstream Vera Fonts Copyright
 
The fonts have a generous copyright, allowing derivative works (as
long as "Bitstream" or "Vera" are not in the names), and full
redistribution (so long as they are not *sold* by themselves). They
can be be bundled, redistributed and sold with any software.
 
The fonts are distributed under the following copyright:
 
Copyright
=========
 
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream
Vera is a trademark of Bitstream, Inc.
 
Permission is hereby granted, free of charge, to any person obtaining
a copy of the fonts accompanying this license ("Fonts") and associated
documentation files (the "Font Software"), to reproduce and distribute
the Font Software, including without limitation the rights to use,
copy, merge, publish, distribute, and/or sell copies of the Font
Software, and to permit persons to whom the Font Software is furnished
to do so, subject to the following conditions:
 
The above copyright and trademark notices and this permission notice
shall be included in all copies of one or more of the Font Software
typefaces.
 
The Font Software may be modified, altered, or added to, and in
particular the designs of glyphs or characters in the Fonts may be
modified and additional glyphs or characters may be added to the
Fonts, only if the fonts are renamed to names not containing either
the words "Bitstream" or the word "Vera".
 
This License becomes null and void to the extent applicable to Fonts
or Font Software that has been modified and is distributed under the
"Bitstream Vera" names.
 
The Font Software may be sold as part of a larger software package but
no copy of one or more of the Font Software typefaces may be sold by
itself.
 
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT
SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
 
Except as contained in this notice, the names of Gnome, the Gnome
Foundation, and Bitstream Inc., shall not be used in advertising or
otherwise to promote the sale, use or other dealings in this Font
Software without prior written authorization from the Gnome Foundation
or Bitstream Inc., respectively. For further information, contact:
fonts at gnome dot org.
 
Copyright FAQ
=============
 
1. I don't understand the resale restriction... What gives?
 
Bitstream is giving away these fonts, but wishes to ensure its
competitors can't just drop the fonts as is into a font sale system
and sell them as is. It seems fair that if Bitstream can't make money
from the Bitstream Vera fonts, their competitors should not be able to
do so either. You can sell the fonts as part of any software package,
however.
 
2. I want to package these fonts separately for distribution and
sale as part of a larger software package or system. Can I do so?
 
Yes. A RPM or Debian package is a "larger software package" to begin
with, and you aren't selling them independently by themselves.
See 1. above.
 
3. Are derivative works allowed?
Yes!
 
4. Can I change or add to the font(s)?
Yes, but you must change the name(s) of the font(s).
 
5. Under what terms are derivative works allowed?
 
You must change the name(s) of the fonts. This is to ensure the
quality of the fonts, both to protect Bitstream and Gnome. We want to
ensure that if an application has opened a font specifically of these
names, it gets what it expects (though of course, using fontconfig,
substitutions could still could have occurred during font
opening). You must include the Bitstream copyright. Additional
copyrights can be added, as per copyright law. Happy Font Hacking!
 
6. If I have improvements for Bitstream Vera, is it possible they might get
adopted in future versions?
 
Yes. The contract between the Gnome Foundation and Bitstream has
provisions for working with Bitstream to ensure quality additions to
the Bitstream Vera font family. Please contact us if you have such
additions. Note, that in general, we will want such additions for the
entire family, not just a single font, and that you'll have to keep
both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add
glyphs to the font, they must be stylistically in keeping with Vera's
design. Vera cannot become a "ransom note" font. Jim Lyles will be
providing a document describing the design elements used in Vera, as a
guide and aid for people interested in contributing to Vera.
 
7. I want to sell a software package that uses these fonts: Can I do so?
 
Sure. Bundle the fonts with your software and sell your software
with the fonts. That is the intent of the copyright.
 
8. If applications have built the names "Bitstream Vera" into them,
can I override this somehow to use fonts of my choosing?
 
This depends on exact details of the software. Most open source
systems and software (e.g., Gnome, KDE, etc.) are now converting to
use fontconfig (see www.fontconfig.org) to handle font configuration,
selection and substitution; it has provisions for overriding font
names and subsituting alternatives. An example is provided by the
supplied local.conf file, which chooses the family Bitstream Vera for
"sans", "serif" and "monospace". Other software (e.g., the XFree86
core server) has other mechanisms for font substitution.
 
DressToKillOptions.lua New file
0,0 → 1,260
local L = DressToKillLocals
 
local frame = CreateFrame("Frame", "DressToKillOptionsFrame", UIParent)
frame.name = L["Dress to Kill"]
frame:Hide()
frame:SetScript("OnShow", function(frame)
local title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
title:SetPoint("TOPLEFT", frame, "TOPLEFT", 15, -15)
title:SetPoint("BOTTOMRIGHT", frame, "TOPRIGHT", 10, -45)
title:SetJustifyH("LEFT")
title:SetJustifyV("TOP")
title:SetText(L["Dress to Kill"])
 
--[[
local check = CreateFrame("CheckButton", "DressToKillEnableButton", frame, "UICheckButtonTemplate")
check:SetPoint("TOPLEFT", frame, "TOPLEFT", 15, -50)
check:SetWidth(24)
check:SetHeight(24)
check:SetChecked(AddonLoaderSV.silent)
AddonLoaderCheckButtonText:SetText("Hide Loading Messages")
AddonLoaderCheckButtonText:SetPoint("LEFT", check, "RIGHT")
check:SetScript("OnClick", function()
AddonLoaderSV.silent = not AddonLoaderSV.silent
check:SetChecked(AddonLoaderSV.silent)
end)
--]]
 
local explain = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
explain:SetTextColor(1,1,1)
explain:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, 0)
explain:SetPoint("RIGHT", frame, "RIGHT", -15, 0)
explain:SetJustifyH("LEFT")
explain:SetJustifyV("TOP")
explain:SetHeight(100)
explain:SetText(L["Dress to Kill is a simple addon which takes the guesswork out of building equipment sets for your character. Rather than use a complicated library to scan your items for potential bonuses it sacrifices speed for optimal results. It will scan through your inventory trying items on until it find the most effective mix. You can customize the way equipment is evaluated through a custom evaluation function or you can simply use one of the built in functions."])
 
local function dropdown_onclick()
local selected = this.value
 
if selected == "__NEW__" then
-- Make a new entry
StaticPopup_Show("DRESSTOKILL_NEW_FUNCTION")
else
DressToKill.profile.selected = selected
UIDropDownMenu_SetSelectedValue(dropdown, selected)
editbox:SetText(DressToKill.profile.weightFuncs[selected].handler)
DressToKillDeleteButton:Enable()
end
end
 
local function initdropdown()
local sort = {}
for k,v in pairs(DressToKill.profile.weightFuncs) do
table.insert(sort, k)
end
table.sort(sort)
 
local info = UIDropDownMenu_CreateInfo()
 
for idx,name in ipairs(sort) do
info.text = name
info.value = name
info.func = dropdown_onclick
info.checked = false
UIDropDownMenu_AddButton(info)
end
 
info.text = L["New weight function"]
info.value = "__NEW__"
info.func = dropdown_onclick
info.checked = false
UIDropDownMenu_AddButton(info)
end
 
local dropdownlabel = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
dropdownlabel:SetPoint("TOPLEFT", explain, "BOTTOMLEFT", 0, -10)
dropdownlabel:SetText("Weight Function:")
dropdownlabel:SetHeight(15)
dropdownlabel:SetWidth(100)
 
dropdown = CreateFrame("Frame", "DressToKillDropDown", frame, "UIDropDownMenuTemplate")
dropdown:EnableMouse(true)
dropdown:SetPoint("LEFT", dropdownlabel, "RIGHT")
UIDropDownMenu_Initialize(dropdown, initdropdown)
UIDropDownMenu_SetSelectedValue(dropdown, DressToKill.profile.selected)
UIDropDownMenu_SetWidth(160, dropdown)
UIDropDownMenu_JustifyText("LEFT", dropdown)
 
editbox = CreateFrame("EditBox", "DressToKillEditBox", frame)
editbox:SetPoint("TOPLEFT", dropdownlabel, "BOTTOMLEFT", 0, -15)
editbox:SetPoint("RIGHT", -15, 0)
editbox:SetPoint("BOTTOM", 0, 45)
editbox:SetFont("Interface\\AddOns\\WowLua\\fonts\\VeraMono.ttf", 12)
editbox:SetTextInsets(8,8,8,8)
editbox:SetBackdrop({
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
edgeSize = 16,
insets = {left = 4, right = 4, top = 4, bottom = 4}
})
editbox:SetBackdropColor(.1,.1,.1,.3)
editbox:SetBackdropBorderColor(.5,.5,.5)
editbox:SetMultiLine(true)
editbox:SetAutoFocus(false)
editbox:ClearFocus()
editbox:SetScript("OnEscapePressed", function(self)
self:ClearFocus()
end)
 
local selected = DressToKill.profile.selected
if selected then
editbox:SetText(DressToKill.profile.weightFuncs[selected].handler or "")
else
editbox:SetText("-- Select an existing weight function from the above dropdown or create a new function.")
end
 
IndentationLib.enable(editbox)
 
reset = CreateFrame("Button", "DressToKillResetButton", frame, "UIPanelButtonTemplate2")
reset:SetText(L["Reset"])
reset:SetWidth(80)
reset:SetPoint("TOPRIGHT", editbox, "BOTTOMRIGHT", 0, -5)
reset:SetScript("OnClick", function(self)
local selected = DressToKill.profile.selected
local handler = DressToKill.profile.weightFuncs[selected].handler
editbox:SetText(handler)
self:Disable()
end)
reset.counter = 0
reset:SetScript("OnUpdate", function(self, elapsed)
self.counter = self.counter + elapsed
if self.counter > 0.2 then
self.counter = 0
-- Do check
local selected = DressToKill.profile.selected
if not selected then
self:Disable()
return
end
local source = DressToKill.profile.weightFuncs[selected].handler
if editbox:GetText() ~= source then
self:Enable()
else
self:Disable()
end
end
end)
 
save = CreateFrame("Button", "DressToKillSaveButton", frame, "UIPanelButtonTemplate2")
save:SetText(L["Save"])
save:SetWidth(80)
save:SetPoint("RIGHT", reset, "LEFT", -3, 0)
save:SetScript("OnClick", function(self)
local selected = DressToKill.profile.selected
local entry = DressToKill.profile.weightFuncs[selected]
entry.handler = editbox:GetText()
self:Disable()
end)
save.counter = 0
save:SetScript("OnUpdate", function(self, elapsed)
self.counter = self.counter + elapsed
if self.counter > 0.2 then
self.counter = 0
-- Do check
local selected = DressToKill.profile.selected
if not selected then
self:Disable()
return
end
local source = DressToKill.profile.weightFuncs[selected].handler
if editbox:GetText() ~= source then
self:Enable()
else
self:Disable()
end
end
end)
 
delete = CreateFrame("Button", "DressToKillDeleteButton", frame, "UIPanelButtonTemplate2")
delete:SetText(L["Delete"])
delete:SetWidth(80)
delete:SetPoint("RIGHT", save, "LEFT", -3, 0)
delete:SetScript("OnClick", function(self)
local selected = DressToKill.profile.selected
DressToKill.profile.selected = nil
DressToKill.profile.weightFuncs[selected] = nil
UIDropDownMenu_Initialize(dropdown, initdropdown)
UIDropDownMenu_SetSelectedValue(dropdown, nil)
editbox:SetText("")
self:Disable()
end)
 
local function OnAccept()
local name = this:GetParent():GetName().."EditBox"
local button = getglobal(name)
local text = button:GetText()
DressToKill.profile.selected = text
DressToKill.profile.weightFuncs[text] = {
handler = ""
}
UIDropDownMenu_Initialize(dropdown, initdropdown)
UIDropDownMenu_SetSelectedValue(dropdown, text)
editbox:SetText([[
--This function receives a single parameter
--'link', which is the link of the item
--being tested. Example usage to max
--the player's health:
--
-- return UnitHealthMax("player")]])
end
 
-- Create a static popup
StaticPopupDialogs["DRESSTOKILL_NEW_FUNCTION"] = {
text = TEXT("Enter the name of the function you'd like to create"),
button1 = TEXT(OKAY),
button2 = TEXT(CANCEL),
OnAccept = OnAccept,
timeout = 0,
whileDead = 1,
exclusive = 1,
showAlert = 1,
hideOnEscape = 1,
hasEditBox = 1,
maxLetters = 32,
OnShow = function()
getglobal(this:GetName().."Button1"):Disable();
getglobal(this:GetName().."EditBox"):SetFocus();
end,
OnHide = function()
if ( ChatFrameEditBox:IsVisible() ) then
ChatFrameEditBox:SetFocus();
end
getglobal(this:GetName().."EditBox"):SetText("");
end,
EditBoxOnEnterPressed = function()
if ( getglobal(this:GetParent():GetName().."Button1"):IsEnabled() == 1 ) then
OnAccept()
this:GetParent():Hide()
end
end,
EditBoxOnTextChanged = function ()
local editBox = getglobal(this:GetParent():GetName().."EditBox");
local txt = editBox:GetText()
if #txt > 0 and not DressToKill.profile.weightFuncs[txt] then
getglobal(this:GetParent():GetName().."Button1"):Enable();
else
getglobal(this:GetParent():GetName().."Button1"):Disable();
end
end,
EditBoxOnEscapePressed = function()
this:GetParent():Hide();
ClearCursor();
end
}
 
frame:SetScript("OnShow", nil)
end )
 
InterfaceOptions_AddCategory(frame)
 
DressToKill.lua
4,7 → 4,7
DressToKill = {}
local L = DressToKillLocals
 
local DEBUG = true
local DEBUG = false
local function debug(fmt, ...)
if DEBUG then
local msg = "|cffffff78DressToKill:|r "
17,6 → 17,16
end
end
 
local function print(fmt, ...)
local msg = "|cffffff78DressToKill:|r "
if select("#", ...) > 0 then
msg = msg .. fmt:format(...)
else
msg = msg .. fmt
end
ChatFrame1:AddMessage(msg)
end
 
local slotNames = {
BackSlot = L["Back"],
ChestSlot = L["Chest"],
40,7 → 50,10
local slotIds = {}
 
function DressToKill:Initialize()
debug(L["Scanning for inventory slot Ids"])
-- Only run this code once
if self.initDone then return end
self.initDone = true
 
for slot in pairs(slotNames) do
local id = GetInventorySlotInfo(slot)
if id then
51,6 → 64,21
return
end
end
 
DressToKillDB = DressToKillDB or {}
self.profile = DressToKillDB
self.profile.weightFuncs = self.profile.weightFuncs or {
["Healing/Mana"] = {handler = [[local healing = GetSpellBonusHealing()
local mana = UnitManaMax("player")
return (healing * 1.0) + (mana * 0.7)]]},
["Health/Armor"] = {handler = [[local health = UnitHealthMax("player")
local base, pos, neg = UnitArmor("player")
local armor = base + pos - neg
return (health * 1.0) + (armor * 0.8)]]},
["Attack Power"] = {handler = [[local base, pos, neg = UnitAttackPower("player")
local apower = base + pos - neg
return apower]]},
}
end
 
function DressToKill:FindEmptySlot()
63,8 → 91,9
end
end
 
local function scanFunction()
local function scanFunction(weightFunction)
debug(L["Beginning an inventory scan"])
UIErrorsFrame:AddMessage("Please do not change buffs or forums during scan...", 1, 0.2, 0.2)
 
local blacklist = {}
local slotAvail = {}
84,7 → 113,7
-- We current have an item equipped, so stash that item elsewhere
local bag,slot = DressToKill:FindEmptySlot()
if not bag or not slot then
debug(L["Out of inventory space, cannot proceed"])
print(L["Out of inventory space, cannot proceed"])
return
end
 
100,7 → 129,8
if not blacklist[mask] then
local equipped = DressToKill:EquipItem(slotId, mask, stash)
if equipped then
local score = DressToKill:EvaluateScore()
local link = GetInventoryItemLink(slotId)
local score = weightFunction(link)
if score >= maxScore then
maxScore = score
winner = mask
116,35 → 146,14
end
end
-- Now equip the item that won this round and blacklist
debug(L["Equipping winning item"])
DressToKill:EquipItem(slotId, winner, stash)
local link = GetInventoryItemLink(slotId)
print("Chose %s as the winning item for this slot", link)
blacklist[winner] = true
end
debug(L["All done trying clothes on!"])
print("Evaluation complete!")
end
 
function DressToKill:EvaluateScore()
--local base,pos,neg = UnitAttackPower("player")
--return base + pos - neg
 
--[[
local health = UnitHealthMax("player")
local base, pos, neg = UnitArmor("player")
local armor = base + pos - neg
local score = (health * 0.8) + (armor * 1.0)
return score
 
-- 1.0/0.8 - 7430 health, 7910 armor
-- 1.0/1.0 - 7430 health, 7910 armor
-- 0.8/1.0 - 7390 health, 7948 armor
--]]
 
local mana = UnitManaMax("player")
local healing = GetSpellBonusHealing()
local score = (mana * 1.0) + (healing * 1.0)
return score
end
 
function DressToKill:EquipItem(slotId, mask, stash)
local bag,slot = DressToKill:ItemInBag(mask)
local eqslot = DressToKill:ItemEquipped(mask)
212,10 → 221,49
SLASH_DRESSTOKILL3 = "/dresstokill"
 
SlashCmdList["DRESSTOKILL"] = function(msg, editbox)
debug(L["Dressing to kill..."])
DressToKill:Initialize()
if msg and msg:lower():match("%s*config%s*") then
InterfaceOptionsFrame_OpenToFrame(DressToKillOptionsFrame)
return
end
 
-- Check to see if a function was specified on the commandline
local weightName = msg:match("^%s*(.+)$")
if not weightName then
if DressToKill.profile.selected then
weightName = DressToKill.profile.selected
else
print(L["You don't have a default weight function selected"])
return
end
end
 
-- Make sure the function actually exist
if not DressToKill.profile.weightFuncs[weightName] then
print(L["The weight function '%s' doesn't exist"], weightName)
return
end
 
-- Compile the function
local source = DressToKill.profile.weightFuncs[weightName].handler
local weightFunction,err = loadstring("return function (link) " .. source .. " end")
 
if not weightFunction then
print("Failed to compile weight function: " .. tostring(err))
return
else
local succ,err = pcall(weightFunction)
if not succ then
print("Failed when running weight function: " .. tostring(err))
return
else
weightFunction = err
end
end
 
print(L["Dressing to kill with %s..."], weightName)
DressToKill.currentThread = coroutine.create(scanFunction)
coroutine.resume(DressToKill.currentThread)
coroutine.resume(DressToKill.currentThread, weightFunction)
end
 
local ITEM_INVENTORY_PLAYER = 0x00100000
FAIAP.lua New file
0,0 → 1,1326
-- For All Indents And Purposes
local revision = 18
-- Maintainer: kristofer.karlsson@gmail.com
 
-- For All Indents And Purposes -
-- a indentation + syntax highlighting library
-- All valid lua code should be processed correctly.
 
-- Usage (for developers)
--------
-- Variant 1: - non embedded
-- 1) Add ForAllIndentsAndPurposes to your dependencies (or optional dependencies)
 
-- Variant 2: - embedded
-- 1.a) Copy indent.lua to your addon directory
-- 1.b) Put indent.lua first in your list of files in the TOC
 
-- For both variants:
-- 2) hook the editboxes that you want to have indentation like this:
-- IndentationLib.enable(editbox [, colorTable [, tabWidth] ])
-- if you don't select a color table, it will use the default.
-- Read through this code for further usage help.
-- (The documentation IS the code)
 
if not IndentationLib then
IndentationLib = {}
end
 
if not IndentationLib.revision or revision > IndentationLib.revision then
local lib = IndentationLib
lib.revision = revision
 
local stringlen = string.len
local stringformat = string.format
local stringfind = string.find
local stringsub = string.sub
local stringbyte = string.byte
local stringchar = string.char
local stringrep = string.rep
local stringgsub = string.gsub
 
local workingTable = {}
local workingTable2 = {}
local function tableclear(t)
for k in next,t do
t[k] = nil
end
end
 
local function stringinsert(s, pos, insertStr)
return stringsub(s, 1, pos) .. insertStr .. stringsub(s, pos + 1)
end
lib.stringinsert = stringinsert
 
local function stringdelete(s, pos1, pos2)
return stringsub(s, 1, pos1 - 1) .. stringsub(s, pos2 + 1)
end
lib.stringdelete = stringdelete
 
-- token types
local tokens = {}
lib.tokens = tokens
 
tokens.TOKEN_UNKNOWN = 0
tokens.TOKEN_NUMBER = 1
tokens.TOKEN_LINEBREAK = 2
tokens.TOKEN_WHITESPACE = 3
tokens.TOKEN_IDENTIFIER = 4
tokens.TOKEN_ASSIGNMENT = 5
tokens.TOKEN_EQUALITY = 6
tokens.TOKEN_MINUS = 7
tokens.TOKEN_COMMENT_SHORT = 8
tokens.TOKEN_COMMENT_LONG = 9
tokens.TOKEN_STRING = 10
tokens.TOKEN_LEFTBRACKET = 11
tokens.TOKEN_PERIOD = 12
tokens.TOKEN_DOUBLEPERIOD = 13
tokens.TOKEN_TRIPLEPERIOD = 14
tokens.TOKEN_LTE = 15
tokens.TOKEN_LT = 16
tokens.TOKEN_GTE = 17
tokens.TOKEN_GT = 18
tokens.TOKEN_NOTEQUAL = 19
tokens.TOKEN_COMMA = 20
tokens.TOKEN_SEMICOLON = 21
tokens.TOKEN_COLON = 22
tokens.TOKEN_LEFTPAREN = 23
tokens.TOKEN_RIGHTPAREN = 24
tokens.TOKEN_PLUS = 25
tokens.TOKEN_SLASH = 27
tokens.TOKEN_LEFTWING = 28
tokens.TOKEN_RIGHTWING = 29
tokens.TOKEN_CIRCUMFLEX = 30
tokens.TOKEN_ASTERISK = 31
tokens.TOKEN_RIGHTBRACKET = 32
tokens.TOKEN_KEYWORD = 33
tokens.TOKEN_SPECIAL = 34
tokens.TOKEN_VERTICAL = 35
tokens.TOKEN_TILDE = 36
 
-- WoW specific tokens
tokens.TOKEN_COLORCODE_START = 37
tokens.TOKEN_COLORCODE_STOP = 38
 
-- new as of lua 5.1
tokens.TOKEN_HASH = 39
tokens.TOKEN_PERCENT = 40
 
 
-- ascii codes
local bytes = {}
lib.bytes = bytes
bytes.BYTE_LINEBREAK_UNIX = stringbyte("\n")
bytes.BYTE_LINEBREAK_MAC = stringbyte("\r")
bytes.BYTE_SINGLE_QUOTE = stringbyte("'")
bytes.BYTE_DOUBLE_QUOTE = stringbyte('"')
bytes.BYTE_0 = stringbyte("0")
bytes.BYTE_9 = stringbyte("9")
bytes.BYTE_PERIOD = stringbyte(".")
bytes.BYTE_SPACE = stringbyte(" ")
bytes.BYTE_TAB = stringbyte("\t")
bytes.BYTE_E = stringbyte("E")
bytes.BYTE_e = stringbyte("e")
bytes.BYTE_MINUS = stringbyte("-")
bytes.BYTE_EQUALS = stringbyte("=")
bytes.BYTE_LEFTBRACKET = stringbyte("[")
bytes.BYTE_RIGHTBRACKET = stringbyte("]")
bytes.BYTE_BACKSLASH = stringbyte("\\")
bytes.BYTE_COMMA = stringbyte(",")
bytes.BYTE_SEMICOLON = stringbyte(";")
bytes.BYTE_COLON = stringbyte(":")
bytes.BYTE_LEFTPAREN = stringbyte("(")
bytes.BYTE_RIGHTPAREN = stringbyte(")")
bytes.BYTE_TILDE = stringbyte("~")
bytes.BYTE_PLUS = stringbyte("+")
bytes.BYTE_SLASH = stringbyte("/")
bytes.BYTE_LEFTWING = stringbyte("{")
bytes.BYTE_RIGHTWING = stringbyte("}")
bytes.BYTE_CIRCUMFLEX = stringbyte("^")
bytes.BYTE_ASTERISK = stringbyte("*")
bytes.BYTE_LESSTHAN = stringbyte("<")
bytes.BYTE_GREATERTHAN = stringbyte(">")
-- WoW specific chars
bytes.BYTE_VERTICAL = stringbyte("|")
bytes.BYTE_r = stringbyte("r")
bytes.BYTE_c = stringbyte("c")
 
-- new as of lua 5.1
bytes.BYTE_HASH = stringbyte("#")
bytes.BYTE_PERCENT = stringbyte("%")
 
 
local linebreakCharacters = {}
lib.linebreakCharacters = linebreakCharacters
linebreakCharacters[bytes.BYTE_LINEBREAK_UNIX] = 1
linebreakCharacters[bytes.BYTE_LINEBREAK_MAC] = 1
 
local whitespaceCharacters = {}
lib.whitespaceCharacters = whitespaceCharacters
whitespaceCharacters[bytes.BYTE_SPACE] = 1
whitespaceCharacters[bytes.BYTE_TAB] = 1
 
local specialCharacters = {}
lib.specialCharacters = specialCharacters
specialCharacters[bytes.BYTE_PERIOD] = -1
specialCharacters[bytes.BYTE_LESSTHAN] = -1
specialCharacters[bytes.BYTE_GREATERTHAN] = -1
specialCharacters[bytes.BYTE_LEFTBRACKET] = -1
specialCharacters[bytes.BYTE_EQUALS] = -1
specialCharacters[bytes.BYTE_MINUS] = -1
specialCharacters[bytes.BYTE_SINGLE_QUOTE] = -1
specialCharacters[bytes.BYTE_DOUBLE_QUOTE] = -1
specialCharacters[bytes.BYTE_TILDE] = -1
specialCharacters[bytes.BYTE_RIGHTBRACKET] = tokens.TOKEN_RIGHTBRACKET
specialCharacters[bytes.BYTE_COMMA] = tokens.TOKEN_COMMA
specialCharacters[bytes.BYTE_COLON] = tokens.TOKEN_COLON
specialCharacters[bytes.BYTE_SEMICOLON] = tokens.TOKEN_SEMICOLON
specialCharacters[bytes.BYTE_LEFTPAREN] = tokens.TOKEN_LEFTPAREN
specialCharacters[bytes.BYTE_RIGHTPAREN] = tokens.TOKEN_RIGHTPAREN
specialCharacters[bytes.BYTE_PLUS] = tokens.TOKEN_PLUS
specialCharacters[bytes.BYTE_SLASH] = tokens.TOKEN_SLASH
specialCharacters[bytes.BYTE_LEFTWING] = tokens.TOKEN_LEFTWING
specialCharacters[bytes.BYTE_RIGHTWING] = tokens.TOKEN_RIGHTWING
specialCharacters[bytes.BYTE_CIRCUMFLEX] = tokens.TOKEN_CIRCUMFLEX
specialCharacters[bytes.BYTE_ASTERISK] = tokens.TOKEN_ASTERISK
-- WoW specific
specialCharacters[bytes.BYTE_VERTICAL] = -1
-- new as of lua 5.1
specialCharacters[bytes.BYTE_HASH] = tokens.TOKEN_HASH
specialCharacters[bytes.BYTE_PERCENT] = tokens.TOKEN_PERCENT
 
local function nextNumberExponentPartInt(text, pos)
while true do
local byte = stringbyte(text, pos)
if not byte then
return tokens.TOKEN_NUMBER, pos
end
 
if byte >= bytes.BYTE_0 and byte <= bytes.BYTE_9 then
pos = pos + 1
else
return tokens.TOKEN_NUMBER, pos
end
end
end
 
local function nextNumberExponentPart(text, pos)
local byte = stringbyte(text, pos)
if not byte then
return tokens.TOKEN_NUMBER, pos
end
 
if byte == bytes.BYTE_MINUS then
-- handle this case: a = 1.2e-- some comment
-- i decide to let 1.2e be parsed as a a number
byte = stringbyte(text, pos + 1)
if byte == bytes.BYTE_MINUS then
return tokens.TOKEN_NUMBER, pos
end
return nextNumberExponentPartInt(text, pos + 1)
end
 
return nextNumberExponentPartInt(text, pos)
end
 
local function nextNumberFractionPart(text, pos)
while true do
local byte = stringbyte(text, pos)
if not byte then
return tokens.TOKEN_NUMBER, pos
end
 
if byte >= bytes.BYTE_0 and byte <= bytes.BYTE_9 then
pos = pos + 1
elseif byte == bytes.BYTE_E or byte == bytes.BYTE_e then
return nextNumberExponentPart(text, pos + 1)
else
return tokens.TOKEN_NUMBER, pos
end
end
end
 
local function nextNumberIntPart(text, pos)
while true do
local byte = stringbyte(text, pos)
if not byte then
return tokens.TOKEN_NUMBER, pos
end
 
if byte >= bytes.BYTE_0 and byte <= bytes.BYTE_9 then
pos = pos + 1
elseif byte == bytes.BYTE_PERIOD then
return nextNumberFractionPart(text, pos + 1)
elseif byte == bytes.BYTE_E or byte == bytes.BYTE_e then
return nextNumberExponentPart(text, pos + 1)
else
return tokens.TOKEN_NUMBER, pos
end
end
end
 
local function nextIdentifier(text, pos)
while true do
local byte = stringbyte(text, pos)
 
if not byte or
linebreakCharacters[byte] or
whitespaceCharacters[byte] or
specialCharacters[byte] then
return tokens.TOKEN_IDENTIFIER, pos
end
pos = pos + 1
end
end
 
-- returns false or: true, nextPos, equalsCount
local function isBracketStringNext(text, pos)
local byte = stringbyte(text, pos)
if byte == bytes.BYTE_LEFTBRACKET then
local pos2 = pos + 1
byte = stringbyte(text, pos2)
while byte == bytes.BYTE_EQUALS do
pos2 = pos2 + 1
byte = stringbyte(text, pos2)
end
if byte == bytes.BYTE_LEFTBRACKET then
return true, pos2 + 1, (pos2 - 1) - pos
else
return false
end
else
return false
end
end
 
 
-- Already parsed the [==[ part when get here
local function nextBracketString(text, pos, equalsCount)
local state = 0
while true do
local byte = stringbyte(text, pos)
if not byte then
return tokens.TOKEN_STRING, pos
end
 
if byte == bytes.BYTE_RIGHTBRACKET then
if state == 0 then
state = 1
elseif state == equalsCount + 1 then
return tokens.TOKEN_STRING, pos + 1
else
state = 0
end
elseif byte == bytes.BYTE_EQUALS then
if state > 0 then
state = state + 1
end
else
state = 0
end
pos = pos + 1
end
end
 
local function nextComment(text, pos)
-- When we get here we have already parsed the "--"
-- Check for long comment
local isBracketString, nextPos, equalsCount = isBracketStringNext(text, pos)
if isBracketString then
local tokenType, nextPos2 = nextBracketString(text, nextPos, equalsCount)
return tokens.TOKEN_COMMENT_LONG, nextPos2
end
 
local byte = stringbyte(text, pos)
 
-- Short comment, find the first linebreak
while true do
byte = stringbyte(text, pos)
if not byte then
return tokens.TOKEN_COMMENT_SHORT, pos
end
if linebreakCharacters[byte] then
return tokens.TOKEN_COMMENT_SHORT, pos
end
pos = pos + 1
end
end
 
local function nextString(text, pos, character)
local even = true
while true do
local byte = stringbyte(text, pos)
if not byte then
return tokens.TOKEN_STRING, pos
end
 
if byte == character then
if even then
return tokens.TOKEN_STRING, pos + 1
end
end
if byte == bytes.BYTE_BACKSLASH then
even = not even
else
even = true
end
 
pos = pos + 1
end
end
 
-- INPUT
-- 1: text: text to search in
-- 2: tokenPos: where to start searching
-- OUTPUT
-- 1: token type
-- 2: position after the last character of the token
function nextToken(text, pos)
local byte = stringbyte(text, pos)
if not byte then
return nil
end
 
if linebreakCharacters[byte] then
return tokens.TOKEN_LINEBREAK, pos + 1
end
 
if whitespaceCharacters[byte] then
while true do
pos = pos + 1
byte = stringbyte(text, pos)
if not byte or not whitespaceCharacters[byte] then
return tokens.TOKEN_WHITESPACE, pos
end
end
end
 
local token = specialCharacters[byte]
if token then
if token ~= -1 then
return token, pos + 1
end
 
-- WoW specific (for color codes)
if byte == bytes.BYTE_VERTICAL then
byte = stringbyte(text, pos + 1)
if byte == bytes.BYTE_VERTICAL then
return tokens.TOKEN_VERTICAL, pos + 2
end
if byte == bytes.BYTE_c then
return tokens.TOKEN_COLORCODE_START, pos + 10
end
if byte == bytes.BYTE_r then
return tokens.TOKEN_COLORCODE_STOP, pos + 2
end
return tokens.TOKEN_UNKNOWN, pos + 1
end
 
if byte == bytes.BYTE_MINUS then
byte = stringbyte(text, pos + 1)
if byte == bytes.BYTE_MINUS then
return nextComment(text, pos + 2)
end
return tokens.TOKEN_MINUS, pos + 1
end
 
if byte == bytes.BYTE_SINGLE_QUOTE then
return nextString(text, pos + 1, bytes.BYTE_SINGLE_QUOTE)
end
 
if byte == bytes.BYTE_DOUBLE_QUOTE then
return nextString(text, pos + 1, bytes.BYTE_DOUBLE_QUOTE)
end
 
if byte == bytes.BYTE_LEFTBRACKET then
local isBracketString, nextPos, equalsCount = isBracketStringNext(text, pos)
if isBracketString then
return nextBracketString(text, nextPos, equalsCount)
else
return tokens.TOKEN_LEFTBRACKET, pos + 1
end
end
 
if byte == bytes.BYTE_EQUALS then
byte = stringbyte(text, pos + 1)
if not byte then
return tokens.TOKEN_ASSIGNMENT, pos + 1
end
if byte == bytes.BYTE_EQUALS then
return tokens.TOKEN_EQUALITY, pos + 2
end
return tokens.TOKEN_ASSIGNMENT, pos + 1
end
 
if byte == bytes.BYTE_PERIOD then
byte = stringbyte(text, pos + 1)
if not byte then
return tokens.TOKEN_PERIOD, pos + 1
end
if byte == bytes.BYTE_PERIOD then
byte = stringbyte(text, pos + 2)
if byte == bytes.BYTE_PERIOD then
return tokens.TOKEN_TRIPLEPERIOD, pos + 3
end
return tokens.TOKEN_DOUBLEPERIOD, pos + 2
elseif byte >= bytes.BYTE_0 and byte <= bytes.BYTE_9 then
return nextNumberFractionPart(text, pos + 2)
end
return tokens.TOKEN_PERIOD, pos + 1
end
 
if byte == bytes.BYTE_LESSTHAN then
byte = stringbyte(text, pos + 1)
if byte == bytes.BYTE_EQUALS then
return tokens.TOKEN_LTE, pos + 2
end
return tokens.TOKEN_LT, pos + 1
end
 
if byte == bytes.BYTE_GREATERTHAN then
byte = stringbyte(text, pos + 1)
if byte == bytes.BYTE_EQUALS then
return tokens.TOKEN_GTE, pos + 2
end
return tokens.TOKEN_GT, pos + 1
end
 
if byte == bytes.BYTE_TILDE then
byte = stringbyte(text, pos + 1)
if byte == bytes.BYTE_EQUALS then
return tokens.TOKEN_NOTEQUAL, pos + 2
end
return tokens.TOKEN_TILDE, pos + 1
end
 
return tokens.TOKEN_UNKNOWN, pos + 1
elseif byte >= bytes.BYTE_0 and byte <= bytes.BYTE_9 then
return nextNumberIntPart(text, pos + 1)
else
return nextIdentifier(text, pos + 1)
end
end
 
-- Cool stuff begins here! (indentation and highlighting)
 
local noIndentEffect = {0, 0}
local indentLeft = {-1, 0}
local indentRight = {0, 1}
local indentBoth = {-1, 1}
 
local keywords = {}
lib.keywords = keywords
keywords["and"] = noIndentEffect
keywords["break"] = noIndentEffect
keywords["false"] = noIndentEffect
keywords["for"] = noIndentEffect
keywords["if"] = noIndentEffect
keywords["in"] = noIndentEffect
keywords["local"] = noIndentEffect
keywords["nil"] = noIndentEffect
keywords["not"] = noIndentEffect
keywords["or"] = noIndentEffect
keywords["return"] = noIndentEffect
keywords["true"] = noIndentEffect
keywords["while"] = noIndentEffect
 
keywords["until"] = indentLeft
keywords["elseif"] = indentLeft
keywords["end"] = indentLeft
 
keywords["do"] = indentRight
keywords["then"] = indentRight
keywords["repeat"] = indentRight
keywords["function"] = indentRight
 
keywords["else"] = indentBoth
 
tokenIndentation = {}
lib.tokenIndentation = tokenIndentation
tokenIndentation[tokens.TOKEN_LEFTPAREN] = indentRight
tokenIndentation[tokens.TOKEN_LEFTBRACKET] = indentRight
tokenIndentation[tokens.TOKEN_LEFTWING] = indentRight
 
tokenIndentation[tokens.TOKEN_RIGHTPAREN] = indentLeft
tokenIndentation[tokens.TOKEN_RIGHTBRACKET] = indentLeft
tokenIndentation[tokens.TOKEN_RIGHTWING] = indentLeft
 
local function fillWithTabs(n)
return stringrep("\t", n)
end
 
local function fillWithSpaces(a, b)
return stringrep(" ", a*b)
end
 
function lib.colorCodeCode(code, colorTable, caretPosition)
local stopColor = colorTable and colorTable[0]
if not stopColor then
return code, caretPosition
end
 
local stopColorLen = stringlen(stopColor)
 
tableclear(workingTable)
local tsize = 0
local totalLen = 0
 
local numLines = 0
local newCaretPosition
local prevTokenWasColored = false
local prevTokenWidth = 0
 
local pos = 1
local level = 0
 
while true do
if caretPosition and not newCaretPosition and pos >= caretPosition then
if pos == caretPosition then
newCaretPosition = totalLen
else
newCaretPosition = totalLen
local diff = pos - caretPosition
if diff > prevTokenWidth then
diff = prevTokenWidth
end
if prevTokenWasColored then
diff = diff + stopColorLen
end
newCaretPosition = newCaretPosition - diff
end
end
 
prevTokenWasColored = false
prevTokenWidth = 0
 
local tokenType, nextPos = nextToken(code, pos)
 
if not tokenType then
break
end
 
if tokenType == tokens.TOKEN_COLORCODE_START or tokenType == tokens.TOKEN_COLORCODE_STOP or tokenType == tokens.TOKEN_UNKNOWN then
-- ignore color codes
 
elseif tokenType == tokens.TOKEN_LINEBREAK or tokenType == tokens.TOKEN_WHITESPACE then
if tokenType == tokens.TOKEN_LINEBREAK then
numLines = numLines + 1
end
local str = stringsub(code, pos, nextPos - 1)
prevTokenWidth = nextPos - pos
 
tsize = tsize + 1
workingTable[tsize] = str
totalLen = totalLen + stringlen(str)
else
local str = stringsub(code, pos, nextPos - 1)
 
prevTokenWidth = nextPos - pos
 
-- Add coloring
if keywords[str] then
tokenType = tokens.TOKEN_KEYWORD
end
 
local color
if stopColor then
color = colorTable[str]
if not color then
color = colorTable[tokenType]
if not color then
if tokenType == tokens.TOKEN_IDENTIFIER then
color = colorTable[tokens.TOKEN_IDENTIFIER]
else
color = colorTable[tokens.TOKEN_SPECIAL]
end
end
end
end
 
if color then
tsize = tsize + 1
workingTable[tsize] = color
tsize = tsize + 1
workingTable[tsize] = str
tsize = tsize + 1
workingTable[tsize] = stopColor
 
totalLen = totalLen + stringlen(color) + (nextPos - pos) + stopColorLen
prevTokenWasColored = true
else
tsize = tsize + 1
workingTable[tsize] = str
 
totalLen = totalLen + stringlen(str)
end
end
 
pos = nextPos
end
return table.concat(workingTable), newCaretPosition, numLines
end
 
function lib.indentCode(code, tabWidth, colorTable, caretPosition)
local fillFunction
if tabWidth == nil then
tabWidth = defaultTabWidth
end
if tabWidth then
fillFunction = fillWithSpaces
else
fillFunction = fillWithTabs
end
 
tableclear(workingTable)
local tsize = 0
local totalLen = 0
 
tableclear(workingTable2)
local tsize2 = 0
local totalLen2 = 0
 
 
local stopColor = colorTable and colorTable[0]
local stopColorLen = not stopColor or stringlen(stopColor)
 
local newCaretPosition
local newCaretPositionFinalized = false
local prevTokenWasColored = false
local prevTokenWidth = 0
 
local pos = 1
local level = 0
 
local hitNonWhitespace = false
local hitIndentRight = false
local preIndent = 0
local postIndent = 0
while true do
if caretPosition and not newCaretPosition and pos >= caretPosition then
if pos == caretPosition then
newCaretPosition = totalLen + totalLen2
else
newCaretPosition = totalLen + totalLen2
local diff = pos - caretPosition
if diff > prevTokenWidth then
diff = prevTokenWidth
end
if prevTokenWasColored then
diff = diff + stopColorLen
end
newCaretPosition = newCaretPosition - diff
end
end
 
prevTokenWasColored = false
prevTokenWidth = 0
 
local tokenType, nextPos = nextToken(code, pos)
 
if not tokenType or tokenType == tokens.TOKEN_LINEBREAK then
level = level + preIndent
if level < 0 then level = 0 end
 
local s = fillFunction(level, tabWidth)
 
tsize = tsize + 1
workingTable[tsize] = s
totalLen = totalLen + stringlen(s)
 
if newCaretPosition and not newCaretPositionFinalized then
newCaretPosition = newCaretPosition + stringlen(s)
newCaretPositionFinalized = true
end
 
 
for k, v in next,workingTable2 do
tsize = tsize + 1
workingTable[tsize] = v
totalLen = totalLen + stringlen(v)
end
 
if not tokenType then
break
end
 
tsize = tsize + 1
workingTable[tsize] = stringsub(code, pos, nextPos - 1)
totalLen = totalLen + nextPos - pos
 
level = level + postIndent
if level < 0 then level = 0 end
 
tableclear(workingTable2)
tsize2 = 0
totalLen2 = 0
 
hitNonWhitespace = false
hitIndentRight = false
preIndent = 0
postIndent = 0
elseif tokenType == tokens.TOKEN_WHITESPACE then
if hitNonWhitespace then
prevTokenWidth = nextPos - pos
 
tsize2 = tsize2 + 1
local s = stringsub(code, pos, nextPos - 1)
workingTable2[tsize2] = s
totalLen2 = totalLen2 + stringlen(s)
end
elseif tokenType == tokens.TOKEN_COLORCODE_START or tokenType == tokens.TOKEN_COLORCODE_STOP or tokenType == tokens.TOKEN_UNKNOWN then
-- skip these, though they shouldn't be encountered here anyway
else
hitNonWhitespace = true
 
local str = stringsub(code, pos, nextPos - 1)
 
prevTokenWidth = nextPos - pos
 
-- See if this is an indent-modifier
local indentTable
if tokenType == tokens.TOKEN_IDENTIFIER then
indentTable = keywords[str]
else
indentTable = tokenIndentation[tokenType]
end
 
if indentTable then
if hitIndentRight then
postIndent = postIndent + indentTable[1] + indentTable[2]
else
local pre = indentTable[1]
local post = indentTable[2]
if post > 0 then
hitIndentRight = true
end
preIndent = preIndent + pre
postIndent = postIndent + post
end
end
 
-- Add coloring
if keywords[str] then
tokenType = tokens.TOKEN_KEYWORD
end
 
local color
if stopColor then
color = colorTable[str]
if not color then
color = colorTable[tokenType]
if not color then
if tokenType == tokens.TOKEN_IDENTIFIER then
color = colorTable[tokens.TOKEN_IDENTIFIER]
else
color = colorTable[tokens.TOKEN_SPECIAL]
end
end
end
end
 
if color then
tsize2 = tsize2 + 1
workingTable2[tsize2] = color
totalLen2 = totalLen2 + stringlen(color)
 
tsize2 = tsize2 + 1
workingTable2[tsize2] = str
totalLen2 = totalLen2 + nextPos - pos
 
tsize2 = tsize2 + 1
workingTable2[tsize2] = stopColor
totalLen2 = totalLen2 + stopColorLen
 
prevTokenWasColored = true
else
tsize2 = tsize2 + 1
workingTable2[tsize2] = str
totalLen2 = totalLen2 + nextPos - pos
 
end
end
pos = nextPos
end
return table.concat(workingTable), newCaretPosition
end
 
 
 
-- WoW specific code:
local GetTime = GetTime
 
local editboxSetText
local editboxGetText
 
-- Caret code (thanks Tem!)
local function critical_enter(editbox)
local script = editbox:GetScript("OnTextSet")
if script then
editbox:SetScript("OnTextSet", nil)
end
return script
end
 
local function critical_leave(editbox, script)
if script then
editbox:SetScript("OnTextSet", script)
end
end
 
local function setCaretPos_main(editbox, pos)
local text = editboxGetText(editbox)
 
if stringlen(text) > 0 then
editboxSetText(editbox, stringinsert(text, pos, "a"))
editbox:HighlightText(pos, pos + 1)
editbox:Insert("\0")
end
end
 
local function getCaretPos(editbox)
local script = critical_enter(editbox)
 
local text = editboxGetText(editbox)
editbox:Insert("\1")
local pos = stringfind(editboxGetText(editbox), "\1", 1, 1)
editboxSetText(editbox, text)
 
if pos then
setCaretPos_main(editbox, pos - 1)
end
critical_leave(editbox, script)
 
return (pos or 0) - 1
end
 
local function setCaretPos(editbox, pos)
local script = critical_enter(editbox)
setCaretPos_main(editbox, pos)
critical_leave(editbox, script, script2)
end
-- end of caret code
 
function lib.stripWowColors(code)
 
-- HACK!
-- This is a fix for a bug, where an unfinished string causes a lot of newlines to be created.
-- The reason for the bug, is that a |r\n\n gets converted to \n\n|r after the next indent-run
-- The fix is to remove those last two linebreaks when stripping
code = stringgsub(code, "|r\n\n$", "|r")
 
tableclear(workingTable)
local tsize = 0
 
local pos = 1
 
local prevVertical = false
local even = true
local selectionStart = 1
 
while true do
local byte = stringbyte(code, pos)
if not byte then
break
end
if byte == bytes.BYTE_VERTICAL then
even = not even
prevVertical = true
else
if prevVertical and not even then
if byte == bytes.BYTE_c then
 
if pos - 2 >= selectionStart then
tsize = tsize + 1
workingTable[tsize] = stringsub(code, selectionStart, pos - 2)
end
 
pos = pos + 8
selectionStart = pos + 1
elseif byte == bytes.BYTE_r then
 
if pos - 2 >= selectionStart then
tsize = tsize + 1
workingTable[tsize] = stringsub(code, selectionStart, pos - 2)
end
selectionStart = pos + 1
end
end
prevVertical = false
even = true
end
pos = pos + 1
end
if pos >= selectionStart then
tsize = tsize + 1
workingTable[tsize] = stringsub(code, selectionStart, pos - 1)
end
return table.concat(workingTable)
end
 
function lib.decode(code)
if code then
code = lib.stripWowColors(code)
code = stringgsub(code, "||", "|")
end
return code or ""
end
 
function lib.encode(code)
if code then
code = stringgsub(code, "|", "||")
end
return code or ""
end
 
function lib.stripWowColorsWithPos(code, pos)
code = stringinsert(code, pos, "\2")
code = lib.stripWowColors(code)
pos = stringfind(code, "\2", 1, 1)
code = stringdelete(code, pos, pos)
return code, pos
end
 
-- returns the padded code, and true if modified, false if unmodified
local linebreak = stringbyte("\n")
function lib.padWithLinebreaks(code)
local len = stringlen(code)
if stringbyte(code, len) == linebreak then
if stringbyte(code, len - 1) == linebreak then
return code, false
end
return code .. "\n", true
end
return code, true
 
end
 
local defaultTabWidth = 2
local defaultColorTable
 
-- Data tables
-- No weak table magic, since editboxes can never be removed in WoW
local enabled = {}
local dirty = {}
 
local editboxIndentCache = {}
local decodeCache = {}
local editboxStringCache = {}
local editboxNumLinesCache = {}
 
function lib.colorCodeEditbox(editbox)
dirty[editbox] = nil
 
local colorTable = editbox.faiap_colorTable or defaultColorTable
local tabWidth = editbox.faiap_tabWidth
 
local orgCode = editboxGetText(editbox)
local prevCode = editboxStringCache[editbox]
if prevCode == orgCode then
return
end
 
local pos = getCaretPos(editbox)
 
local code
code, pos = lib.stripWowColorsWithPos(orgCode, pos)
 
colorTable[0] = "|r"
 
local newCode, newPos, numLines = lib.colorCodeCode(code, colorTable, pos)
newCode = lib.padWithLinebreaks(newCode)
 
editboxStringCache[editbox] = newCode
if orgCode ~= newCode then
local script, script2 = critical_enter(editbox)
decodeCache[editbox] = nil
local stringlenNewCode = stringlen(newCode)
 
editboxSetText(editbox, newCode)
if newPos then
if newPos < 0 then newPos = 0 end
if newPos > stringlenNewCode then newPos = stringlenNewCode end
 
setCaretPos(editbox, newPos)
end
critical_leave(editbox, script, script2)
end
 
if editboxNumLinesCache[editbox] ~= numLines then
lib.indentEditbox(editbox)
end
editboxNumLinesCache[editbox] = numLines
end
 
function lib.indentEditbox(editbox)
dirty[editbox] = nil
 
local colorTable = editbox.faiap_colorTable or defaultColorTable
local tabWidth = editbox.faiap_tabWidth
 
local orgCode = editboxGetText(editbox)
local prevCode = editboxIndentCache[editbox]
if prevCode == orgCode then
return
end
 
local pos = getCaretPos(editbox)
 
local code
code, pos = lib.stripWowColorsWithPos(orgCode, pos)
 
colorTable[0] = "|r"
local newCode, newPos = lib.indentCode(code, tabWidth, colorTable, pos)
newCode = lib.padWithLinebreaks(newCode)
editboxIndentCache[editbox] = newCode
if code ~= newCode then
local script, script2 = critical_enter(editbox)
decodeCache[editbox] = nil
 
local stringlenNewCode = stringlen(newCode)
 
editboxSetText(editbox, newCode)
 
if newPos then
if newPos < 0 then newPos = 0 end
if newPos > stringlenNewCode then newPos = stringlenNewCode end
 
setCaretPos(editbox, newPos)
end
critical_leave(editbox, script, script2)
end
end
 
local function hookHandler(editbox, handler, newFun)
local oldFun = editbox:GetScript(handler)
if oldFun == newFun then
-- already hooked, ignore it
return
end
editbox["faiap_old_" .. handler] = oldFun
editbox:SetScript(handler, newFun)
end
 
local function textChangedHook(editbox, ...)
local oldFun = editbox["faiap_old_OnTextChanged"]
if oldFun then
oldFun(editbox, ...)
end
if enabled[editbox] then
dirty[editbox] = GetTime()
end
end
 
local function tabPressedHook(editbox, ...)
local oldFun = editbox["faiap_old_OnTabPressed"]
if oldFun then
oldFun(editbox, ...)
end
if enabled[editbox] then
return lib.indentEditbox(editbox)
end
end
 
local function onUpdateHook(editbox, ...)
local oldFun = editbox["faiap_old_OnUpdate"]
if oldFun then
oldFun(editbox, ...)
end
if enabled[editbox] then
local now = GetTime()
local lastUpdate = dirty[editbox] or now
if now - lastUpdate > 0.2 then
decodeCache[editbox] = nil
return lib.colorCodeEditbox(editbox)
end
end
end
 
local function newGetText(editbox)
local decoded = decodeCache[editbox]
if not decoded then
decoded = lib.decode(editboxGetText(editbox))
decodeCache[editbox] = decoded
end
return decoded or ""
end
 
local function newSetText(editbox, text)
decodeCache[editbox] = nil
if text then
local encoded = lib.encode(text)
 
return editboxSetText(editbox, encoded)
end
end
 
function lib.enable(editbox, colorTable, tabWidth)
if not editboxSetText then
editboxSetText = editbox.SetText
editboxGetText = editbox.GetText
end
 
local modified
if editbox.faiap_colorTable ~= colorTable then
editbox.faiap_colorTable = colorTable
modified = true
end
if editbox.faiap_tabWidth ~= tabWidth then
editbox.faiap_tabWidth = tabWidth
modified = true
end
 
if enabled[editbox] then
if modified then
lib.indentEditbox(editbox)
end
return
end
 
-- Editbox is possibly hooked, but disabled
enabled[editbox] = true
 
editbox.oldMaxBytes = editbox:GetMaxBytes()
editbox.oldMaxLetters = editbox:GetMaxLetters()
editbox:SetMaxBytes(0)
editbox:SetMaxLetters(0)
 
editbox.GetText = newGetText
editbox.SetText = newSetText
 
hookHandler(editbox, "OnTextChanged", textChangedHook)
hookHandler(editbox, "OnTabPressed", tabPressedHook)
hookHandler(editbox, "OnUpdate", onUpdateHook)
 
lib.indentEditbox(editbox)
end
 
-- Deprecated function
lib.addSmartCode = lib.enable
 
function lib.disable(editbox)
if not enabled[editbox] then
return
end
enabled[editbox] = nil
 
-- revert settings for max bytes / letters
editbox:SetMaxBytes(editbox.oldMaxBytes)
editbox:SetMaxLetters(editbox.oldMaxLetters)
 
-- try a real unhooking, if possible
if editbox:GetScript("OnTextChanged") == textChangedHook then
editbox:SetScript("OnTextChanged", editbox.faiap_old_OnTextChanged)
editbox.faiap_old_OnTextChanged = nil
end
 
if editbox:GetScript("OnTabPressed") == tabPressedHook then
editbox:SetScript("OnTabPressed", editbox.faiap_old_OnTabPressed)
editbox.faiap_old_OnTabPressed = nil
end
 
if editbox:GetScript("OnUpdate") == onUpdateHook then
editbox:SetScript("OnUpdate", editbox.faiap_old_OnUpdate)
editbox.faiap_old_OnUpdate = nil
end
 
editbox.GetText = nil
editbox.SetText = nil
 
-- change the text back to unformatted
editbox:SetText(newGetText(editbox))
 
-- clear caches
editboxIndentCache[editbox] = nil
decodeCache[editbox] = nil
editboxStringCache[editbox] = nil
editboxNumLinesCache[editbox] = nil
end
 
defaultColorTable = {}
lib.defaultColorTable = defaultColorTable
defaultColorTable[tokens.TOKEN_SPECIAL] = "|c00ff99ff"
defaultColorTable[tokens.TOKEN_KEYWORD] = "|c006666ff"
defaultColorTable[tokens.TOKEN_COMMENT_SHORT] = "|c00999999"
defaultColorTable[tokens.TOKEN_COMMENT_LONG] = "|c00999999"
 
local stringColor = "|c00ffff77"
defaultColorTable[tokens.TOKEN_STRING] = stringColor
defaultColorTable[".."] = stringColor
 
local tableColor = "|c00ff9900"
defaultColorTable["..."] = tableColor
defaultColorTable["{"] = tableColor
defaultColorTable["}"] = tableColor
defaultColorTable["["] = tableColor
defaultColorTable["]"] = tableColor
 
local arithmeticColor = "|c0033ff55"
defaultColorTable[tokens.TOKEN_NUMBER] = arithmeticColor
defaultColorTable["+"] = arithmeticColor
defaultColorTable["-"] = arithmeticColor
defaultColorTable["/"] = arithmeticColor
defaultColorTable["*"] = arithmeticColor
 
local logicColor1 = "|c0055ff88"
defaultColorTable["=="] = logicColor1
defaultColorTable["<"] = logicColor1
defaultColorTable["<="] = logicColor1
defaultColorTable[">"] = logicColor1
defaultColorTable[">="] = logicColor1
defaultColorTable["~="] = logicColor1
 
local logicColor2 = "|c0088ffbb"
defaultColorTable["and"] = logicColor2
defaultColorTable["or"] = logicColor2
defaultColorTable["not"] = logicColor2
 
defaultColorTable[0] = "|r"
 
end
 
-- just for testing
--[[
function testTokenizer()
local str = ""
for line in io.lines("indent.lua") do
str = str .. line .. "\n"
end
 
local pos = 1
 
while true do
local tokenType, nextPos = nextToken(str, pos)
 
if not tokenType then
break
end
 
if true or tokenType ~= tokens.TOKEN_WHITESPACE and tokenType ~= tokens.TOKEN_LINEBREAK then
print(stringformat("Found token %d (%d-%d): (%s)", tokenType, pos, nextPos - 1, stringsub(str, pos, nextPos - 1)))
end
 
if tokenType == tokens.TOKEN_UNKNOWN then
print("unknown token!")
break
end
 
pos = nextPos
end
end
 
 
function testIndenter(i)
local lib = IndentationLib
local str = ""
for line in io.lines("test.lua") do
str = str .. line .. "\n"
end
 
local colorTable = lib.defaultColorTable
print(lib.indentCode(str, 4, colorTable, i))
end
 
 
testIndenter()
 
--]]
Property changes : Added: svn:executable + *
DressToKill.toc
3,8 → 3,10
## Version: wowi:revision
## Notes: When you go out, make sure you're prepared to kill..
## Author: Cladhaire
## SavedVariables: DressToKillDB
## SavedVariablesPerCharacter: DressToKillDB
 
Localization.enUS.lua
 
DressToKill.lua
FAIAP.lua
DressToKillOptions.lua