vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
--[[
        CommonFunctions
        
        This file is for commonly used functions available to any
        UltimateUI Mod. Thanks to Chitinous, Skrag and Thott for most of
        these functions. :-)

        by Alexander Brazie

  ]]--

DEBUG_CHAT_FRAME = ChatFrame1;

-- Output functions

        -- NOTE***  ChatTimeStamps is completely overloading this function when enabled
function Print(msg, r, g, b, frame, id) 
        if (not r) then r = 1.0; end
        if (not g) then g = 1.0; end
        if (not b) then b = 1.0; end
        if (not frame) then frame = DEFAULT_CHAT_FRAME; end
        if (frame) then
                if (not id) then
                        frame:AddMessage(msg,r,g,b);
                else
                        frame:AddMessage(msg,r,g,b,id);
                end
        end
end

-- Prints a table in an organized format
function PrintTable(table, rowname, level) 
        if ( rowname == nil ) then rowname = "ROOT"; end
        if ( level == nil ) then level = 1; end

        local msg = "";
        for i=1,level, 1 do 
                msg = msg .. "   ";     
        end

        if ( table == nil ) then Print (msg.."["..rowname.."] := nil "); return end
        if ( type(table) == "table" ) then
                Print (msg..rowname.." { ");
                for k,v in table do
                        PrintTable(v,k,level+1);
                end
                Print(msg.."} ");
        elseif (type(table) == "function" ) then 
                Print(msg.."["..rowname.."] => {{FunctionPtr*}}");
        elseif (type(table) == "userdata" ) then 
                Print(msg.."["..rowname.."] => {{UserData}}");
        elseif (type(table) == "boolean" ) then 
                local value = "true";
                if ( not table ) then
                        value = "false";
                end
                Print(msg.."["..rowname.."] => "..value);
        else
                Print(msg.."["..rowname.."] => "..table);
        end
end

-- DebugPrint takes a message and a debug key. 
-- 
-- The default is "ULTIMATEUI_DEBUG", but you can set it to any 
-- value you like. The result is that if setglobal(debugkey,1); 
-- has been called, your debug message will be output.
--
function DebugPrint(msg, debugkey) 
        if ( not debugkey ) then debugkey = "ULTIMATEUI_DEBUG"; end

        if ( type(msg) == "string" ) then 
                if ( getglobal(debugkey) == 1 ) then
                                Print("DBG: "..msg, 1, 1, .5, DEBUG_CHAT_FRAME);
                end
        end
end

-- Prints an error message
function ErrorPrint(msg)
        local info = ChatTypeInfo["YELL"];
        UIErrorsFrame:AddMessage(msg, info.r, info.g, info.b, 1.0, UIERRORS_HOLD_TIME);
end

-- Table functions
function GetIndexInList(list,item) 
        for k,v in list do 
                if ( v == item ) then return k; end
        end
        return -1;
end

-- Function Hooking (From ThottbotUtil.lua)

-- HookFunction("some_blizzard_function","my_function","before|after|hide")
-- calls "my_function" before/after "some_blizzard_function".
-- if type is "hide", calls "my_function" before all others, and only continues if it returns true
-- This method is used so the hook can be later undone without screwing up someone else's later hook.
HookFunction = function (...) 
        -- Sea.io.error (this:GetName(), " is using HookFunction, which is outdated. Please upgrade to -- Sea.util.hook!" );
        -- Sea.util.hook(unpack(arg));
end
-- same format as HookFunction
-- UnHookFunction = -- Sea.util.unhook;
-- Tooltip Scanning and Changing Functions

-- Clears a tooltip for usage.
function ClearTooltip(TooltipNameBase)
        for i=1, 15, 1 do
                getglobal(TooltipNameBase.."TextLeft"..i):SetText("");
                getglobal(TooltipNameBase.."TextRight"..i):SetText("");
        end

end

-- 
-- Prints an items' text into the chat window. 
-- 
function PrintItemText( bag, slot, TooltipNameBase )
        if ( TooltipNameBase == nil ) then 
                TooltipNameBase = "UltimateUITooltip";
        end

        local tooltip = getglobal (TooltipNameBase);
        
        --ClearToolTip(TooltipNameBase);
        tooltip:SetOwner( getglobal("UIParent"), "ANCHOR_CURSOR" );
        -- Sea.wow.tooltip.protectTooltipMoney();
        tooltip:SetBagItem( bag, slot );
        -- Sea.wow.tooltip.unprotectTooltipMoney();
        if( tooltip:IsVisible() ) then
                Print( "Visible tooltip" );
                tooltip:Hide();

                for i=1, tooltip:NumLines(), 1 do
                        show = 0;
                        str = "Left"..i..": ";
                        str2 = getglobal(TooltipNameBase.."TextLeft"..i):GetText();
                        if( str2 ~= nil ) then
                                show = 1;
                                str = str..str2;
                        end
                        str = str.." Right"..i..": ";
                        str2 = getglobal(TooltipNameBase.."TextRight"..i):GetText();
                        if( str2 ~= nil ) then
                                show = 1;
                                str = str..str2;
                        end
                        if( show == 1 ) then Print( str ); end
                end
        end
