changeset 1:3f83977570fb

Project file layout
author ShadowTheAge
date Wed, 04 Mar 2015 21:54:21 +0300
parents 9fa8442dd547
children c73a7706861e
files CrossRealmAssist.lua CrossRealmAssist.toc CrossRealmAssist/CrossRealmAssist.lua CrossRealmAssist/CrossRealmAssist.toc CrossRealmAssist/embeds.xml embeds.xml
diffstat 6 files changed, 489 insertions(+), 486 deletions(-) [+]
line wrap: on
line diff
--- a/CrossRealmAssist.lua	Wed Mar 04 21:37:31 2015 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,472 +0,0 @@
-CrossRealmAssist = LibStub("AceAddon-3.0"):NewAddon("CrossRealmAssist", "AceEvent-3.0", "AceConsole-3.0", "AceTimer-3.0")
-local AceGUI = LibStub("AceGUI-3.0")
-local addon = CrossRealmAssist;
-local wholib = LibStub:GetLibrary('LibWho-2.0'):Library()
-
-local hgroup, lfgContainer, currealm, partyrealm, recrealm, curRealmStat, homeRealm, realmSep, gui, leavebtn
-
-local scanstate=0
-local recentRealms={}
-local active=false
-
-local lfgGroups={
-    6, -- Custom
-    10, -- Ashran
-    1, -- Quests
-    3, -- Raids
-    8 -- BGs
-}
-
-local lfgTabs
-local curLfgGroup
-local sheduledScan
-local wasInGroup
-local lfgScanInProgress=false
-
-function addon:OnInitialize()
-
-end
-
-function addon:OnEnable()
-    local tabCount = table.getn(lfgGroups)
-    lfgTabs = {}
-    for i=1,tabCount do
-        local cat = lfgGroups[i]
-        table.insert(lfgTabs,{value=cat,text=(C_LFGList.GetCategoryInfo(cat))})
-    end
-    realmSep = _G.REALM_SEPARATORS
-    homeRealm = GetRealmName()
-    addon:RegisterChatCommand("cra", "Activate")
-    addon:RegisterChatCommand("crossrealmassist", "Activate")
-end
-
-function addon:OnDisable()
-    addon:Deactivate()
-end
-
-function addon:Activate()
-    if active then return end
-    active=true
-    wasInGroup = IsInGroup()
-    addon:CreateUI()
-    addon:ScanRealm()
-    addon:RegisterEvent("ZONE_CHANGED_NEW_AREA", "SheduleScan")
-    addon:RegisterEvent("LFG_LIST_SEARCH_RESULTS_RECEIVED", "LfgResponseData")
-    addon:RegisterEvent("LFG_LIST_SEARCH_FAILED", "LfgScanFailed")
-    addon:RegisterEvent("GROUP_ROSTER_UPDATE", "updatePartyInfo")
-    addon:RegisterEvent("LFG_LIST_APPLICATION_STATUS_UPDATED", "updateAppStatus")
-end
-
-function addon:Deactivate()
-    if not active then return end
-    active = false
-    scanstate = 0
-    lfgScanInProgress = false
-    gui:Release();
-    gui, lfgContainer, hgroup, currealm, partyrealm, recrealm, leavebtn = nil,nil,nil,nil,nil,nil,nil
-    addon:UnregisterEvent("ZONE_CHANGED_NEW_AREA")
-    addon:UnregisterEvent("PLAYER_REGEN_ENABLED")
-    addon:UnregisterEvent("LFG_LIST_SEARCH_RESULTS_RECEIVED")
-    addon:UnregisterEvent("LFG_LIST_SEARCH_FAILED")
-    addon:UnregisterEvent("GROUP_ROSTER_UPDATE")
-    addon:UnregisterEvent("LFG_LIST_APPLICATION_STATUS_UPDATED")
-end
-
-function addon:CreateUI()
-    gui = addon:AddUI(nil,"Window",{SetTitle="Cross Realm Assist",EnableResize=false,SetLayout="Flow"},true,{OnClose=addon.Deactivate})
-
-    local tabgroup = addon:AddUI(gui,"TabGroup",{SetFullHeight=true,SetTabs=lfgTabs,SetLayout="Fill"},true,{OnGroupSelected=addon.LfgScan})
-    tabgroup:SelectTab(lfgGroups[1])
-    lfgContainer = addon:AddUI(tabgroup,"ScrollFrame")
-
-    local hgroupc = addon:AddUI(gui,"InlineGroup",{SetLayout="Fill",SetFullHeight=true,SetTitle="Realm info"},true)
-    hgroup = addon:AddUI(hgroupc,"ScrollFrame")
-
-    addon:AddUI(hgroup,"Heading",{SetText="Current realm"})
-    currealm = addon:AddUI(hgroup,"SimpleGroup");
-    addon:AddUI(hgroup,"Button",{SetText="Refresh",SetRelativeWidth=0.5,SetHeight=20},true,{OnClick=addon.ScanRealm})
-
-    addon:AddUI(hgroup,"Heading",{SetText="Party statistics"})
-    partyrealm = addon:AddUI(hgroup,"SimpleGroup");
-    leavebtn = addon:AddUI(hgroup,"Button",{SetText="Leave",SetRelativeWidth=0.5,SetHeight=20,SetDisabled=not IsInGroup()},true,{OnClick=LeaveParty})
-
-    addon:AddUI(hgroup,"Heading",{SetText="Recent realms"})
-    recrealm = addon:AddUI(hgroup,"SimpleGroup");
-    addon:AddUI(hgroup,"Button",{SetText="Clear",SetRelativeWidth=0.5,SetHeight=20},true,{OnClick=addon.ClearRecentRealms})
-
-    gui:DoLayout()
-    gui:PauseLayout()
-
-    tabgroup:SetPoint("BOTTOMRIGHT", gui.content, "BOTTOMRIGHT", -200, 0)
-    hgroupc:SetPoint("TOPLEFT", gui.content, "TOPRIGHT", -200, 0)
-    hgroupc:SetPoint("BOTTOMRIGHT", gui.content, "BOTTOMRIGHT")
-
-    if curRealmStat then addon:updateCurrentRealm() end
-    addon:updatePartyInfo()
-    addon:updateRecentRealms()
-end
-
--- LFG scanning routine
-
-function addon.refreshLfgCurrent()
-    addon.LfgScan(nil,nil,curLfgGroup)
-end
-
-function addon.LfgScan(widget, callback, tab)
-    if not tab then return end
-    lfgScanInProgress = true
-    if lfgContainer then lfgContainer:ReleaseChildren() end
-    curLfgGroup = tab
-    C_LFGList.Search(curLfgGroup,"")
-end
-
-function addon:LfgScanFailed(event, reason)
-    print("scan failed "..reason)
-    if reason == "throttled" then
-        addon:ScheduleTimer(addon.LfgScan, 2, nil, nil, curLfgGroup)
-    end
-end
-
-local function WeightLfgItem(item)
-    local leaderRealm = 3
-    if (item.realm ~= "???") then leaderRealm = (5 - (recentRealms[item.realm] or 0)) end
-    local weight =
-        (item.autoinv and 5 or 2) *
-                ((item.friends > 0) and 2 or 3) *
-                ((item.ilvl > 0) and 2 or 3) *
-                ((item.voice ~= "") and 2 or 4)
-    local count = item.pcount;
-    local countWeight
-    if count >= 39 or count <= 1 then countWeight = 1
-    elseif count >= 35 or count <= 4 then countWeight = 2
-    elseif count >= 30 or count <= 10 then countWeight = 3
-    else countWeight = 4 end
-    return weight * leaderRealm * countWeight;
-end
-
-function addon:LfgResponseData()
-    lfgScanInProgress = false;
-    addon:UpdateCurrentLfgInfo()
-end
-
-function addon:UpdateCurrentLfgInfo(repeated)
-    if lfgScanInProgress then return end
-    local count, list = C_LFGList.GetSearchResults()
-    local lfgList = {}
-    local lfgEntries = 0
-    local hasUnknowns = false
-    for i = 1,count do
-        local rid = list[i];
-        if not rid then break end
-        local _, action, caption, desc, voice, ilvl, time, bnetfr, charfr, guild, delisted, fullname, pcount, autoinv = C_LFGList.GetSearchResultInfo(rid)
-        if not fullname then
-            fullname = "???-???"
-            hasUnknowns = true
-        end
-        if not delisted then
-            local pname, realm = strsplit(realmSep, fullname)
-            realm = realm or homeRealm
-            local item = {
-                rid=rid,
-                action=action,
-                caption=caption,
-                desc=desc,
-                ilvl=ilvl,
-                voice=voice,
-                time=time,
-                friends=bnetfr+charfr+guild,
-                name=pname,
-                realm = realm,
-                pcount=pcount,
-                autoinv=autoinv
-            }
-            item.weight = WeightLfgItem(item)
-            table.insert(lfgList, item)
-            lfgEntries = lfgEntries + 1
-        end
-    end
-    addon:refreshLFGList(lfgList, lfgEntries);
-    if hasUnknowns and repeated ~= true then addon:ScheduleTimer("UpdateCurrentLfgInfo", 1, true) end
-end
-
-function addon:refreshLFGList(list, count)
-    if not lfgContainer then return end
-    lfgContainer:PauseLayout()
-    lfgContainer:ReleaseChildren()
-    table.sort(list, addon.SortLfgItems)
-
-    local canJoin = addon.canJoinGroup()
-
-    for i=1,count do
-        local data = list[i];
-        local renderer = addon:AddUI(lfgContainer,"SimpleGroup",{SetLayout="Flow",SetHeight=20,SetAutoAdjustHeight=false});
-
-        addon:AddUI(renderer,"Label",{SetText=data.caption,SetRelativeWidth=0.4},true)
-        addon:AddUI(renderer,"Label",{SetText=data.pcount,SetRelativeWidth=0.05},true)
-        local realm = addon:AddUI(renderer,"Label",{SetText=data.realm,SetRelativeWidth=0.25},true)
-        if recentRealms[data.realm] then
-            local r,g,b = GetItemQualityColor(recentRealms[data.realm]);
-            realm:SetColor(r,g,b)
-        end
-
-        local tooltip = {}
-        if data.voice ~= "" then table.insert(tooltip,"Voice: "..data.voice) end
-        if data.friends > 0 then table.insert(tooltip,"Friends: "..data.friends) end
-        if data.ilvl > 0 then table.insert(tooltip,"Min. Ilvl: "..data.ilvl) end
-        addon:AddIcon(renderer, "Interface/GossipFrame/ActiveQuestIcon", 16, tooltip[1] ~= nil, tooltip)
-        addon:AddIcon(renderer, READY_CHECK_READY_TEXTURE, 16, data.autoinv, {"Auto invite!"})
-        local btn = addon:AddUI(renderer,"Button",{SetText="Join",SetRelativeWidth=0.2,SetHeight=20,SetDisabled=not canJoin},true,{OnClick=addon.joinGroup})
-        btn:SetUserData('rid',data.rid)
-    end
-    addon:AddUI(lfgContainer,"Button",{SetText="Refresh",SetWidth=100,SetHeight=20},true,{OnClick=addon.refreshLfgCurrent})
-
-    lfgContainer:ResumeLayout()
-    lfgContainer:DoLayout()
-end
-
-function addon.SortLfgItems(a,b)
-    return a.weight > b.weight
-end
-
-function addon.canJoinGroup()
-    return (not IsInGroup()) or (UnitIsGroupLeader('player') and not IsInRaid())
-end
-
-function addon.joinGroup(widget)
-    if not addon.canJoinGroup() then return end
-    local rid = widget:GetUserData('rid');
-    widget:SetDisabled(true)
-    C_LFGList.ApplyToGroup(rid, "", C_LFGList.GetAvailableRoles())
-end
-
-function addon:updateAppStatus(event, id, status, oldstatus)
-    if status == "invited" then
-        LFGListInviteDialog_Accept(LFGListInviteDialog)
-    end
-end
-
--- Zone scanning routine
-
-function addon:LeaveCombat()
-    addon:UnregisterEvent("PLAYER_REGEN_ENABLED")
-    scanstate = 0
-    addon:ScanRealm();
-end
-
-function addon:ScanRealm()
-    if scanstate > 0 then return end
-    if InCombatLockdown() then
-        addon:RegisterEvent("PLAYER_REGEN_ENABLED","LeaveCombat")
-        scanstate = 1
-        addon:AddLabel(currealm, "Scan sheduled after combat...", 3);
-        return
-    end
-    scanstate = 2
-    addon:AddLabel(currealm, "Scanning...", 3);
-    local searchString = _G.WHO_TAG_ZONE .. '"' .. GetZoneText() .. '"';
-    wholib:Who(searchString, {callback = "ScanResult", handler=addon})
-end
-
-function addon:ScanResult(query, results, complete)
-    local realms = {}
-    for _, player in ipairs(results) do
-        addon:AddRealmStat(player.Name, realms);
-    end
-    curRealmStat = addon:GetRealmStat(realms);
-    addon:updateCurrentRealm();
-    local rescan = scanstate == 3
-    scanstate = 0;
-    if rescan then addon:ScanRealm() end
-end
-
-function addon:AddRealmStat(name, realms)
-    local _, realm = strsplit(realmSep, name) -- TODO exclude self
-    realm = realm or homeRealm
-    realms[realm] = (realms[realm] or 0) + 1
-end
-
-function addon:AddUnitIdStat(unitid, realms)
-    local name, realm = UnitName(unitid);
-    if not name then return end
-    if realm == "" then realm = homeRealm end
-    realm = realm or homeRealm
-    realms[realm] = (realms[realm] or 0) + 1
-end
-
-function addon:GetRealmStat(realms)
-    local rcount = 0;
-    local stat = {};
-    local pcount = 0;
-    for realm,count in pairs(realms) do
-        table.insert(stat,{realm=realm,count=count})
-        rcount = rcount + 1;
-        pcount = pcount + count;
-    end
-    if rcount > 1 then
-        table.sort(stat, function(a,b) return a.count > b.count end)
-        stat.max = stat[1].count
-    else stat.max = 0 end
-    stat.players = pcount;
-    stat.realms = rcount;
-    return stat;
-end
-
-function addon:updateCurrentRealm()
-    if not currealm then return end
-    currealm:ReleaseChildren()
-    if curRealmStat.players < 5 then
-        addon:AddLabel(currealm, "Not enough players", 0);
-    end
-    local sureplayers = curRealmStat.players/2
-    local maybeplayers = math.max(curRealmStat.max/4, 3)
-    local recentRealmUpdated = false
-    for i=1,curRealmStat.realms do
-        local data = curRealmStat[i]
-        if data.count >= maybeplayers then
-            local realm = data.realm
-            local rvalue = (data.count >= sureplayers) and 4 or 2
-            if (rvalue > (recentRealms[realm] or 0)) then
-                recentRealmUpdated = true
-                recentRealms[realm] = rvalue
-            end
-        end
-    end
-    addon:addLabels(currealm, curRealmStat, sureplayers, maybeplayers, 3, "Realm unknown");
-    if recentRealmUpdated then
-        addon:UpdateCurrentLfgInfo()
-        addon:updateRecentRealms()
-    end
-end
-
-function addon:addLabels(container, stats, epict, whitet, grayt, emptytext)
-    if stats.realms == 0 then
-        addon:AddLabel(container, emptytext)
-    else
-        local players = stats.players
-        local pleft = players;
-        for i=1,stats.realms do
-            local data = stats[i]
-            local count = data.count;
-            local percent = math.ceil(100 * count / players - 0.5)
-            local label = data.realm .. " (" .. percent .. "%)";
-            local color
-            if count < grayt then break
-            elseif count < whitet then color = 1
-            elseif count < epict then color = 2
-            else color = 4 end
-            pleft = pleft - count
-            addon:AddLabel(container, label, color);
-        end
-        if pleft > 0 then
-            addon:AddLabel(container, "Other" .. " (" .. math.ceil(100 * pleft / players - 0.5) .. "%)", 0);
-        end
-    end
-end
-
-function addon:updateRecentRealms()
-    if not recrealm then return end
-    recrealm:ReleaseChildren()
-    local nothing = true;
-    for realm,color in pairs(recentRealms) do
-        addon:AddLabel(recrealm, realm,color);
-        nothing = false;
-    end
-    if nothing then
-        addon:AddLabel(recrealm, "No recent realms");
-    end
-end
-
-function addon:ClearRecentRealms()
-    recentRealms = {}
-    addon:UpdateCurrentLfgInfo()
-    addon:updateRecentRealms()
-end
-
-function addon:SheduleScan()
-    if scanstate == 0 then
-        addon:ScanRealm()
-    elseif scanstate == 2 then
-        scanstate = 3
-    end
-end
-
-function addon:updatePartyInfo()
-    if not partyrealm then return end
-    partyrealm:ReleaseChildren()
-    local realms = {}
-    local inGroup = IsInGroup()
-    if IsInGroup() then
-        if IsInRaid() then
-            for i=1, MAX_RAID_MEMBERS do
-                addon:AddUnitIdStat("raid"..i, realms)
-            end
-        else
-            for i=1, MAX_PARTY_MEMBERS do
-                addon:AddUnitIdStat("party"..i, realms)
-            end
-        end
-    end
-    if inGroup ~= wasInGroup  then
-        addon:SheduleScan()
-        wasInGroup = inGroup
-        addon:UpdateCurrentLfgInfo()
-        leavebtn:SetDisabled(not inGroup)
-    end
-    local partyStat = addon:GetRealmStat(realms);
-    addon:addLabels(partyrealm, partyStat, partyStat.players/2, 2, 0, "Not in party");
-end
-
--- Utils functions
-
-function addon:AddUI(owner, type, parameters, manual, callbacks)
-    local ui = AceGUI:Create(type)
-    if parameters then
-        for key,value in pairs(parameters) do ui[key](ui,value) end
-    end
-    if callbacks then
-        for key,value in pairs(callbacks) do ui:SetCallback(key,value) end
-    end
-    if not manual then ui:SetFullWidth(true) end
-    if owner then owner:AddChild(ui) end
-    return ui
-end
-
-function addon:AddLabel(owner, text, color, icon)
-    local label = AceGUI:Create("Label")
-    label:SetText(text)
-    label:SetFullWidth(true)
-    if color ~= nil then
-        local r,g,b = GetItemQualityColor(color);
-        label:SetColor(r,g,b)
-    end
-    owner:AddChild(label)
-    return label
-end
-
-function addon:AddIcon(owner, image, size, visible, tooltip)
-    if visible ~= false then
-        local icon = addon:AddUI(owner,"Icon",{SetWidth=size,SetHeight=size},true)
-        icon:SetImage(image)
-        icon:SetImageSize(size,size)
-        if tooltip then
-            icon:SetUserData('tooltip',tooltip)
-            icon:SetCallback("OnEnter",addon.ShowTooltip)
-            icon:SetCallback("OnLeave",addon.HideTooltip)
-        end
-    else -- add placeholder
-        addon:AddUI(owner,"Label",{SetWidth=size,SetHeight=size},true)
-    end
-end
-
-function addon.ShowTooltip(widget)
-    GameTooltip:SetOwner(widget.frame, "ANCHOR_TOP")
-    local tooltip = widget:GetUserData('tooltip')
-    for i=1,#tooltip do
-        GameTooltip:AddLine(tooltip[i], 1, 1, 1)
-    end
-    GameTooltip:Show()
-end
-
-function addon.HideTooltip(widget)
-    GameTooltip:Hide()
-end
\ No newline at end of file
--- a/CrossRealmAssist.toc	Wed Mar 04 21:37:31 2015 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-## Interface: 60100
-## Title: Cross Realm Assist
-## Notes: Easy tool for cross-realm hoping
-## Author: ShadowTheAge
-## Version: 0.1
-
-embeds.xml
-CrossRealmAssist.lua
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CrossRealmAssist/CrossRealmAssist.lua	Wed Mar 04 21:54:21 2015 +0300
@@ -0,0 +1,472 @@
+CrossRealmAssist = LibStub("AceAddon-3.0"):NewAddon("CrossRealmAssist", "AceEvent-3.0", "AceConsole-3.0", "AceTimer-3.0")
+local AceGUI = LibStub("AceGUI-3.0")
+local addon = CrossRealmAssist;
+local wholib = LibStub:GetLibrary('LibWho-2.0'):Library()
+
+local hgroup, lfgContainer, currealm, partyrealm, recrealm, curRealmStat, homeRealm, realmSep, gui, leavebtn
+
+local scanstate=0
+local recentRealms={}
+local active=false
+
+local lfgGroups={
+    6, -- Custom
+    10, -- Ashran
+    1, -- Quests
+    3, -- Raids
+    8 -- BGs
+}
+
+local lfgTabs
+local curLfgGroup
+local sheduledScan
+local wasInGroup
+local lfgScanInProgress=false
+
+function addon:OnInitialize()
+
+end
+
+function addon:OnEnable()
+    local tabCount = table.getn(lfgGroups)
+    lfgTabs = {}
+    for i=1,tabCount do
+        local cat = lfgGroups[i]
+        table.insert(lfgTabs,{value=cat,text=(C_LFGList.GetCategoryInfo(cat))})
+    end
+    realmSep = _G.REALM_SEPARATORS
+    homeRealm = GetRealmName()
+    addon:RegisterChatCommand("cra", "Activate")
+    addon:RegisterChatCommand("crossrealmassist", "Activate")
+end
+
+function addon:OnDisable()
+    addon:Deactivate()
+end
+
+function addon:Activate()
+    if active then return end
+    active=true
+    wasInGroup = IsInGroup()
+    addon:CreateUI()
+    addon:ScanRealm()
+    addon:RegisterEvent("ZONE_CHANGED_NEW_AREA", "SheduleScan")
+    addon:RegisterEvent("LFG_LIST_SEARCH_RESULTS_RECEIVED", "LfgResponseData")
+    addon:RegisterEvent("LFG_LIST_SEARCH_FAILED", "LfgScanFailed")
+    addon:RegisterEvent("GROUP_ROSTER_UPDATE", "updatePartyInfo")
+    addon:RegisterEvent("LFG_LIST_APPLICATION_STATUS_UPDATED", "updateAppStatus")
+end
+
+function addon:Deactivate()
+    if not active then return end
+    active = false
+    scanstate = 0
+    lfgScanInProgress = false
+    gui:Release();
+    gui, lfgContainer, hgroup, currealm, partyrealm, recrealm, leavebtn = nil,nil,nil,nil,nil,nil,nil
+    addon:UnregisterEvent("ZONE_CHANGED_NEW_AREA")
+    addon:UnregisterEvent("PLAYER_REGEN_ENABLED")
+    addon:UnregisterEvent("LFG_LIST_SEARCH_RESULTS_RECEIVED")
+    addon:UnregisterEvent("LFG_LIST_SEARCH_FAILED")
+    addon:UnregisterEvent("GROUP_ROSTER_UPDATE")
+    addon:UnregisterEvent("LFG_LIST_APPLICATION_STATUS_UPDATED")
+end
+
+function addon:CreateUI()
+    gui = addon:AddUI(nil,"Window",{SetTitle="Cross Realm Assist",EnableResize=false,SetLayout="Flow"},true,{OnClose=addon.Deactivate})
+
+    local tabgroup = addon:AddUI(gui,"TabGroup",{SetFullHeight=true,SetTabs=lfgTabs,SetLayout="Fill"},true,{OnGroupSelected=addon.LfgScan})
+    tabgroup:SelectTab(lfgGroups[1])
+    lfgContainer = addon:AddUI(tabgroup,"ScrollFrame")
+
+    local hgroupc = addon:AddUI(gui,"InlineGroup",{SetLayout="Fill",SetFullHeight=true,SetTitle="Realm info"},true)
+    hgroup = addon:AddUI(hgroupc,"ScrollFrame")
+
+    addon:AddUI(hgroup,"Heading",{SetText="Current realm"})
+    currealm = addon:AddUI(hgroup,"SimpleGroup");
+    addon:AddUI(hgroup,"Button",{SetText="Refresh",SetRelativeWidth=0.5,SetHeight=20},true,{OnClick=addon.ScanRealm})
+
+    addon:AddUI(hgroup,"Heading",{SetText="Party statistics"})
+    partyrealm = addon:AddUI(hgroup,"SimpleGroup");
+    leavebtn = addon:AddUI(hgroup,"Button",{SetText="Leave",SetRelativeWidth=0.5,SetHeight=20,SetDisabled=not IsInGroup()},true,{OnClick=LeaveParty})
+
+    addon:AddUI(hgroup,"Heading",{SetText="Recent realms"})
+    recrealm = addon:AddUI(hgroup,"SimpleGroup");
+    addon:AddUI(hgroup,"Button",{SetText="Clear",SetRelativeWidth=0.5,SetHeight=20},true,{OnClick=addon.ClearRecentRealms})
+
+    gui:DoLayout()
+    gui:PauseLayout()
+
+    tabgroup:SetPoint("BOTTOMRIGHT", gui.content, "BOTTOMRIGHT", -200, 0)
+    hgroupc:SetPoint("TOPLEFT", gui.content, "TOPRIGHT", -200, 0)
+    hgroupc:SetPoint("BOTTOMRIGHT", gui.content, "BOTTOMRIGHT")
+
+    if curRealmStat then addon:updateCurrentRealm() end
+    addon:updatePartyInfo()
+    addon:updateRecentRealms()
+end
+
+-- LFG scanning routine
+
+function addon.refreshLfgCurrent()
+    addon.LfgScan(nil,nil,curLfgGroup)
+end
+
+function addon.LfgScan(widget, callback, tab)
+    if not tab then return end
+    lfgScanInProgress = true
+    if lfgContainer then lfgContainer:ReleaseChildren() end
+    curLfgGroup = tab
+    C_LFGList.Search(curLfgGroup,"")
+end
+
+function addon:LfgScanFailed(event, reason)
+    print("scan failed "..reason)
+    if reason == "throttled" then
+        addon:ScheduleTimer(addon.LfgScan, 2, nil, nil, curLfgGroup)
+    end
+end
+
+local function WeightLfgItem(item)
+    local leaderRealm = 3
+    if (item.realm ~= "???") then leaderRealm = (5 - (recentRealms[item.realm] or 0)) end
+    local weight =
+        (item.autoinv and 5 or 2) *
+                ((item.friends > 0) and 2 or 3) *
+                ((item.ilvl > 0) and 2 or 3) *
+                ((item.voice ~= "") and 2 or 4)
+    local count = item.pcount;
+    local countWeight
+    if count >= 39 or count <= 1 then countWeight = 1
+    elseif count >= 35 or count <= 4 then countWeight = 2
+    elseif count >= 30 or count <= 10 then countWeight = 3
+    else countWeight = 4 end
+    return weight * leaderRealm * countWeight;
+end
+
+function addon:LfgResponseData()
+    lfgScanInProgress = false;
+    addon:UpdateCurrentLfgInfo()
+end
+
+function addon:UpdateCurrentLfgInfo(repeated)
+    if lfgScanInProgress then return end
+    local count, list = C_LFGList.GetSearchResults()
+    local lfgList = {}
+    local lfgEntries = 0
+    local hasUnknowns = false
+    for i = 1,count do
+        local rid = list[i];
+        if not rid then break end
+        local _, action, caption, desc, voice, ilvl, time, bnetfr, charfr, guild, delisted, fullname, pcount, autoinv = C_LFGList.GetSearchResultInfo(rid)
+        if not fullname then
+            fullname = "???-???"
+            hasUnknowns = true
+        end
+        if not delisted then
+            local pname, realm = strsplit(realmSep, fullname)
+            realm = realm or homeRealm
+            local item = {
+                rid=rid,
+                action=action,
+                caption=caption,
+                desc=desc,
+                ilvl=ilvl,
+                voice=voice,
+                time=time,
+                friends=bnetfr+charfr+guild,
+                name=pname,
+                realm = realm,
+                pcount=pcount,
+                autoinv=autoinv
+            }
+            item.weight = WeightLfgItem(item)
+            table.insert(lfgList, item)
+            lfgEntries = lfgEntries + 1
+        end
+    end
+    addon:refreshLFGList(lfgList, lfgEntries);
+    if hasUnknowns and repeated ~= true then addon:ScheduleTimer("UpdateCurrentLfgInfo", 1, true) end
+end
+
+function addon:refreshLFGList(list, count)
+    if not lfgContainer then return end
+    lfgContainer:PauseLayout()
+    lfgContainer:ReleaseChildren()
+    table.sort(list, addon.SortLfgItems)
+
+    local canJoin = addon.canJoinGroup()
+
+    for i=1,count do
+        local data = list[i];
+        local renderer = addon:AddUI(lfgContainer,"SimpleGroup",{SetLayout="Flow",SetHeight=20,SetAutoAdjustHeight=false});
+
+        addon:AddUI(renderer,"Label",{SetText=data.caption,SetRelativeWidth=0.4},true)
+        addon:AddUI(renderer,"Label",{SetText=data.pcount,SetRelativeWidth=0.05},true)
+        local realm = addon:AddUI(renderer,"Label",{SetText=data.realm,SetRelativeWidth=0.25},true)
+        if recentRealms[data.realm] then
+            local r,g,b = GetItemQualityColor(recentRealms[data.realm]);
+            realm:SetColor(r,g,b)
+        end
+
+        local tooltip = {}
+        if data.voice ~= "" then table.insert(tooltip,"Voice: "..data.voice) end
+        if data.friends > 0 then table.insert(tooltip,"Friends: "..data.friends) end
+        if data.ilvl > 0 then table.insert(tooltip,"Min. Ilvl: "..data.ilvl) end
+        addon:AddIcon(renderer, "Interface/GossipFrame/ActiveQuestIcon", 16, tooltip[1] ~= nil, tooltip)
+        addon:AddIcon(renderer, READY_CHECK_READY_TEXTURE, 16, data.autoinv, {"Auto invite!"})
+        local btn = addon:AddUI(renderer,"Button",{SetText="Join",SetRelativeWidth=0.2,SetHeight=20,SetDisabled=not canJoin},true,{OnClick=addon.joinGroup})
+        btn:SetUserData('rid',data.rid)
+    end
+    addon:AddUI(lfgContainer,"Button",{SetText="Refresh",SetWidth=100,SetHeight=20},true,{OnClick=addon.refreshLfgCurrent})
+
+    lfgContainer:ResumeLayout()
+    lfgContainer:DoLayout()
+end
+
+function addon.SortLfgItems(a,b)
+    return a.weight > b.weight
+end
+
+function addon.canJoinGroup()
+    return (not IsInGroup()) or (UnitIsGroupLeader('player') and not IsInRaid())
+end
+
+function addon.joinGroup(widget)
+    if not addon.canJoinGroup() then return end
+    local rid = widget:GetUserData('rid');
+    widget:SetDisabled(true)
+    C_LFGList.ApplyToGroup(rid, "", C_LFGList.GetAvailableRoles())
+end
+
+function addon:updateAppStatus(event, id, status, oldstatus)
+    if status == "invited" then
+        LFGListInviteDialog_Accept(LFGListInviteDialog)
+    end
+end
+
+-- Zone scanning routine
+
+function addon:LeaveCombat()
+    addon:UnregisterEvent("PLAYER_REGEN_ENABLED")
+    scanstate = 0
+    addon:ScanRealm();
+end
+
+function addon:ScanRealm()
+    if scanstate > 0 then return end
+    if InCombatLockdown() then
+        addon:RegisterEvent("PLAYER_REGEN_ENABLED","LeaveCombat")
+        scanstate = 1
+        addon:AddLabel(currealm, "Scan sheduled after combat...", 3);
+        return
+    end
+    scanstate = 2
+    addon:AddLabel(currealm, "Scanning...", 3);
+    local searchString = _G.WHO_TAG_ZONE .. '"' .. GetZoneText() .. '"';
+    wholib:Who(searchString, {callback = "ScanResult", handler=addon})
+end
+
+function addon:ScanResult(query, results, complete)
+    local realms = {}
+    for _, player in ipairs(results) do
+        addon:AddRealmStat(player.Name, realms);
+    end
+    curRealmStat = addon:GetRealmStat(realms);
+    addon:updateCurrentRealm();
+    local rescan = scanstate == 3
+    scanstate = 0;
+    if rescan then addon:ScanRealm() end
+end
+
+function addon:AddRealmStat(name, realms)
+    local _, realm = strsplit(realmSep, name) -- TODO exclude self
+    realm = realm or homeRealm
+    realms[realm] = (realms[realm] or 0) + 1
+end
+
+function addon:AddUnitIdStat(unitid, realms)
+    local name, realm = UnitName(unitid);
+    if not name then return end
+    if realm == "" then realm = homeRealm end
+    realm = realm or homeRealm
+    realms[realm] = (realms[realm] or 0) + 1
+end
+
+function addon:GetRealmStat(realms)
+    local rcount = 0;
+    local stat = {};
+    local pcount = 0;
+    for realm,count in pairs(realms) do
+        table.insert(stat,{realm=realm,count=count})
+        rcount = rcount + 1;
+        pcount = pcount + count;
+    end
+    if rcount > 1 then
+        table.sort(stat, function(a,b) return a.count > b.count end)
+        stat.max = stat[1].count
+    else stat.max = 0 end
+    stat.players = pcount;
+    stat.realms = rcount;
+    return stat;
+end
+
+function addon:updateCurrentRealm()
+    if not currealm then return end
+    currealm:ReleaseChildren()
+    if curRealmStat.players < 5 then
+        addon:AddLabel(currealm, "Not enough players", 0);
+    end
+    local sureplayers = curRealmStat.players/2
+    local maybeplayers = math.max(curRealmStat.max/4, 3)
+    local recentRealmUpdated = false
+    for i=1,curRealmStat.realms do
+        local data = curRealmStat[i]
+        if data.count >= maybeplayers then
+            local realm = data.realm
+            local rvalue = (data.count >= sureplayers) and 4 or 2
+            if (rvalue > (recentRealms[realm] or 0)) then
+                recentRealmUpdated = true
+                recentRealms[realm] = rvalue
+            end
+        end
+    end
+    addon:addLabels(currealm, curRealmStat, sureplayers, maybeplayers, 3, "Realm unknown");
+    if recentRealmUpdated then
+        addon:UpdateCurrentLfgInfo()
+        addon:updateRecentRealms()
+    end
+end
+
+function addon:addLabels(container, stats, epict, whitet, grayt, emptytext)
+    if stats.realms == 0 then
+        addon:AddLabel(container, emptytext)
+    else
+        local players = stats.players
+        local pleft = players;
+        for i=1,stats.realms do
+            local data = stats[i]
+            local count = data.count;
+            local percent = math.ceil(100 * count / players - 0.5)
+            local label = data.realm .. " (" .. percent .. "%)";
+            local color
+            if count < grayt then break
+            elseif count < whitet then color = 1
+            elseif count < epict then color = 2
+            else color = 4 end
+            pleft = pleft - count
+            addon:AddLabel(container, label, color);
+        end
+        if pleft > 0 then
+            addon:AddLabel(container, "Other" .. " (" .. math.ceil(100 * pleft / players - 0.5) .. "%)", 0);
+        end
+    end
+end
+
+function addon:updateRecentRealms()
+    if not recrealm then return end
+    recrealm:ReleaseChildren()
+    local nothing = true;
+    for realm,color in pairs(recentRealms) do
+        addon:AddLabel(recrealm, realm,color);
+        nothing = false;
+    end
+    if nothing then
+        addon:AddLabel(recrealm, "No recent realms");
+    end
+end
+
+function addon:ClearRecentRealms()
+    recentRealms = {}
+    addon:UpdateCurrentLfgInfo()
+    addon:updateRecentRealms()
+end
+
+function addon:SheduleScan()
+    if scanstate == 0 then
+        addon:ScanRealm()
+    elseif scanstate == 2 then
+        scanstate = 3
+    end
+end
+
+function addon:updatePartyInfo()
+    if not partyrealm then return end
+    partyrealm:ReleaseChildren()
+    local realms = {}
+    local inGroup = IsInGroup()
+    if IsInGroup() then
+        if IsInRaid() then
+            for i=1, MAX_RAID_MEMBERS do
+                addon:AddUnitIdStat("raid"..i, realms)
+            end
+        else
+            for i=1, MAX_PARTY_MEMBERS do
+                addon:AddUnitIdStat("party"..i, realms)
+            end
+        end
+    end
+    if inGroup ~= wasInGroup  then
+        addon:SheduleScan()
+        wasInGroup = inGroup
+        addon:UpdateCurrentLfgInfo()
+        leavebtn:SetDisabled(not inGroup)
+    end
+    local partyStat = addon:GetRealmStat(realms);
+    addon:addLabels(partyrealm, partyStat, partyStat.players/2, 2, 0, "Not in party");
+end
+
+-- Utils functions
+
+function addon:AddUI(owner, type, parameters, manual, callbacks)
+    local ui = AceGUI:Create(type)
+    if parameters then
+        for key,value in pairs(parameters) do ui[key](ui,value) end
+    end
+    if callbacks then
+        for key,value in pairs(callbacks) do ui:SetCallback(key,value) end
+    end
+    if not manual then ui:SetFullWidth(true) end
+    if owner then owner:AddChild(ui) end
+    return ui
+end
+
+function addon:AddLabel(owner, text, color, icon)
+    local label = AceGUI:Create("Label")
+    label:SetText(text)
+    label:SetFullWidth(true)
+    if color ~= nil then
+        local r,g,b = GetItemQualityColor(color);
+        label:SetColor(r,g,b)
+    end
+    owner:AddChild(label)
+    return label
+end
+
+function addon:AddIcon(owner, image, size, visible, tooltip)
+    if visible ~= false then
+        local icon = addon:AddUI(owner,"Icon",{SetWidth=size,SetHeight=size},true)
+        icon:SetImage(image)
+        icon:SetImageSize(size,size)
+        if tooltip then
+            icon:SetUserData('tooltip',tooltip)
+            icon:SetCallback("OnEnter",addon.ShowTooltip)
+            icon:SetCallback("OnLeave",addon.HideTooltip)
+        end
+    else -- add placeholder
+        addon:AddUI(owner,"Label",{SetWidth=size,SetHeight=size},true)
+    end
+end
+
+function addon.ShowTooltip(widget)
+    GameTooltip:SetOwner(widget.frame, "ANCHOR_TOP")
+    local tooltip = widget:GetUserData('tooltip')
+    for i=1,#tooltip do
+        GameTooltip:AddLine(tooltip[i], 1, 1, 1)
+    end
+    GameTooltip:Show()
+end
+
+function addon.HideTooltip(widget)
+    GameTooltip:Hide()
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CrossRealmAssist/CrossRealmAssist.toc	Wed Mar 04 21:54:21 2015 +0300
@@ -0,0 +1,8 @@
+## Interface: 60100
+## Title: Cross Realm Assist
+## Notes: Easy tool for cross-realm hoping
+## Author: ShadowTheAge
+## Version: 0.1
+
+embeds.xml
+CrossRealmAssist.lua
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CrossRealmAssist/embeds.xml	Wed Mar 04 21:54:21 2015 +0300
@@ -0,0 +1,9 @@
+<Ui xsi:schemaLocation="http://www.blizzard.com/wow/ui/ ..\FrameXML\UI.xsd">
+	<Script file="libs\LibStub\LibStub.lua"/>
+	<Include file="Libs\AceAddon-3.0\AceAddon-3.0.xml"/>
+	<Include file="libs\AceGUI-3.0\AceGUI-3.0.xml"/>
+	<Include file="Libs\AceConsole-3.0\AceConsole-3.0.xml"/>
+	<Include file="Libs\AceEvent-3.0\AceEvent-3.0.xml"/>
+	<Include file="Libs\AceTimer-3.0\AceTimer-3.0.xml"/>
+	<Script file="Libs\LibWho-2.0\LibWho-2.0\LibWho-2.0.lua"/>
+</Ui>
\ No newline at end of file
--- a/embeds.xml	Wed Mar 04 21:37:31 2015 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-<Ui xsi:schemaLocation="http://www.blizzard.com/wow/ui/ ..\FrameXML\UI.xsd">
-	<Script file="libs\LibStub\LibStub.lua"/>
-	<Include file="Libs\AceAddon-3.0\AceAddon-3.0.xml"/>
-	<Include file="libs\AceGUI-3.0\AceGUI-3.0.xml"/>
-	<Include file="Libs\AceConsole-3.0\AceConsole-3.0.xml"/>
-	<Include file="Libs\AceEvent-3.0\AceEvent-3.0.xml"/>
-	<Include file="Libs\AceTimer-3.0\AceTimer-3.0.xml"/>
-	<Script file="Libs\LibWho-2.0\LibWho-2.0\LibWho-2.0.lua"/>
-</Ui>
\ No newline at end of file