end

-- Gets all lines out of a tooltip.
function ScanTooltip(TooltipNameBase)
        if ( TooltipNameBase == nil ) then 
                TooltipNameBase = "UltimateUITooltip";
        end
        
        local strings = {};
        for idx = 1, 10 do
                local textLeft = nil;
                local textRight = nil;
                ttext = getglobal(TooltipNameBase.."TextLeft"..idx);
                if(ttext and ttext:IsVisible() and ttext:GetText() ~= nil)
                then
                        textLeft = ttext:GetText();
                end
                ttext = getglobal(TooltipNameBase.."TextRight"..idx);
                if(ttext and ttext:IsVisible() and ttext:GetText() ~= nil)
                then
                        textRight = ttext:GetText();
                end
                if (textLeft or textRight)
                then
                        strings[idx] = {};
                        strings[idx].left = textLeft;
                        strings[idx].right = textRight;
                end
        end
        
        return strings;
end

function GetItemName(bag, slot)
        local name = "";
        local strings = nil;
        if ( bag > -1 ) then
                strings = GetItemInfoStrings(bag, slot, "UltimateUITooltip");
        else
                -- Sea.wow.tooltip.protectTooltipMoney();
                local hasItem, hasCooldown = UltimateUITooltip:SetInventoryItem("player", slot);
                -- Sea.wow.tooltip.unprotectTooltipMoney();
                strings = ScanTooltip("UltimateUITooltip");
                if ( not hasItem) then
                        if ( strings[1] ) then
                                strings[1].left = "";
                        end
                end
        end
        -- Determine if the item is an ore, gem or herb
        if ( strings[1] ) then
                name = strings[1].left;
        end
        return name;
end

--Sets the tooltip relative to the owner in a position
--appropriate for where the owner is on the screen.
--owner - optional, the owner object, defaults to this
--tooltip, optional, the tooltip to set, defaults to GameTooltip
--setX, optional, sets the x offset(flipped based on screen corner)
--setY, optional, sets the y offset(flipped based on screen corner)
function SmartSetOwner(owner, tooltip, setX, setY)
        if (not owner) then
                owner = this;
        end
        if (not tooltip) then
                tooltip = GameTooltip;
        end
        if (not setX) then
                setX = 0;
        end
        if (not setY) then
                setY = 0;
        end
        if (owner and (owner ~= UIParent)) then
                local x,y = owner:GetCenter();
                local left = owner:GetLeft();
                local right = owner:GetRight();
                local top = owner:GetTop();
                local bottom = owner:GetBottom();
                local screenWidth = UIParent:GetWidth();
                local screenHeight = UIParent:GetHeight();
                local scale = owner:GetScale();
                if (x~=nil and y~=nil and left~=nil and right~=nil and top~=nil and bottom~=nil and screenWidth>0 and screenHeight>0) then
                        setX = setX * scale;
                        setY = setY * scale;
                        x = x * scale;
                        y = y * scale;
                        left = left * scale;
                        right = right * scale;
                        width = right - left;
                        top = top * scale;
                        bottom = bottom * scale;
                        local anchorPoint = "";
                        if (y <= (screenHeight * (1/2))) then
                                top = top + setY;
                                anchorPoint = "TOP";
                                if (top < 0) then
                                        setY = setY - top;
                                end
                        else
                                setY = -setY;
                                bottom = bottom + setY;
                                anchorPoint = "BOTTOM";
                                if (bottom > screenHeight) then
                                        setY = setY + (screenHeight - bottom);
                                end
                        end
                        
                        if (x <= (screenWidth * (1/2))) then
                                left = left + setX;
                                if (anchorPoint == "BOTTOM") then
                                        anchorPoint = anchorPoint.."RIGHT";
                                        setX = setX - width;
                                        if (left < 0) then
                                                setX = setX - left;
                                        end
                                else
                                        anchorPoint = anchorPoint.."LEFT";
                                        if (left < 0) then
                                                setX = setX - left;
                                        end
                                end
                        else
                                setX = -setX;
                                right = right + setX;
                                if (anchorPoint == "BOTTOM") then
                                        anchorPoint = anchorPoint.."LEFT";
                                        setX = setX + width;
                                        if (right > screenWidth) then
                                                setX = setX - (right - screenWidth);
                                        end
                                else
                                        anchorPoint = anchorPoint.."RIGHT";
                                        if (right > screenWidth) then
                                                setX = setX + (screenWidth - right);
                                        end
                                end
                        end
                        
                        if (anchorPoint == "") then
                                anchorPoint = "TOPLEFT";
                        end
                        scale = tooltip:GetScale();
                        if (scale) then
                                setX = setX / scale;
                                setY = setY / scale;
                        end
                        tooltip:SetOwner(owner, "ANCHOR_"..anchorPoint, setX, setY);
                end
        elseif (owner == UIParent) then
                local x,y = GetCursorPosition();
                local width = tooltip:GetWidth();
                local height = tooltip:GetHeifht();
                local screenWidth = UIParent:GetWidth();
                local screenHeight = UIParent:GetHeight();
                local scale = owner:GetScale();
                if (x~=nil and y~=nil and width and height and screenWidth>0 and screenHeight>0) then
                        setX = setX * scale;
                        setY = setY * scale;
                        x = x * scale;
                        y = y * scale;
                        width = width * scale;
                        height = height * scale;
                        local anchorPoint = "";
                        if (y <= (screenHeight * (1/2))) then
                                local top = (y + height) + setY;
                                if (top < 0) then
                                        setY = setY - top;
                                end
                        else
                                setY = -setY;
                                local bottom = (y - height) + setY;
                                setY = setY - height;
                                if (bottom > screenHeight) then
                                        setY = setY + (screenHeight - bottom);
                                end
                        end
                        
                        if (x <= (screenWidth * (1/2))) then
                                local left = x + setX;
                                setX = setX + (width / 2);
                                if (left < 0) then
                                        setX = setX - left;
                                end
                        else
                                setX = -setX;
                                local right = x + setX;
                                setX = setX - (width / 2);
                                if (right > screenWidth) then
                                        setX = setX - (right - screenWidth);
                                end
                        end
                        
                        scale = tooltip:GetScale();
                        if (scale) then
                                setX = setX / scale;
                                setY = setY / scale;
                        end
                        tooltip:SetOwner(owner, "ANCHOR_CURSOR", setX, setY);
                end
        else
                tooltip:SetOwner(owner, "ANCHOR_TOPLEFT");
        end
end

-- Obtains all information about a bag/slot and returns it as an array 
function GetItemInfoStrings(bag,slot, TooltipNameBase)
        if ( TooltipNameBase == nil ) then 
                TooltipNameBase = "UltimateUITooltip";
        end

        ClearTooltip(TooltipNameBase);

        local tooltip = getglobal(TooltipNameBase);
        
        -- Open tooltip & read contents
        -- Sea.wow.tooltip.protectTooltipMoney();
        tooltip:SetBagItem( bag, slot );
        -- Sea.wow.tooltip.protectTooltipMoney();
        local strings = ScanTooltip(TooltipNameBase);

        -- Done our duty, send report
        return strings;
end

-- Item Classification
-- Thanks to Zatharas, Narands, Xdra, Mirodin and Reima for this list
HerbList = { 
        "Peacebloom", 
        "Silverleaf", 
        "Snakeroot", 
        "Magebloom", 
        "Bruiseweed", 
        "Swiftthistle",
        "Fadeleaf", 
        "Briarthorn", 
        "Kingsblood",
        "Wild Steelbloom",
        "Grave Moss", 
        "Liferoot",
        "Firebloom",
        "Goldthorn",
        "Stranglekelp",
        "Khadgar's Whiskers"
};
-- Baccarus for Truesilver
OreList = {
        "Copper Ore",
        "Tin Ore",
        "Silver Ore",
        "Mithril Ore",
        "Iron Ore",
        "Gold Ore",
        "Truesilver Ore",
        "Thorium Ore"
};
-- Melwein for Malachite
GemList = {
        "Malachite",
        "Pearl",
        "Moss Agate",
        "Tigerseye", 
        "Citrine",
        "Jade",
        "Shadowgem",
        "Aquamarine",
        "Blue Pearl",
        "Star Ruby"
};
-- Potion List ugh...
-- Thanks to Zatharas for linking holy spring water
PotionList = {
        "Potion",
        "Elixir",
        "Oil",
        "Holy Spring Water"
};
-- Tailoring List... ugh...
-- Thanks to Able for Silk (Spider etc)
TailorList = { 
        "Bolt",
        "Cloth",
        "Thread",
        "Dye",
        "Silk",
        "Bleach",
        "Magewave",
        "Runecloth",
        "Felcloth"
}
-- Leathercrafting ... ugh...
LeatherList = {
        "Leather",
        "Hide",
        "Scales"
};
-- Fishing 
FishingList = {
        "Bauble",
        "Nightcrawler",
        "Fishing Pole",
        "Fish Attractor"
};
-- Warlock Items
WarlockList = {
        "Soul Shard",
        "Healthstone",
        "Manastone",
        "Soulstone"
};
-- Shaman Items
ShamanList = {
        "Earth Totem",
        "Fire Totem",
        "Water Totem",
        "Air Totem",
        "Sapta"
};

-- And the worst ones: (Prepare for bad humor)

-- HP restoring items
FoodList = {
        "Bread", -- Boring but important first
        "Cornbread",
        "Haunch",
        "Mutton Chop",
        "Hog Shank",
        "Tough Jerky",
        "Stormwind Brie", -- What is Brie anyways?
        "Aged Cheddar",
        "Alterac Swiss",
        "Apple",
        "Watermelon",
        "Banana",
        "Ham Steak", -- I Love Ham Steak!
        "Dalaran Sharp", -- Ouch
        "Bleu", -- Ewwww Bleu cheese
        "Dwarven Mild", -- About time them dorfs got it right
        "Wolf Meat", -- Hawooooo
        "Roasted Boar Meat",
        "Mackerel", 
        "Smallfish",
        "Kaldorei Caviar",
        "Scorpid Surprise",
        "Beer Basted",
        "Smoked Bear Meat",
        "Roasted Kodo Meat",
        "Mud Snapper",
        "Rainbow Fin Albacore",
        "Halibut",
        "Strider Stew",
        "Fillet of Frenzy",
        "Boiled Clams",
        "Goretusk Liver Pie",
        "Loch Frenzy Delights",
        "Coyote Steak",
        "Blood Sausage",
        "Westfall Stew",
        "Crab Cake",
        "Crispy Lizard",
        "Pork Ribs",
        "Croclist Steak",
        "Savory Deviate",
        "Scorpid Surprise",
        "Cooked Crab Claw",
        "Dig Rat Stew",
        "Murloc Fin Soup",
        "Clam Chowder",
        "Seasoned Wolf",
        "Spider Cake",
        "Bear Steak",
        "Venison",
        "Pork Ribs",
        "Gumbo",
        "Lion Chops",
        "Goblin Deviled",
        "Omelet",
        "Tasty Lion Steak",
        "Barbecued Buzzard",
        "Giant Clam Scorcho",
        "Soothing Turtle",
        "Rockscale Cod",
        "Cave Mold",
        "eckled Mushroom",
        "Mushroom Cap",
        "Bolete",
        "Morel",
        "Truffle",
        "Strider Stew",
        "Mystery Stew",
        "Roast Raptor",
        "Carrion Surprise",
        "Dragonbreath Chi",
        "Spiced Chi",
        "Monster Om",
        "Spiced Wolf",
        "Goldthorn Tea",
        "Squid",
        "Pumpkin",
        "Ice Cream" -- Tigule's specialty
};
-- Mana restoring items
DrinkList = {
        "Cherry Grog", -- The ONE AND ONLY BABY!        
        "Moonberry Juice",
        "Cold Milk",
        "Melon Juice",
        "Sweet Nectar",
        "Spring Water",
        "Glory Dew"
};

-- Checks if any item from a list is in a specified word

function IsListItemInWord(list, word) 
        for k,v in list do
                if ( string.find(word,v) ~= nil ) then
                        return true;
                end
        end
        return false;
end

-- This function obtains all the information you could ever want about an item... 
--  ... if its available.
--
--  classification, name, quality, itemCount, leftString, rightString, minLevel, unique, soulbound, bindsOnPickup, bindsOnEquip, questItem;
function ClassifyItem(bag, slot, TooltipNameBase)
        if ( TooltipNameBase == nil ) then 
                TooltipNameBase = "UltimateUITooltip";
        end
        
        local strings = GetItemInfoStrings(bag, slot, TooltipNameBase);
        local classification = "Misc";
        local leftString, rightString, minLevel;
        local unique = false;
        local soulbound = false;
        local bindsOnPickup = false;
        local bindsOnEquip = false;
        local questItem = false;
        local isHerb = false;
        local isGem = false;
        local isOre = false;
        local isLeather = false;
        local isTailor = false;
        local isFishing = false;
        local isFood = false;
        local isDrink = false;
        local isShaman = false;
        local isWarlock = false;
        local isJunk = false;
        local engineering = false;
        local firstaid = false;
        local classitem = false;
        local class = "";
        local texture, itemCount, locked, quality = GetContainerItemInfo(bag,slot);
        local name = "";
        -- Look for the target line that identifies an item; it'll either be line 2, 3, or 4.

        -- Determine if the item is an ore, gem or herb
        if ( strings[1] ) then
                name = strings[1].left;
        end
        
        isHerb = IsListItemInWord(HerbList, name);
        isGem  = IsListItemInWord(GemList, name);
        isOre  = IsListItemInWord(OreList, name);
        isLeather = IsListItemInWord(LeatherList, name);
        isFishing = IsListItemInWord(FishingList, name);
        isTailor = IsListItemInWord(TailorList, name);
        isPotion = IsListItemInWord(PotionList, name);
        isFood = IsListItemInWord(FoodList, name);
        isDrink = IsListItemInWord(DrinkList, name);
        isShaman = IsListItemInWord(ShamanList, name);
        isWarlock = IsListItemInWord(WarlockList, name);

        if ( ( quality ) and ( quality < 0 ) ) then 
                isJunk = true;
        end

        for i = 2, 5, 1 do
                if (not strings[i])
                then
                        break;
                end
                if (strings[i].left == ITEM_UNIQUE or strings[i].left == ITEM_UNIQUE_MULTIPLE ) then unique = true; end
                if (strings[i].left == ITEM_SOULBOUND ) then soulbound = true; end
                if (strings[i].left == ITEM_BIND_ON_PICKUP ) then bindsOnPickup = true; end
                if (strings[i].left == ITEM_BIND_ON_EQUIP ) then bindsOnEquip = true; end
                if (strings[i].left == ITEM_BIND_QUEST ) then questItem = true; end

                if (string.find(strings[i].left,"First Aid", 0, true ) ) then firstaid = true; end
                if (string.find(strings[i].left,"Engineering", 0, true ) ) then engineering = true; end
                if (string.find(strings[i].left,"Classes:", 0, true ) ) then classitem = true; class = string.sub(strings[i].left, string.find(strings[i].left,":", 0, true )+2); end
                
                if (strings[i].left and
                        strings[i].left ~= ITEM_UNIQUE and
                        strings[i].left ~= ITEM_SOULBOUND and
                        strings[i].left ~= ITEM_BIND_ON_EQUIP and
                        strings[i].left ~= ITEM_BIND_ON_PICKUP and
                        strings[i].left ~= ITEM_BIND_QUEST)
                then
                        leftString = strings[i].left;
                        rightString = strings[i].right;
                        break;
                end
        end
        
        -- Find last line
        local lastLine;
        for i = 1, 10, 1 do
                if (strings[i] and strings[i].left)
                then
                        lastLine = strings[i].left;
                else
                        break;
                end
        end

        -- look at last line to see if it's a level requirement
        local minLevel = 0;
        if (lastLine)
        then
                local index, length, levelString = string.find(lastLine, "^Requires Level (%d+)$");
                if (index)
                then
                        minLevel = MakeIntFromString(levelString);
                end
        end
        
        -- classify item based on found strings
        if (leftString)
        then
                if (leftString == "Main Hand" or
                        leftString == "Two-Hand" or
                        leftString == "One-Hand")
                then
                        classification = "Weapon";
                elseif (leftString == "Head" or
                        leftString == "Hand" or
                        leftString == "Waist" or
                        leftString == "Shoulders" or
                        leftString == "Legs" or
                        leftString == "Back" or
                        leftString == "Feet" or
                        leftString == "Chest" or
                        leftString == "Wrist")
                then
                        classification = "Armor";
                elseif (leftString == "Off Hand")
                then
                        classification = "Shield";
                elseif (leftString == "Wand" or
                        leftString == "Ranged" or
                        leftString == "Gun" )
                then
                        classification = "Ranged";
                elseif (leftString == "Projectile") 
                then
                        classification = "Ammo";
                elseif (leftString == "Shirt" or
                        leftString == "Tabard" or
                        leftString == "Finger" or
                        leftString == "Neck" or
                        leftString == "Trinket" or
                        leftString == "Held In Hand")
                then
                        classification = "Clothing";
                end
        end
        if ( classification == "Misc" ) then
                if ( isGem ) then classification = "Gem"; end
                if ( isOre ) then classification = "Ore"; end
                if ( isHerb ) then classification = "Herb"; end
                if ( engineering ) then classification = "Engineering"; end
                if ( firstaid ) then classification = "First Aid"; end
                if ( isLeather ) then classification = "Leather"; end
                if ( isTailor ) then classification = "Thread"; end
                if ( isPotion ) then classification = "Potion"; end
                if ( isFishing ) then classification = "Fishing"; end
                if ( isFood ) then classification = "Food"; end
                if ( isDrink ) then classification = "Drink"; end
                if ( isShaman ) then classification = "Shaman"; end
                if ( isWarlock ) then classification = "Warlock"; end
                if ( classitem ) then classification = class; end
                if ( questItem ) then classification = "QuestItem"; end
                if ( isJunk ) then classification = "Junk"; end
        end 

        return classification, name, quality, itemCount, leftString, rightString, minLevel, unique, soulbound, bindsOnPickup, bindsOnEquip, questItem;
end

-- String Functions

function MakeIntFromString(str)
        DebugPrint("MakeIntFromString("..str..")", 4);
        local remain = str;
        local amount = 0;
        while (remain ~= "") do
                amount = amount * 10;
                amount = amount + (string.byte(strsub(remain, 1, 1)) - string.byte("0"));
                remain = strsub(remain, 2);
        end
        DebugPrint("MakeIntFromStr("..str..") = "..amount, 4);
        return amount;
end

function MakeIntFromHexString(str)
        DebugPrint("MakeIntFromHexString("..str..")", 4);
        local remain = str;
        local amount = 0;
        while (remain ~= "") do
                amount = amount * 16;
                local byteVal = string.byte(strupper(strsub(remain, 1, 1)));
                if (byteVal >= string.byte("0") and byteVal <= string.byte("9"))
                then
                        amount = amount + (byteVal - string.byte("0"));
                elseif (byteVal >= string.byte("A") and byteVal <= string.byte("F"))
                then
                        amount = amount + 10 + (byteVal - string.byte("A"));
                end
                remain = strsub(remain, 2);
        end
        DebugPrint("MakeIntFromHexStr("..str..") = "..amount, 4);
        return amount;
end

function MakeHexStringFromInt(intval, minlength)
        return string.format("%"..minlength.."x", intval );
end

function EscapeString(plainString, disallowedChars)
        -- yay URL-encoding
        local str = "";
        local remain = plainString;
        disallowedChars = disallowedChars.."%";
        while (remain ~= "") do
                local char = strsub(remain, 1, 1);
                if (string.find(disallowedChars, char, 1, true))
                then
                        str = str.."%";
                        local hexRepresentation = string.format("%02x", string.byte(char));
                        str = str..hexRepresentation;
                else
                        str = str..char;
                end
                remain = strsub(remain, 2);
        end
        return str;
end

function UnescapeString(escapedString)
        local str = "";
        local remain = escapedString;
        while (remain ~= "") do
                local char = strsub(remain, 1, 1);
                if (char == "%")
                then
                        str = str..string.char(MakeIntFromHexString(strsub(remain, 2, 3)));
                        remain = strsub(remain, 4);
                else
                        str = str..char;
                        remain = strsub(remain, 2);
                end
        end
        return str;
end

-- Converts numeric time into readable time.
function GetTimeString(time)
        if (time < 0)
        then
                time = 0;
        end

        local seconds = mod(floor(time), 60);
        if (seconds < 10)
        then
                seconds = "0"..seconds;
        end
        local minutes = mod(floor(time/60), 60);
        local hours = floor(time/(60*60));
        
        local timeString;
        if (hours > 0)
        then
                if (minutes < 10)
                then
                        minutes = "0"..minutes;
                end
                timeString = hours..":"..minutes..":"..seconds;
        else
                timeString = minutes..":"..seconds;
        end
        return timeString;
end


function GetRGBAFromHexColor(hexColor)
        local alpha = MakeIntFromHexString(strsub(hexColor, 1, 2)) / 256;
        local red = MakeIntFromHexString(strsub(hexColor, 3, 4)) / 256;
        local green = MakeIntFromHexString(strsub(hexColor, 5, 6)) / 256;
        local blue = MakeIntFromHexString(strsub(hexColor, 7, 8)) / 256;
        return red, green, blue;
end             

-- Mathematical Functions (yuck!)
function CheckBits(field, bits)
        local result = true;
        while (bits > 0) do
                if (mod(bits, 2) > 0)
                then
                        if (mod(field, 2) == 0)
                        then
                                result = false;
                                break;
                        end
                end
                field = floor(field / 2);
                bits = floor(bits / 2);
        end
        return result;
end


function SetBit(field, bit)
        local source = 2^31; -- 1 in highest-order bit
        local shiftCount = 1;
        local result = 0;
        while (bit > 0) do
                result = floor(result / 2);
                -- if we're about to shift off a 1, put a 1 on the destination.
                if ((mod(bits, 2) == 1) or (mod(field, 2) == 1))
                then
                        result = result + source;
                end
                field = floor(field / 2);
                bits = floor(bits / 2);
                shiftCount = shiftCount + 1;
        end
        for i = shiftCount, 32, 1 do
                result = floor(result / 2);
                -- if we're about to shift off a 1, put a 1 on the destination.
                if (mod(bits, 2) == 1)
                then
                        result = result + source;
                end
                field = floor(field / 2);
        end
        return result;
end

function ClearBit(field, bit)
        local source = 2^31; -- 1 in highest-order bit
        local shiftCount = 1;
        local result = 0;
        while (bit > 0) do
                result = floor(result / 2);
                -- if we're about to shift off a 1, put a 1 on the destination. AS LONG AS this is not the bit to clear
                if ((mod(field, 2) == 1) and not (mod(bit, 2) == 1))
                then
                        result = result + source;
                end
                field = floor(field / 2);
                bits = floor(bits / 2);
                shiftCount = shiftCount + 1;
        end
        for i = shiftCount, 32, 1 do
                result = floor(result / 2);
                -- if we're about to shift off a 1, put a 1 on the destination.
                if (mod(bits, 2) == 1)
                then
                        result = result + source;
                end
                field = floor(field / 2);
        end
        return result;
end

function UnitFaction(who)
        local faction;
        local race = UnitRace(who);
        local HordeRaces = {"Orc", "Tauren", "Troll", "Undead" };

        for index, hordeRace in HordeRaces do
                if (race == hordeRace)
                then
                        return 1; -- Horde
                end
        end
        return 0; -- Alliance
end

function BaseConversion(input, inputBase, outputBase)
-----------------------------------------------------------
--               Function made by KaTTaNa !              -- 
--               --------------------------              --
--   http://www.wc3sear.ch/index.php?p=JASS&ID=37&sid=   --
--               --------------------------              --
--               Converted in LUA by vjeux               --
--                                                       --
-- Usage : BaseConversion(255, 10, 16)                   --
-- => Return "ff"                                        --
--                                                       --
-- Usage : BaseConversion("ff", 16, 10)                  --
-- => Return "10"                                        --
-----------------------------------------------------------
    local charMap = "0123456789abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+-=[]";
    local s;
    local result = "";
    local val = 0;
    local i;
    local p = 0;
    local pow = 1;
    local sign = "";

    if ( inputBase < 2 or inputBase > strlen(charMap) or outputBase < 2 or outputBase > strlen(charMap) ) then
        -- Bases are invalid or out of bounds
        return "Invalid bases given";
    end
    if ( strsub(input, 1, 1) == "-" ) then
        sign = "-";
        input = strsub(input, 1, strlen(input));
    end
    i = strlen(input);
    -- Get the integer value of input
    while (i > 0) do
        s = strsub(input, i, i);
        p = 0;
                local bool = false;
        while (bool == false) do
            if ( p >= inputBase ) then
                -- Input cannot match base
                return "Input does not match base!\n P = "..p;
            end
                        if ( s == strsub(charMap, p+1, p+1) ) then
                val = val + pow * p;
                pow = pow * inputBase;
                bool = true;
            end
            p = p + 1;
        end
        i = i - 1;
    end
    while (val > 0) do
        p = mod(val, outputBase);
        result = strsub(charMap, p+1, p+1)..result;
        val = val / outputBase;
    end

        for i = 1, strlen(result), 1 do
                if (strsub(result, 1, 1) == "0") then
                        result = strsub(result, 2, strlen(result));
                else
                        return sign..result
                end
        end

        if (strlen(sign..result) == 0) then 
                return "0";
        else
                return sign..result.."-"..strlen(sign..result);
        end
end


-- Thott's personalized functions
function dbanner(...)
  if ( (Thottbot) and (Thottbot.Debug) ) then
    UIErrorsFrame:AddMessage(join(arg,""), 0.9, 0.9, 0.0, 1.0, UIERRORS_HOLD_TIME);                                                             
    dprint(join(arg,""));
  end
end
function banner(...)
  UIErrorsFrame:AddMessage(join(arg,""), 0.9, 0.9, 0.0, 1.0, UIERRORS_HOLD_TIME);                                                               
end
function dbyte(c)
  return string.format("<%02X>",string.byte(c));
end
--function dprint_runqueue()
--  if(dprint_nextmsg) then
--    print2(dprint_nextmsg);
--    dprint_nextmsg = nil;
--  end
--  if(dprint_queue and dprint_queue[1]) then
--    local msg = table.remove(dprint_queue,1);
--    dprint_nextmsg = msg;
--    msg = string.gsub(msg,"([^%w%s])",dbyte)
--    print2("Next: ",msg);
--    UltimateUI_Schedule(0.01,dprint_runqueue);
--    dprint_queue_scheduled = true;
--  else
--    dprint_queue_scheduled = false;
--  end
--end
function dprint(...)
  if ( ( Thottbot ) and (Thottbot.Debug) ) then
    local msg = join(arg,"");
    msg = string.gsub(msg,"|","<pipe>");
    msg = string.gsub(msg,"([^%w%s%a%p])",dbyte);
    if(Thottbot.DebugFrame) then
      printframe(Thottbot.DebugFrame,msg);
    else
      print2(msg);
    end
--    if(not dprint_queue) then
--      dprint_queue = {};
--    end
--    table.insert(dprint_queue,join(arg,""));
--    if(not dprint_queue_scheduled) then
--      dprint_queue_scheduled = true;
--      UltimateUI_Schedule(0.01,dprint_runqueue);
--      -- 68.94.173.94
--    end
  end
end
--    Thottbot.PrintCount = Thottbot.PrintCount + 1;
--    if(Thottbot.PrintCount < 60) then
--      for key,value in arg do
--        arg[key] = string.gsub(value,"[^%w%s%p]",".");
--      end
--      print2(Thottbot.PrintCount,":",join(arg,""));
function dprint1(...)
  if ( (Thottbot) and (Thottbot.Debug) ) then
    print1(join(arg,""));
  end
end
function split(s,seperator)
  local t = {};
  t.n = 0;
  for value in string.gfind(s,"[^"..seperator.."]+") do
    t.n = t.n + 1;
    t[t.n] = value;
  end
  return t;
end
function join(list,seperator)
  local i;
  local c = "";
  local msg = "";
  if(type(list) ~= "table") then
    dbanner("unknown type ",type(list)," table passed to join, seperator ",seperator);
    return;
  end
  if(not list.n) then
    dbanner("ERROR: no .n variable in list!");
    return "";
  end
  for i=1, list.n, 1 do
    if(list[i]) then
      if(type(list[i]) ~= "string" and type(list[i]) ~= "number") then
        dbanner("found ",type(list[i])," in list!");
        msg = msg .. c .. "(" .. type(list[i]) .. ")";
      else
        msg = msg .. c .. list[i];
      end
    else
      msg = msg .. c .. "(nil)";
    end
    c = seperator;
  end
  return msg;
end
function dprintlist(list)
  if ( (Thottbot) and (Thottbot.Debug) ) then
    if(list) then
      dprint(join(list,","));
    else
      dprint("nil list");
    end
  end
end
function dprintcomma(...)
  dprint(join(arg,","));
end
function printframe(frame,...)
  if(frame) then
    frame:AddMessage(join(arg,""), 1.0, 1.0, 0.0);
  end
end
function print2(...)
  if(ChatFrame2) then
    ChatFrame2:AddMessage(join(arg,""), 1.0, 1.0, 0.0);
  end
end
function print1(...)
  if(ChatFrame1) then
    ChatFrame1:AddMessage(join(arg,""), 1.0, 1.0, 0.0);
  end
end
function push(t,v)
  if(not t or not t.n) then
    dbanner("Bad table passed to push");
    return nil;
  end
  t.n = t.n+1;
  t[t.n] = v;
end
function pop(t)
  if(not t or not t.n) then
    dbanner("Bad table passed to push");
    return nil;
  end
  local v = t[t.n];
  t.n = t.n - 1;
  return v;
end
function push2(t,x,y)
  if(not t or not t.n) then
    dbanner("Bad table passed to push2");
    return nil;
  end
  t.n = t.n+1;
  t[t.n] = x;
  t.n = t.n+1;
  t[t.n] = y;
end
function pop2(t)
  if(not t or not t.n) then
    --dbanner("Bad table passed to pop2");
    return nil;
  end
  if(t.n < 2) then
    return nil;
  end
  local tt = {};
  tt.n = 2;
  tt[1] = t[t.n-1];
  tt[2] = t[t.n];
  t.n = t.n - 2;
  return tt;
end
function fixnil(...)
  for i=1, arg.n, 1 do
    if(not arg[i]) then
      arg[i] = "(nil)";
    end
  end
  return arg;
end
function fixnilempty(...)
  for i=1, arg.n, 1 do
    if(not arg[i]) then
      arg[i] = "";
    end
  end
  return arg;
end
function fixnilzero(...)
  for i=1, arg.n, 1 do
    if(not arg[i]) then
      arg[i] = 0;
    end
  end
  return arg;
end
function HookHandler(name,arg)
  --dprint("HookHandler ",name);
  local called = false;
  local continue = true;
  local retval;
  for key,value in Hooks[name].hide do
    if(type(value) == "function") then
      --dprint("calling before ",name);
      if(not value(unpack(arg))) then
        continue = false;
      end
      called = true;
    end
  end
  if(not continue) then
    dprint("hide returned false, aborting call to ",name);
    return;
  end
  for key,value in Hooks[name].before do
    if(type(value) == "function") then
      --dprint("calling before ",name);
      value(unpack(arg));
      called = true;
    end
  end
  --dprint("calling original ",name);
  retval = Hooks[name].orig(unpack(arg));
  for key,value in Hooks[name].after do
    if(type(value) == "function") then
      --dprint("calling after ",name);
      value(unpack(arg));
      called = true;
    end
  end
  if(not called) then
    dprint("no hooks left for ",name,", clearing");
    setglobal(name,Hooks[name].orig);
    Hooks[name] = nil;
  end
  return retval;
end

-- Thanks to George Warner for suggesting this refactoring suggestion... as always, I modified it a bit :) /sarf
-- Source : http://www.ultimateuiui.org/cgi-bin/bugzilla/show_bug.cgi?id=158
function UltimateUI_IsUltimateUIUser(name)
        if ( not name ) then return false; end
        if ( UltimateUIMaster_UltimateUIUsers ) then
                if ( UltimateUIMaster_UltimateUIUsers[name] ) then 
                        return true;
                end
        end
        return false;
end

-- helper function - returns value as a byte
function UltimateUI_GetByteValue(pValue)
        local value = tonumber(pValue);
        if ( value <= 0 ) then return 0; end
        if ( value >= 255 ) then return 255; end
        return value;
end


-- Yet another function from George Warner, modified a bit to fit my own nefarious purposes. 
-- It can now accept r, g and b specifications, too (leaving out a), as well as handle 255 255 255
-- Source : http://www.ultimateuiui.org/cgi-bin/bugzilla/show_bug.cgi?id=159
function UltimateUI_GetColorFormatString(a, r, g, b)
        local percent = false;
        if ( ( ( not b ) or ( b <= 1 ) ) and ( a <= 1 ) and ( r <= 1 ) and ( g <= 1) ) then percent = true; end
        if ( ( not b ) and ( a ) and ( r ) and ( g ) ) then b = g; g = r; r = a; if ( percent ) then a = 1; else a = 255; end end
        if ( percent ) then a = a * 255; r = r * 255; g = g * 255; b = b * 255; end
        a = UltimateUI_GetByteValue(a); r = UltimateUI_GetByteValue(r); g = UltimateUI_GetByteValue(g); b = UltimateUI_GetByteValue(b);
        
        return format("|c%02X%02X%02X%02X%%s|r", a, r, g, b);
end


-- global localization string functions START

function loc_format(...)
  ret = arg[1];
  for i=2, arg.n, 1 do
    ret = string.gsub (ret, "<"..(i-1)..">", arg[i]);
  end
  return ret;
end

function loc_read(str, fmt, ord, n)
  local ret = {};
  fmt1 = string.gsub (fmt, "%b<>", "(.+)");
  for i=1, n, 1 do
    ret[ord[i]] = string.gsub(str, fmt1, "%"..i);
  end
  return unpack(ret);
end

-- global localization string functions END

--Returns true if variable is not nill and a table
function isTable(var)
        if (var and (type(var) == "table")) then
                return true;
        end
        return false;   
end

function MakeHyperLink(type, name, color)
        local link = "["..name.."]";
        if (color) then
                link = "|cFF"..color.."["..name.."]|r";
        end
        return "|H"..type.."|h"..link.."|h";
end

-- elite frame functions

function UUISetElite()
        PlayerFrameTexture:SetTexture("Interface\\TargetingFrame\\UI-TargetingFrame-Elite");
end

Generated by GNU Enscript 1.6.5.90.