Mercurial > wow > reaction
comparison modules/FuBar_ReActionFu/lib/LibRock-1.0/LibRock-1.0.lua @ 30:0d95ce7a9ec2
- added Ace3 externs
- converted ReAction_ConfigUI to use blizzard interface addons panel via AceConfigDialog-3.0
- partially converted FuBar module to LibRock, deprecated it (going to remove it entirely later)
- cleaned up a couple other tidbits
| author | Flick <flickerstreak@gmail.com> |
|---|---|
| date | Wed, 02 Apr 2008 23:31:13 +0000 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 29:9c89042bc328 | 30:0d95ce7a9ec2 |
|---|---|
| 1 --[[ | |
| 2 Name: LibRock-1.0 | |
| 3 Revision: $Rev: 63317 $ | |
| 4 Developed by: ckknight (ckknight@gmail.com) | |
| 5 Website: http://www.wowace.com/ | |
| 6 Description: Library to allow for library and addon creation and easy table recycling functions. | |
| 7 License: LGPL v2.1 | |
| 8 ]] | |
| 9 | |
| 10 local MAJOR_VERSION = "LibRock-1.0" | |
| 11 local MINOR_VERSION = tonumber(("$Revision: 63317 $"):match("(%d+)")) - 60000 | |
| 12 | |
| 13 local _G = _G | |
| 14 local GetLocale = _G.GetLocale | |
| 15 local CATEGORIES | |
| 16 if GetLocale() == "deDE" then | |
| 17 CATEGORIES = { | |
| 18 ["Action Bars"] = "Aktionsleisten", | |
| 19 ["Auction"] = "Auktion", | |
| 20 ["Audio"] = "Audio", | |
| 21 ["Battlegrounds/PvP"] = "Schlachtfeld/PvP", | |
| 22 ["Buffs"] = "Stärkungszauber", | |
| 23 ["Chat/Communication"] = "Chat/Kommunikation", | |
| 24 ["Druid"] = "Druide", | |
| 25 ["Hunter"] = "Jäger", | |
| 26 ["Mage"] = "Magier", | |
| 27 ["Paladin"] = "Paladin", | |
| 28 ["Priest"] = "Priester", | |
| 29 ["Rogue"] = "Schurke", | |
| 30 ["Shaman"] = "Schamane", | |
| 31 ["Warlock"] = "Hexenmeister", | |
| 32 ["Warrior"] = "Krieger", | |
| 33 ["Healer"] = "Heiler", | |
| 34 ["Tank"] = "Tank", | |
| 35 ["Caster"] = "Zauberer", | |
| 36 ["Combat"] = "Kampf", | |
| 37 ["Compilations"] = "Zusammenstellungen", | |
| 38 ["Data Export"] = "Datenexport", | |
| 39 ["Development Tools"] = "Entwicklungstools", | |
| 40 ["Guild"] = "Gilde", | |
| 41 ["Frame Modification"] = "Frameveränderungen", | |
| 42 ["Interface Enhancements"] = "Interfaceverbesserungen", | |
| 43 ["Inventory"] = "Inventar", | |
| 44 ["Library"] = "Bibliotheken", | |
| 45 ["Map"] = "Karte", | |
| 46 ["Mail"] = "Post", | |
| 47 ["Miscellaneous"] = "Diverses", | |
| 48 ["Quest"] = "Quest", | |
| 49 ["Raid"] = "Schlachtzug", | |
| 50 ["Tradeskill"] = "Beruf", | |
| 51 ["UnitFrame"] = "Einheiten-Fenster", | |
| 52 } | |
| 53 elseif GetLocale() == "frFR" then | |
| 54 CATEGORIES = { | |
| 55 ["Action Bars"] = "Barres d'action", | |
| 56 ["Auction"] = "Hôtel des ventes", | |
| 57 ["Audio"] = "Audio", | |
| 58 ["Battlegrounds/PvP"] = "Champs de bataille/JcJ", | |
| 59 ["Buffs"] = "Buffs", | |
| 60 ["Chat/Communication"] = "Chat/Communication", | |
| 61 ["Druid"] = "Druide", | |
| 62 ["Hunter"] = "Chasseur", | |
| 63 ["Mage"] = "Mage", | |
| 64 ["Paladin"] = "Paladin", | |
| 65 ["Priest"] = "Prêtre", | |
| 66 ["Rogue"] = "Voleur", | |
| 67 ["Shaman"] = "Chaman", | |
| 68 ["Warlock"] = "Démoniste", | |
| 69 ["Warrior"] = "Guerrier", | |
| 70 ["Healer"] = "Soigneur", | |
| 71 ["Tank"] = "Tank", | |
| 72 ["Caster"] = "Casteur", | |
| 73 ["Combat"] = "Combat", | |
| 74 ["Compilations"] = "Compilations", | |
| 75 ["Data Export"] = "Exportation de données", | |
| 76 ["Development Tools"] = "Outils de développement", | |
| 77 ["Guild"] = "Guilde", | |
| 78 ["Frame Modification"] = "Modification des fenêtres", | |
| 79 ["Interface Enhancements"] = "Améliorations de l'interface", | |
| 80 ["Inventory"] = "Inventaire", | |
| 81 ["Library"] = "Bibliothèques", | |
| 82 ["Map"] = "Carte", | |
| 83 ["Mail"] = "Courrier", | |
| 84 ["Miscellaneous"] = "Divers", | |
| 85 ["Quest"] = "Quêtes", | |
| 86 ["Raid"] = "Raid", | |
| 87 ["Tradeskill"] = "Métiers", | |
| 88 ["UnitFrame"] = "Fenêtres d'unité", | |
| 89 } | |
| 90 elseif GetLocale() == "koKR" then | |
| 91 CATEGORIES = { | |
| 92 ["Action Bars"] = "액션바", | |
| 93 ["Auction"] = "경매", | |
| 94 ["Audio"] = "음향", | |
| 95 ["Battlegrounds/PvP"] = "전장/PvP", | |
| 96 ["Buffs"] = "버프", | |
| 97 ["Chat/Communication"] = "대화/의사소통", | |
| 98 ["Druid"] = "드루이드", | |
| 99 ["Hunter"] = "사냥꾼", | |
| 100 ["Mage"] = "마법사", | |
| 101 ["Paladin"] = "성기사", | |
| 102 ["Priest"] = "사제", | |
| 103 ["Rogue"] = "도적", | |
| 104 ["Shaman"] = "주술사", | |
| 105 ["Warlock"] = "흑마법사", | |
| 106 ["Warrior"] = "전사", | |
| 107 ["Healer"] = "힐러", | |
| 108 ["Tank"] = "탱커", | |
| 109 ["Caster"] = "캐스터", | |
| 110 ["Combat"] = "전투", | |
| 111 ["Compilations"] = "복합", | |
| 112 ["Data Export"] = "자료 출력", | |
| 113 ["Development Tools"] = "개발 도구", | |
| 114 ["Guild"] = "길드", | |
| 115 ["Frame Modification"] = "구조 변경", | |
| 116 ["Interface Enhancements"] = "인터페이스 강화", | |
| 117 ["Inventory"] = "인벤토리", | |
| 118 ["Library"] = "라이브러리", | |
| 119 ["Map"] = "지도", | |
| 120 ["Mail"] = "우편", | |
| 121 ["Miscellaneous"] = "기타", | |
| 122 ["Quest"] = "퀘스트", | |
| 123 ["Raid"] = "공격대", | |
| 124 ["Tradeskill"] = "전문기술", | |
| 125 ["UnitFrame"] = "유닛 프레임", | |
| 126 } | |
| 127 elseif GetLocale() == "zhTW" then | |
| 128 CATEGORIES = { | |
| 129 ["Action Bars"] = "動作列", | |
| 130 ["Auction"] = "拍賣", | |
| 131 ["Audio"] = "音效", | |
| 132 ["Battlegrounds/PvP"] = "戰場/PvP", | |
| 133 ["Buffs"] = "增益", | |
| 134 ["Chat/Communication"] = "聊天/通訊", | |
| 135 ["Druid"] = "德魯伊", | |
| 136 ["Hunter"] = "獵人", | |
| 137 ["Mage"] = "法師", | |
| 138 ["Paladin"] = "聖騎士", | |
| 139 ["Priest"] = "牧師", | |
| 140 ["Rogue"] = "盜賊", | |
| 141 ["Shaman"] = "薩滿", | |
| 142 ["Warlock"] = "術士", | |
| 143 ["Warrior"] = "戰士", | |
| 144 ["Healer"] = "治療者", | |
| 145 ["Tank"] = "坦克", | |
| 146 ["Caster"] = "施法者", | |
| 147 ["Combat"] = "戰鬥", | |
| 148 ["Compilations"] = "整合", | |
| 149 ["Data Export"] = "資料匯出", | |
| 150 ["Development Tools"] = "開發工具", | |
| 151 ["Guild"] = "公會", | |
| 152 ["Frame Modification"] = "框架修改", | |
| 153 ["Interface Enhancements"] = "介面增強", | |
| 154 ["Inventory"] = "庫存", | |
| 155 ["Library"] = "程式庫", | |
| 156 ["Map"] = "地圖", | |
| 157 ["Mail"] = "郵件", | |
| 158 ["Miscellaneous"] = "雜項", | |
| 159 ["Quest"] = "任務", | |
| 160 ["Raid"] = "團隊", | |
| 161 ["Tradeskill"] = "交易技能", | |
| 162 ["UnitFrame"] = "頭像框架", | |
| 163 } | |
| 164 elseif GetLocale() == "zhCN" then | |
| 165 CATEGORIES = { | |
| 166 ["Action Bars"] = "动作条", | |
| 167 ["Auction"] = "拍卖", | |
| 168 ["Audio"] = "音频", | |
| 169 ["Battlegrounds/PvP"] = "战场/PvP", | |
| 170 ["Buffs"] = "增益魔法", | |
| 171 ["Chat/Communication"] = "聊天/交流", | |
| 172 ["Druid"] = "德鲁伊", | |
| 173 ["Hunter"] = "猎人", | |
| 174 ["Mage"] = "法师", | |
| 175 ["Paladin"] = "圣骑士", | |
| 176 ["Priest"] = "牧师", | |
| 177 ["Rogue"] = "潜行者", | |
| 178 ["Shaman"] = "萨满祭司", | |
| 179 ["Warlock"] = "术士", | |
| 180 ["Warrior"] = "战士", | |
| 181 ["Healer"] = "治疗", | |
| 182 ["Tank"] = "坦克", | |
| 183 ["Caster"] = "远程输出", | |
| 184 ["Combat"] = "战斗", | |
| 185 ["Compilations"] = "编译", | |
| 186 ["Data Export"] = "数据导出", | |
| 187 ["Development Tools"] = "开发工具", | |
| 188 ["Guild"] = "公会", | |
| 189 ["Frame Modification"] = "框架修改", | |
| 190 ["Interface Enhancements"] = "界面增强", | |
| 191 ["Inventory"] = "背包", | |
| 192 ["Library"] = "库", | |
| 193 ["Map"] = "地图", | |
| 194 ["Mail"] = "邮件", | |
| 195 ["Miscellaneous"] = "杂项", | |
| 196 ["Quest"] = "任务", | |
| 197 ["Raid"] = "团队", | |
| 198 ["Tradeskill"] = "商业技能", | |
| 199 ["UnitFrame"] = "头像框架", | |
| 200 } | |
| 201 elseif GetLocale() == "esES" then | |
| 202 CATEGORIES = { | |
| 203 ["Action Bars"] = "Barras de Acción", | |
| 204 ["Auction"] = "Subasta", | |
| 205 ["Audio"] = "Audio", | |
| 206 ["Battlegrounds/PvP"] = "Campos de Batalla/JcJ", | |
| 207 ["Buffs"] = "Buffs", | |
| 208 ["Chat/Communication"] = "Chat/Comunicación", | |
| 209 ["Druid"] = "Druida", | |
| 210 ["Hunter"] = "Cazador", | |
| 211 ["Mage"] = "Mago", | |
| 212 ["Paladin"] = "Paladín", | |
| 213 ["Priest"] = "Sacerdote", | |
| 214 ["Rogue"] = "Pícaro", | |
| 215 ["Shaman"] = "Chamán", | |
| 216 ["Warlock"] = "Brujo", | |
| 217 ["Warrior"] = "Guerrero", | |
| 218 ["Healer"] = "Sanador", | |
| 219 ["Tank"] = "Tanque", | |
| 220 ["Caster"] = "Conjurador", | |
| 221 ["Combat"] = "Combate", | |
| 222 ["Compilations"] = "Compilaciones", | |
| 223 ["Data Export"] = "Exportar Datos", | |
| 224 ["Development Tools"] = "Herramientas de Desarrollo", | |
| 225 ["Guild"] = "Hermandad", | |
| 226 ["Frame Modification"] = "Modificación de Marcos", | |
| 227 ["Interface Enhancements"] = "Mejoras de la Interfaz", | |
| 228 ["Inventory"] = "Inventario", | |
| 229 ["Library"] = "Biblioteca", | |
| 230 ["Map"] = "Mapa", | |
| 231 ["Mail"] = "Correo", | |
| 232 ["Miscellaneous"] = "Misceláneo", | |
| 233 ["Quest"] = "Misión", | |
| 234 ["Raid"] = "Banda", | |
| 235 ["Tradeskill"] = "Habilidad de Comercio", | |
| 236 ["UnitFrame"] = "Marco de Unidades", | |
| 237 } | |
| 238 else -- enUS | |
| 239 CATEGORIES = { | |
| 240 ["Action Bars"] = "Action Bars", | |
| 241 ["Auction"] = "Auction", | |
| 242 ["Audio"] = "Audio", | |
| 243 ["Battlegrounds/PvP"] = "Battlegrounds/PvP", | |
| 244 ["Buffs"] = "Buffs", | |
| 245 ["Chat/Communication"] = "Chat/Communication", | |
| 246 ["Druid"] = "Druid", | |
| 247 ["Hunter"] = "Hunter", | |
| 248 ["Mage"] = "Mage", | |
| 249 ["Paladin"] = "Paladin", | |
| 250 ["Priest"] = "Priest", | |
| 251 ["Rogue"] = "Rogue", | |
| 252 ["Shaman"] = "Shaman", | |
| 253 ["Warlock"] = "Warlock", | |
| 254 ["Warrior"] = "Warrior", | |
| 255 ["Healer"] = "Healer", | |
| 256 ["Tank"] = "Tank", | |
| 257 ["Caster"] = "Caster", | |
| 258 ["Combat"] = "Combat", | |
| 259 ["Compilations"] = "Compilations", | |
| 260 ["Data Export"] = "Data Export", | |
| 261 ["Development Tools"] = "Development Tools", | |
| 262 ["Guild"] = "Guild", | |
| 263 ["Frame Modification"] = "Frame Modification", | |
| 264 ["Interface Enhancements"] = "Interface Enhancements", | |
| 265 ["Inventory"] = "Inventory", | |
| 266 ["Library"] = "Library", | |
| 267 ["Map"] = "Map", | |
| 268 ["Mail"] = "Mail", | |
| 269 ["Miscellaneous"] = "Miscellaneous", | |
| 270 ["Quest"] = "Quest", | |
| 271 ["Raid"] = "Raid", | |
| 272 ["Tradeskill"] = "Tradeskill", | |
| 273 ["UnitFrame"] = "UnitFrame", | |
| 274 } | |
| 275 end | |
| 276 | |
| 277 local select = _G.select | |
| 278 local tostring = _G.tostring | |
| 279 local pairs = _G.pairs | |
| 280 local ipairs = _G.ipairs | |
| 281 local error = _G.error | |
| 282 local setmetatable = _G.setmetatable | |
| 283 local getmetatable = _G.getmetatable | |
| 284 local type = _G.type | |
| 285 local pcall = _G.pcall | |
| 286 local next = _G.next | |
| 287 local tonumber = _G.tonumber | |
| 288 local strmatch = _G.strmatch | |
| 289 local table_remove = _G.table.remove | |
| 290 local debugstack = _G.debugstack | |
| 291 local LoadAddOn = _G.LoadAddOn | |
| 292 local GetAddOnInfo = _G.GetAddOnInfo | |
| 293 local GetAddOnMetadata = _G.GetAddOnMetadata | |
| 294 local GetNumAddOns = _G.GetNumAddOns | |
| 295 local DisableAddOn = _G.DisableAddOn | |
| 296 local EnableAddOn = _G.EnableAddOn | |
| 297 local IsAddOnLoadOnDemand = _G.IsAddOnLoadOnDemand | |
| 298 local IsLoggedIn = _G.IsLoggedIn | |
| 299 local geterrorhandler = _G.geterrorhandler | |
| 300 local assert = _G.assert | |
| 301 local collectgarbage = _G.collectgarbage | |
| 302 local table_sort = _G.table.sort | |
| 303 local table_concat = _G.table.concat | |
| 304 | |
| 305 -- #AUTODOC_NAMESPACE Rock | |
| 306 | |
| 307 | |
| 308 local LibStub = _G.LibStub | |
| 309 | |
| 310 local Rock = LibStub:GetLibrary(MAJOR_VERSION, true) or _G.Rock | |
| 311 local oldRock | |
| 312 if not Rock then | |
| 313 Rock = LibStub:NewLibrary(MAJOR_VERSION, MINOR_VERSION) | |
| 314 if not Rock then | |
| 315 return | |
| 316 end | |
| 317 Rock.name = MAJOR_VERSION | |
| 318 else | |
| 319 Rock, oldRock = Rock:NewLibrary(MAJOR_VERSION, MINOR_VERSION) | |
| 320 if not Rock then | |
| 321 return | |
| 322 end | |
| 323 end | |
| 324 _G.Rock = Rock | |
| 325 | |
| 326 local L = setmetatable({}, {__index=function(self,key) self[key] = key; return key end}) | |
| 327 if GetLocale() == "zhCN" then | |
| 328 L["Advanced options"] = "高级选项" | |
| 329 L["Advanced options for developers and power users."] = "开发者与高级用户的高级选项" | |
| 330 L["Unit tests"] = "框体测试" | |
| 331 L["Enable unit tests to be run. This is for developers only.\n\nYou must ReloadUI for changes to take effect."] = "开启框体测试,仅供开发者使用。\n\n需要重载用户界面。" | |
| 332 L["Contracts"] = "侦错协定" | |
| 333 L["Enable contracts to be run. This is for developers and anyone wanting to file a bug. Do not file a bug unless contracts are enabled. This will slightly slow down your addons if enabled."] = "启用侦错协定,这是给插件作者用来通报错误所使用。" | |
| 334 L["Reload UI"] = "重载UI" | |
| 335 L["Reload the User Interface for some changes to take effect."] = "部分功能更改需要重载用户界面才会生效。" | |
| 336 L["Reload"] = "重载" | |
| 337 L["Give donation"] = "捐赠" | |
| 338 L["Donate"] = "捐赠" | |
| 339 L["Give a much-needed donation to the author of this addon."] = "给插件作者捐赠支持插件开发。" | |
| 340 L["File issue"] = "通报错误" | |
| 341 L["Report"] = "报告" | |
| 342 L["File a bug or request a new feature or an improvement to this addon."] = "发送错误报告或请求新功能及要改进的部分。" | |
| 343 L["Press Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar."] = "Ctrl-C复制网址,Alt-Tab切换到桌面,打开浏览器,在地址栏贴上网址。" | |
| 344 L["Press Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar."] = "Cmd-C复制网址,Cmd-Tab切换到电脑桌面,打开浏览器,在地址栏贴上网址。" | |
| 345 L["Enabled"] = "开启" | |
| 346 L["Enable or disable this addon."] = "启用这个插件。" | |
| 347 | |
| 348 elseif GetLocale() == "zhTW" then | |
| 349 L["Advanced options"] = "進階選項" | |
| 350 L["Advanced options for developers and power users."] = "插件作者、進階用戶選項" | |
| 351 L["Unit tests"] = "單元測試" | |
| 352 L["Enable unit tests to be run. This is for developers only.\n\nYou must ReloadUI for changes to take effect."] = "啟用單元測試,這是給插件作者使用的功能。\n\n需要重載介面才能使用。" | |
| 353 L["Contracts"] = "偵錯協定" | |
| 354 L["Enable contracts to be run. This is for developers and anyone wanting to file a bug. Do not file a bug unless contracts are enabled. This will slightly slow down your addons if enabled."] = "啟用偵錯協定,這是給插件作者用來通報錯誤所使用。" | |
| 355 L["Reload UI"] = "重載介面" | |
| 356 L["Reload the User Interface for some changes to take effect."] = "重新載入使用者介面,部分功能才會生效。" | |
| 357 L["Reload"] = "重載" | |
| 358 L["Give donation"] = "捐贈" | |
| 359 L["Donate"] = "捐贈" | |
| 360 L["Give a much-needed donation to the author of this addon."] = "捐贈金錢給插件作者。" | |
| 361 L["File issue"] = "通報錯誤" | |
| 362 L["Report"] = "報告" | |
| 363 L["File a bug or request a new feature or an improvement to this addon."] = "發出錯誤報告或請求新功能及要改進的部分。" | |
| 364 L["Press Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar."] = "Ctrl-C複製網址,Alt-Tab切換到電腦桌面,打開瀏覽器,在網址列貼上網址。" | |
| 365 L["Press Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar."] = "Cmd-C複製網址,Cmd-Tab切換到電腦桌面,打開瀏覽器,在網址列貼上網址。" | |
| 366 L["Enabled"] = "啟用" | |
| 367 L["Enable or disable this addon."] = "啟用這個插件。" | |
| 368 elseif GetLocale() == "koKR" then | |
| 369 L["Advanced options"] = "상세 옵션" | |
| 370 L["Advanced options for developers and power users."] = "개발자와 파워 사용자를 위한 상세 옵션입니다." | |
| 371 L["Unit tests"] = "유닛 테스트" | |
| 372 L["Enable unit tests to be run. This is for developers only.\n\nYou must ReloadUI for changes to take effect."] = "유닛 테스트를 사용합니다. 이것은 개발자만을 위한 옵션입니다.\n\n변경된 결과를 적용하기 위해 당신의 UI를 재실행 합니다." | |
| 373 L["Contracts"] = "계약" | |
| 374 L["Enable contracts to be run. This is for developers and anyone wanting to file a bug. Do not file a bug unless contracts are enabled. This will slightly slow down your addons if enabled."] = "계약을 사용합니다. 이것은 개발자와 버그 파일을 알릴 분이면 누구나 사용 가능합니다. 계약이 가능하지 않으면 버그 파일을 보내지 마십시오. 이것은 당신의 애드온 속도를 약간 떨어뜨립니다." | |
| 375 L["Reload UI"] = "UI 재실행" | |
| 376 L["Reload the User Interface for some changes to take effect."] = "변경된 결과를 적용하기 위해 사용자 인터페이스를 재실행합니다." | |
| 377 L["Reload"] = "재실행" | |
| 378 L["Give donation"] = "기부" | |
| 379 L["Donate"] = "기부" | |
| 380 L["Give a much-needed donation to the author of this addon."] = "이 애드온의 제작자에게 필요한 기부를 합니다." | |
| 381 L["File issue"] = "파일 이슈" | |
| 382 L["Report"] = "보고" | |
| 383 L["File a bug or request a new feature or an improvement to this addon."] = "버그 파일을 알리거나 새로운 기능 또는 이 애드온에 대한 개선을 부탁합니다." | |
| 384 L["Press Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar."] = "Ctrl-C로 복사합니다. Alt-Tab 눌려 게임으로 부터 나간후 웹 브라우저를 엽니다. 복사된 링크를 주소 창에 붙여넣기 합니다." | |
| 385 L["Press Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar."] = "Cmd-C로 복사합니다. Cmd-Tab 눌려 게임으로 부터 나간후 웹 브라우저를 엽니다. 복사된 링크를 주소 창에 붙여넣기 합니다." | |
| 386 L["Enabled"] = "사용" | |
| 387 L["Enable or disable this addon."] = "이 애드온을 사용하거나 사용하지 않습니다." | |
| 388 elseif GetLocale() == "frFR" then | |
| 389 L["Advanced options"] = "Options avancées" | |
| 390 L["Advanced options for developers and power users."] = "Options avancées à l'attention des développeurs et des utilisateurs expérimentés." | |
| 391 L["Reload UI"] = "Recharger IU" | |
| 392 L["Reload the User Interface for some changes to take effect."] = "Recharge l'interface utilisateur afin que certains changements prennent effet." | |
| 393 L["Reload"] = "Recharger" | |
| 394 L["Give donation"] = "Faire un don" | |
| 395 L["Donate"] = "Don" | |
| 396 L["Give a much-needed donation to the author of this addon."] = "Permet de faire un don bien mérité à l'auteur de cet addon." | |
| 397 L["File issue"] = "Problème" | |
| 398 L["Report"] = "Signaler" | |
| 399 L["File a bug or request a new feature or an improvement to this addon."] = "Permet de signaler un bogue ou de demander une amélioration à cet addon." | |
| 400 L["Press Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar."] = "Ctrl-C pour copier, puis Alt-Tab pour sortir du jeu. Ouvrez votre navigateur internet et collez le lien dans la barre d'adresse." | |
| 401 L["Press Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar."] = "Cmd-C pour copier, puis Alt-Tab pour sortir du jeu. Ouvrez votre navigateur internet et collez le lien dans la barre d'adresse." | |
| 402 L["Enabled"] = "Activé" | |
| 403 L["Enable or disable this addon."] = "Active ou désactive cet addon." | |
| 404 end | |
| 405 | |
| 406 local isStandalone = debugstack():match("[Oo%.][Nn%.][Ss%.]\\([^\\]+)\\") == MAJOR_VERSION or nil | |
| 407 local unitTestDB, enableContracts | |
| 408 | |
| 409 local weakKey = { __mode = 'k' } | |
| 410 | |
| 411 -- frame to manage events from | |
| 412 Rock.frame = oldRock and oldRock.frame or _G.CreateFrame("Frame") | |
| 413 local frame = Rock.frame | |
| 414 -- dict of libraries in { ["major"] = object } form | |
| 415 Rock.libraries = oldRock and oldRock.libraries or { [MAJOR_VERSION] = Rock } | |
| 416 local libraries = Rock.libraries | |
| 417 -- set of libraries which have gone through the finalization process in { [object] = true } form | |
| 418 Rock.finalizedLibraries = setmetatable(oldRock and oldRock.finalizedLibraries or { }, weakKey) | |
| 419 local finalizedLibraries = Rock.finalizedLibraries | |
| 420 -- set of libraries which have been tried to be loaded. | |
| 421 Rock.scannedLibraries = oldRock and oldRock.scannedLibraries or {} | |
| 422 local scannedLibraries = Rock.scannedLibraries | |
| 423 -- exportedMethods[library] = { "method1", "method2" } | |
| 424 Rock.exportedMethods = setmetatable(oldRock and oldRock.exportedMethods or {}, weakKey) | |
| 425 local exportedMethods = Rock.exportedMethods | |
| 426 -- mixinToObject[mixin][object] = true | |
| 427 Rock.mixinToObject = setmetatable(oldRock and oldRock.mixinToObject or {}, weakKey) | |
| 428 local mixinToObject = Rock.mixinToObject | |
| 429 -- dict of addons in { ["name"] = object } form | |
| 430 Rock.addons = oldRock and oldRock.addons or {} | |
| 431 local addons = Rock.addons | |
| 432 -- set of libraries that should be finalized before ADDON_LOADED. | |
| 433 Rock.pendingLibraries = setmetatable(oldRock and oldRock.pendingLibraries or { }, weakKey) | |
| 434 local pendingLibraries = Rock.pendingLibraries | |
| 435 -- list of addons in order of created that need to be initialized by ADDON_LOADED. | |
| 436 Rock.pendingAddons = oldRock and oldRock.pendingAddons or {} | |
| 437 local pendingAddons = Rock.pendingAddons | |
| 438 -- dictionary of addons to their folder names | |
| 439 Rock.addonToFolder = oldRock and oldRock.addonToFolder or {} | |
| 440 local addonToFolder = Rock.addonToFolder | |
| 441 -- set of folders which have been loaded | |
| 442 Rock.foldersLoaded = oldRock and oldRock.foldersLoaded or {} | |
| 443 local foldersLoaded = Rock.foldersLoaded | |
| 444 -- list of addons in order of created that need to be enabled by PLAYER_LOGIN. | |
| 445 Rock.pendingAddonsEnable = oldRock and oldRock.pendingAddonsEnable or {} | |
| 446 local pendingAddonsEnable = Rock.pendingAddonsEnable | |
| 447 -- set of addons which have been enabled at least once. | |
| 448 Rock.addonsAlreadyEnabled = oldRock and oldRock.addonsAlreadyEnabled or {} | |
| 449 local addonsAlreadyEnabled = Rock.addonsAlreadyEnabled | |
| 450 -- set of addons which have no database and are set to be inactive. | |
| 451 Rock.inactiveAddons = oldRock and oldRock.inactiveAddons or {} | |
| 452 local inactiveAddons = Rock.inactiveAddons | |
| 453 -- set of addons which are currently enabled (not necessarily should be) | |
| 454 Rock.currentlyEnabledAddons = oldRock and oldRock.currentlyEnabledAddons or {} | |
| 455 local currentlyEnabledAddons = Rock.currentlyEnabledAddons | |
| 456 -- dictionary of namespace to list of functions which will be run. | |
| 457 Rock.unitTests = oldRock and oldRock.unitTests or {} | |
| 458 local unitTests = Rock.unitTests | |
| 459 -- metatable for addons | |
| 460 Rock.addon_mt = oldRock and oldRock.addon_mt or {} | |
| 461 local addon_mt = Rock.addon_mt | |
| 462 for k in pairs(addon_mt) do | |
| 463 addon_mt[k] = nil | |
| 464 end | |
| 465 function addon_mt:__tostring() | |
| 466 return tostring(self.name) | |
| 467 end | |
| 468 | |
| 469 local function better_tostring(self) | |
| 470 if type(self) == "table" and self.name then | |
| 471 return tostring(self.name) | |
| 472 end | |
| 473 return tostring(self) | |
| 474 end | |
| 475 | |
| 476 local function figureCurrentAddon(pos) | |
| 477 local stack = debugstack(pos+1, 1, 0) | |
| 478 local folder = stack:match("[Oo%.][Nn%.][Ss%.]\\([^\\]+)\\") | |
| 479 if folder then | |
| 480 return folder | |
| 481 end | |
| 482 | |
| 483 local partFolder = stack:match("...([^\\]+)\\") | |
| 484 if partFolder then | |
| 485 local partFolder_len = #partFolder | |
| 486 for i = 1, GetNumAddOns() do | |
| 487 local name = GetAddOnInfo(i) | |
| 488 if #name >= partFolder_len then | |
| 489 local partName = name:sub(-partFolder_len) | |
| 490 if partName == partFolder then | |
| 491 return name | |
| 492 end | |
| 493 end | |
| 494 end | |
| 495 end | |
| 496 return nil | |
| 497 end | |
| 498 | |
| 499 --[[--------------------------------------------------------------------------- | |
| 500 Returns: | |
| 501 string - the localized name of the given category. | |
| 502 Arguments: | |
| 503 string - the English name of the category. | |
| 504 Example: | |
| 505 local uf = Rock:GetLocalizedCategory("UnitFrame") | |
| 506 -----------------------------------------------------------------------------]] | |
| 507 function Rock:GetLocalizedCategory(name) | |
| 508 if type(name) ~= "string" then | |
| 509 error(("Bad argument #2 to `GetLocalizedCategory'. Expected %q, got %q."):format("string", type(name)), 2) | |
| 510 end | |
| 511 local cat = CATEGORIES[name] | |
| 512 if cat then | |
| 513 return cat | |
| 514 end | |
| 515 local name_lower = name:lower() | |
| 516 for k in pairs(CATEGORIES) do | |
| 517 if k:lower() == name_lower then | |
| 518 return k | |
| 519 end | |
| 520 end | |
| 521 return _G.UNKNOWN or "Unknown" | |
| 522 end | |
| 523 | |
| 524 local weak = {__mode = 'kv'} | |
| 525 | |
| 526 Rock.recycleData = oldRock and oldRock.recycleData or {} | |
| 527 local recycleData = Rock.recycleData | |
| 528 if recycleData.pools then | |
| 529 setmetatable(recycleData.pools, weak) | |
| 530 end | |
| 531 if recycleData.debugPools then | |
| 532 setmetatable(recycleData.debugPools, weak) | |
| 533 end | |
| 534 if recycleData.newList then | |
| 535 setmetatable(recycleData.newList, weak) | |
| 536 end | |
| 537 if recycleData.newDict then | |
| 538 setmetatable(recycleData.newDict, weak) | |
| 539 end | |
| 540 if recycleData.newSet then | |
| 541 setmetatable(recycleData.newSet, weak) | |
| 542 end | |
| 543 if recycleData.del then | |
| 544 setmetatable(recycleData.del, weak) | |
| 545 end | |
| 546 | |
| 547 local tmp = {} | |
| 548 local function myUnpack(t, start) | |
| 549 if not start then | |
| 550 start = 1 | |
| 551 end | |
| 552 local value = t[start] | |
| 553 if value == nil then | |
| 554 return | |
| 555 end | |
| 556 t[start] = nil | |
| 557 return value, myUnpack(t, start+1) | |
| 558 end | |
| 559 | |
| 560 --[[--------------------------------------------------------------------------- | |
| 561 Notes: | |
| 562 * Returns functions for the specified namespace based on what is provided. | |
| 563 * function types: | |
| 564 ; "newList" : to create a list | |
| 565 ; "newDict" : to create a dictionary | |
| 566 ; "newSet" : to create a set | |
| 567 ; "del" : to delete a table | |
| 568 ; "unpackListAndDel" : deletes a table and returns what its contents were as a list, in order. | |
| 569 ; "unpackSetAndDel" : deletes a table and returns what its contents were as a set, in no particular order. | |
| 570 ; "unpackDictAndDel" : deletes a table and returns what its contents were as a dictionary, in no particular order. | |
| 571 * If you provide "Debug" as the last argument, then the namespace can be debugged with ''':DebugRecycle''' | |
| 572 * It is '''not recommended''' to use table recycling with tables that have more than 128 keys, as it is typically faster to let lua's garbage collector handle it. | |
| 573 Arguments: | |
| 574 string - the namespace. ''Note: this doesn't necessarily have to be a string.'' | |
| 575 Example: | |
| 576 local newList, newDict, newSet, del, unpackListAndDel, unpackSetAndDel, unpackDictAndDel = Rock:GetRecyclingFunctions("MyNamespace", "newList", "newDict", "newSet", "del", "unpackListAndDel", "unpackSetAndDel", "unpackDictAndDel") | |
| 577 | |
| 578 local t = newList('alpha', 'bravo') -- same as t = {'alpha', 'bravo'} | |
| 579 local u = newDict('alpha', 'bravo') -- same as t = {['alpha'] = 'bravo'} | |
| 580 local v = newSet('alpha', 'bravo') -- same as t = {['alpha'] = true, ['bravo'] = true} | |
| 581 t = del(t) -- you want to clear your reference as well as deleting. | |
| 582 u = del(u) | |
| 583 v = del(v) | |
| 584 | |
| 585 -- for debugging | |
| 586 local newList = Rock:GetRecyclingFunctions("MyNamespace", "newList", "Debug") | |
| 587 local t = newList() | |
| 588 Rock:DebugRecycle("MyNamespace") | |
| 589 t = del(t) | |
| 590 | |
| 591 -- unpacking functions | |
| 592 unpackListAndDel(newList(...)) => ... | |
| 593 unpackSetAndDel(newSet(...)) => ... | |
| 594 unpackDictAndDel(newDict(...)) => ... | |
| 595 newList(unpackListAndDel(t)) => t | |
| 596 newSet(unpackSetAndDel(t)) => t | |
| 597 newDict(unpackDictAndDel(t)) => t | |
| 598 -- as you can see, they are inverses of each other. | |
| 599 -----------------------------------------------------------------------------]] | |
| 600 function Rock:GetRecyclingFunctions(namespace, ...) | |
| 601 local pools = recycleData.pools | |
| 602 if not pools then | |
| 603 pools = setmetatable({}, weak) | |
| 604 recycleData.pools = pools | |
| 605 end | |
| 606 if namespace == "newList" or namespace == "newSet" or namespace == "newDict" or namespace == "del" or namespace == "unpackListAndDel" or namespace == "unpackSetAndDel" or namespace == "unpackDictAndDel" then | |
| 607 error(("Bad argument #2 to `GetRecyclingFunctions'. Cannot be %q"):format(namespace), 2) | |
| 608 end | |
| 609 local pool = pools[namespace] | |
| 610 if not pool then | |
| 611 pool = setmetatable({}, weak) | |
| 612 pools[namespace] = pool | |
| 613 end | |
| 614 local n = select('#', ...) | |
| 615 local debug = select(n, ...) == "Debug" | |
| 616 if debug then | |
| 617 n = n - 1 | |
| 618 local debugPools = recycleData.debugPools | |
| 619 if not debugPools then | |
| 620 debugPools = setmetatable({}, weak) | |
| 621 recycleData.debugPools = debugPools | |
| 622 end | |
| 623 debug = debugPools[namespace] | |
| 624 if not debug then | |
| 625 debug = { num = 0 } | |
| 626 debugPools[namespace] = debug | |
| 627 end | |
| 628 elseif recycleData.debugPools and recycleData.debugPools[namespace] then | |
| 629 debug = recycleData.debugPools[namespace] | |
| 630 end | |
| 631 for i = 1, n do | |
| 632 local func = select(i, ...) | |
| 633 local recycleData_func = recycleData[func] | |
| 634 if not recycleData_func then | |
| 635 recycleData_func = setmetatable({}, weak) | |
| 636 recycleData[func] = recycleData_func | |
| 637 end | |
| 638 if func == "newList" then | |
| 639 local newList = recycleData_func[namespace] | |
| 640 if not newList then | |
| 641 function newList(...) | |
| 642 local t = next(pool) | |
| 643 local n = select('#', ...) | |
| 644 if t then | |
| 645 pool[t] = nil | |
| 646 for i = 1, n do | |
| 647 t[i] = select(i, ...) | |
| 648 end | |
| 649 else | |
| 650 t = { ... } | |
| 651 end | |
| 652 | |
| 653 if debug then | |
| 654 debug[t] = debugstack(2) | |
| 655 debug.num = debug.num + 1 | |
| 656 end | |
| 657 | |
| 658 return t, n | |
| 659 end | |
| 660 recycleData_func[namespace] = newList | |
| 661 end | |
| 662 tmp[i] = newList | |
| 663 elseif func == "newDict" then | |
| 664 local newDict = recycleData_func[namespace] | |
| 665 if not newDict then | |
| 666 function newDict(...) | |
| 667 local t = next(pool) | |
| 668 if t then | |
| 669 pool[t] = nil | |
| 670 else | |
| 671 t = {} | |
| 672 end | |
| 673 | |
| 674 for i = 1, select('#', ...), 2 do | |
| 675 t[select(i, ...)] = select(i+1, ...) | |
| 676 end | |
| 677 | |
| 678 if debug then | |
| 679 debug[t] = debugstack(2) | |
| 680 debug.num = debug.num + 1 | |
| 681 end | |
| 682 | |
| 683 return t | |
| 684 end | |
| 685 recycleData_func[namespace] = newDict | |
| 686 end | |
| 687 tmp[i] = newDict | |
| 688 elseif func == "newSet" then | |
| 689 local newSet = recycleData_func[namespace] | |
| 690 if not newSet then | |
| 691 function newSet(...) | |
| 692 local t = next(pool) | |
| 693 if t then | |
| 694 pool[t] = nil | |
| 695 else | |
| 696 t = {} | |
| 697 end | |
| 698 | |
| 699 for i = 1, select('#', ...) do | |
| 700 t[select(i, ...)] = true | |
| 701 end | |
| 702 | |
| 703 if debug then | |
| 704 debug[t] = debugstack(2) | |
| 705 debug.num = debug.num + 1 | |
| 706 end | |
| 707 | |
| 708 return t | |
| 709 end | |
| 710 recycleData_func[namespace] = newSet | |
| 711 end | |
| 712 tmp[i] = newSet | |
| 713 elseif func == "del" then | |
| 714 local del = recycleData_func[namespace] | |
| 715 if not del then | |
| 716 function del(t) | |
| 717 if not t then | |
| 718 error(("Bad argument #1 to `del'. Expected %q, got %q."):format("table", type(t)), 2) | |
| 719 end | |
| 720 if pool[t] then | |
| 721 local _, ret = pcall(error, "Error, double-free syndrome.", 3) | |
| 722 geterrorhandler()(ret) | |
| 723 end | |
| 724 setmetatable(t, nil) | |
| 725 for k in pairs(t) do | |
| 726 t[k] = nil | |
| 727 end | |
| 728 t[true] = true | |
| 729 t[true] = nil | |
| 730 pool[t] = true | |
| 731 | |
| 732 if debug then | |
| 733 debug[t] = nil | |
| 734 debug.num = debug.num - 1 | |
| 735 end | |
| 736 return nil | |
| 737 end | |
| 738 recycleData_func[namespace] = del | |
| 739 end | |
| 740 tmp[i] = del | |
| 741 elseif func == "unpackListAndDel" then | |
| 742 local unpackListAndDel = recycleData_func[namespace] | |
| 743 if not unpackListAndDel then | |
| 744 local function f(t, start, finish) | |
| 745 if start > finish then | |
| 746 for k in pairs(t) do | |
| 747 t[k] = nil | |
| 748 end | |
| 749 t[true] = true | |
| 750 t[true] = nil | |
| 751 pool[t] = true | |
| 752 return | |
| 753 end | |
| 754 return t[start], f(t, start+1, finish) | |
| 755 end | |
| 756 function unpackListAndDel(t, start, finish) | |
| 757 if not t then | |
| 758 error(("Bad argument #1 to `unpackListAndDel'. Expected %q, got %q."):format("table", type(t)), 2) | |
| 759 end | |
| 760 if not start then | |
| 761 start = 1 | |
| 762 end | |
| 763 if not finish then | |
| 764 finish = #t | |
| 765 end | |
| 766 setmetatable(t, nil) | |
| 767 if debug then | |
| 768 debug[t] = nil | |
| 769 debug.num = debug.num - 1 | |
| 770 end | |
| 771 return f(t, start, finish) | |
| 772 end | |
| 773 end | |
| 774 tmp[i] = unpackListAndDel | |
| 775 elseif func == "unpackSetAndDel" then | |
| 776 local unpackSetAndDel = recycleData_func[namespace] | |
| 777 if not unpackSetAndDel then | |
| 778 local function f(t, current) | |
| 779 current = next(t, current) | |
| 780 if current == nil then | |
| 781 for k in pairs(t) do | |
| 782 t[k] = nil | |
| 783 end | |
| 784 t[true] = true | |
| 785 t[true] = nil | |
| 786 pool[t] = true | |
| 787 return | |
| 788 end | |
| 789 return current, f(t, current) | |
| 790 end | |
| 791 function unpackSetAndDel(t) | |
| 792 if not t then | |
| 793 error(("Bad argument #1 to `unpackListAndDel'. Expected %q, got %q."):format("table", type(t)), 2) | |
| 794 end | |
| 795 setmetatable(t, nil) | |
| 796 if debug then | |
| 797 debug[t] = nil | |
| 798 debug.num = debug.num - 1 | |
| 799 end | |
| 800 return f(t, nil) | |
| 801 end | |
| 802 end | |
| 803 tmp[i] = unpackSetAndDel | |
| 804 elseif func == "unpackDictAndDel" then | |
| 805 local unpackDictAndDel = recycleData_func[namespace] | |
| 806 if not unpackDictAndDel then | |
| 807 local function f(t, current) | |
| 808 local value | |
| 809 current, value = next(t, current) | |
| 810 if current == nil then | |
| 811 for k in pairs(t) do | |
| 812 t[k] = nil | |
| 813 end | |
| 814 t[true] = true | |
| 815 t[true] = nil | |
| 816 pool[t] = true | |
| 817 return | |
| 818 end | |
| 819 return current, value, f(t, current) | |
| 820 end | |
| 821 function unpackDictAndDel(t) | |
| 822 if not t then | |
| 823 error(("Bad argument #1 to `unpackListAndDel'. Expected %q, got %q."):format("table", type(t)), 2) | |
| 824 end | |
| 825 setmetatable(t, nil) | |
| 826 if debug then | |
| 827 debug[t] = nil | |
| 828 debug.num = debug.num - 1 | |
| 829 end | |
| 830 return f(t, nil) | |
| 831 end | |
| 832 end | |
| 833 tmp[i] = unpackDictAndDel | |
| 834 else | |
| 835 error(("Bad argument #%d to `GetRecyclingFunctions': %q, %q, %q, %q, %q, %q, or %q expected, got %s"):format(i+2, "newList", "newDict", "newSet", "del", "unpackListAndDel", "unpackSetAndDel", "unpackDictAndDel", type(func) == "string" and ("%q"):format(func) or tostring(func)), 2) | |
| 836 end | |
| 837 end | |
| 838 return myUnpack(tmp) | |
| 839 end | |
| 840 | |
| 841 --[[--------------------------------------------------------------------------- | |
| 842 Notes: | |
| 843 * Prints information about the specified recycling namespace, including what tables are still in play and where they come from and how many there are. | |
| 844 * This goes in tandem with ''':GetRecyclingFunctions''' | |
| 845 Arguments: | |
| 846 string - the namespace. ''Note: this doesn't necessarily have to be a string.'' | |
| 847 Example: | |
| 848 local newList = Rock:GetRecyclingFunctions("MyNamespace", "newList", "Debug") | |
| 849 local t = newList() | |
| 850 Rock:DebugRecycle("MyNamespace") | |
| 851 t = del(t) | |
| 852 -----------------------------------------------------------------------------]] | |
| 853 function Rock:DebugRecycle(namespace) | |
| 854 local debug = recycleData.debugPools and recycleData.debugPools[namespace] | |
| 855 if not debug then | |
| 856 return | |
| 857 end | |
| 858 for k, v in pairs(debug) do | |
| 859 if k ~= "num" then | |
| 860 _G.DEFAULT_CHAT_FRAME:AddMessage(v) | |
| 861 _G.DEFAULT_CHAT_FRAME:AddMessage("------") | |
| 862 end | |
| 863 end | |
| 864 _G.DEFAULT_CHAT_FRAME:AddMessage(("%s: %d tables in action."):format(tostring(namespace), debug.num)) | |
| 865 end | |
| 866 | |
| 867 local newList, del, unpackListAndDel, unpackDictAndDel = Rock:GetRecyclingFunctions(MAJOR_VERSION, "newList", "del", "unpackListAndDel", "unpackDictAndDel") | |
| 868 | |
| 869 --[[--------------------------------------------------------------------------- | |
| 870 Notes: | |
| 871 * Adds a unit test for the specified namespace | |
| 872 * The function provided is called, and it should be where tests are performed, if a problem occurs, an error should fire. If no problems occur, it should return silently. | |
| 873 * You can have as many tests per namespace as you want. | |
| 874 Arguments: | |
| 875 string - the namespace. | |
| 876 function - the function to call. | |
| 877 Example: | |
| 878 Rock:AddUnitTest("LibMonkey-1.0", function() | |
| 879 local LibMonkey = Rock("LibMonkey-1.0") | |
| 880 assert(LibMonkey:Fling() == "Poo") | |
| 881 end) | |
| 882 -----------------------------------------------------------------------------]] | |
| 883 function Rock:AddUnitTest(namespace, func) | |
| 884 if not isStandalone then | |
| 885 return | |
| 886 end | |
| 887 if type(namespace) ~= "string" then | |
| 888 error(("Bad argument #2 to `AddUnitTest'. Expected %q, got %q."):format("string", type(namespace)), 2) | |
| 889 end | |
| 890 if namespace:find("^Lib[A-Z]") then | |
| 891 local addon = figureCurrentAddon(2) | |
| 892 if addon ~= namespace then | |
| 893 return | |
| 894 end | |
| 895 end | |
| 896 if type(func) ~= "function" then | |
| 897 error(("Bad argument #3 to `AddUnitTest'. Expected %q, got %q."):format("function", type(func)), 2) | |
| 898 end | |
| 899 local addon = figureCurrentAddon(2) | |
| 900 if libraries[namespace] and addon ~= namespace then | |
| 901 -- only work on standalone libraries. | |
| 902 return | |
| 903 end | |
| 904 local unitTests_namespace = unitTests[namespace] | |
| 905 if not unitTests_namespace then | |
| 906 unitTests_namespace = newList() | |
| 907 unitTests[namespace] = unitTests_namespace | |
| 908 end | |
| 909 if not unitTests_namespace.addon then | |
| 910 unitTests_namespace.addon = addon | |
| 911 end | |
| 912 if unitTestDB and not unitTestDB[namespace] then | |
| 913 return | |
| 914 end | |
| 915 unitTests_namespace[#unitTests_namespace+1] = func | |
| 916 end | |
| 917 | |
| 918 local LibRockEvent | |
| 919 local LibRockModuleCore | |
| 920 local OpenDonationFrame, OpenIssueFrame | |
| 921 function Rock:OnLibraryLoad(major, library) | |
| 922 if major == "LibRockEvent-1.0" then | |
| 923 LibRockEvent = library | |
| 924 LibRockEvent:Embed(Rock) | |
| 925 elseif major == "LibRockModuleCore-1.0" then | |
| 926 LibRockModuleCore = library | |
| 927 elseif major == "LibRockConfig-1.0" then | |
| 928 if isStandalone then | |
| 929 library.rockOptions.args.advanced = { | |
| 930 type = 'group', | |
| 931 groupType = 'inline', | |
| 932 name = L["Advanced options"], | |
| 933 desc = L["Advanced options for developers and power users."], | |
| 934 order = -1, | |
| 935 args = { | |
| 936 unitTests = { | |
| 937 type = 'multichoice', | |
| 938 name = L["Unit tests"], | |
| 939 desc = L["Enable unit tests to be run. This is for developers only.\n\nYou must ReloadUI for changes to take effect."], | |
| 940 get = function(key) | |
| 941 return unitTestDB[key] | |
| 942 end, | |
| 943 set = function(key, value) | |
| 944 unitTestDB[key] = value or nil | |
| 945 end, | |
| 946 choices = function() | |
| 947 local t = newList() | |
| 948 for k in pairs(unitTests) do | |
| 949 t[k] = k | |
| 950 end | |
| 951 return "@dict", unpackDictAndDel(t) | |
| 952 end | |
| 953 }, | |
| 954 contracts = { | |
| 955 type = 'boolean', | |
| 956 name = L["Contracts"], | |
| 957 desc = L["Enable contracts to be run. This is for developers and anyone wanting to file a bug. Do not file a bug unless contracts are enabled. This will slightly slow down your addons if enabled."], | |
| 958 get = function() | |
| 959 return enableContracts | |
| 960 end, | |
| 961 set = function(value) | |
| 962 _G.LibRock_1_0DB.contracts = value or nil | |
| 963 enableContracts = value | |
| 964 end, | |
| 965 } | |
| 966 } | |
| 967 } | |
| 968 end | |
| 969 library.rockOptions.args.reloadui = { | |
| 970 type = 'execute', | |
| 971 name = L["Reload UI"], | |
| 972 desc = L["Reload the User Interface for some changes to take effect."], | |
| 973 buttonText = L["Reload"], | |
| 974 func = function() | |
| 975 _G.ReloadUI() | |
| 976 end, | |
| 977 order = -2, | |
| 978 } | |
| 979 Rock.donate = "Paypal:ckknight AT gmail DOT com" | |
| 980 library.rockOptions.args.donate = { | |
| 981 type = 'execute', | |
| 982 name = L["Give donation"], | |
| 983 buttonText = L["Donate"], | |
| 984 desc = L["Give a much-needed donation to the author of this addon."], | |
| 985 func = OpenDonationFrame, | |
| 986 passValue = Rock, | |
| 987 order = -3, | |
| 988 } | |
| 989 Rock.issueTracker = "Wowace:10027" | |
| 990 library.rockOptions.args.issue = { | |
| 991 type = 'execute', | |
| 992 name = L["File issue"], | |
| 993 buttonText = L["Report"], | |
| 994 desc = L["File a bug or request a new feature or an improvement to this addon."], | |
| 995 func = OpenIssueFrame, | |
| 996 passValue = Rock, | |
| 997 order = -4, | |
| 998 } | |
| 999 end | |
| 1000 end | |
| 1001 | |
| 1002 addon_mt.__index = {} | |
| 1003 local addon_mt___index = addon_mt.__index | |
| 1004 --[[--------------------------------------------------------------------------- | |
| 1005 #FORCE_DOC | |
| 1006 Notes: | |
| 1007 * This is exported to all addons. | |
| 1008 * This information is retrieved from LibRockModuleCore-1.0 if it is a module, otherwise from LibRockDB-1.0 if it uses that as a mixin, otherwise it keeps a variable locally. | |
| 1009 Returns: | |
| 1010 boolean - whether the addon is in an active state or not. | |
| 1011 Example: | |
| 1012 local active = MyAddon:IsActive() | |
| 1013 -----------------------------------------------------------------------------]] | |
| 1014 function addon_mt___index:IsActive() | |
| 1015 if LibRockModuleCore then | |
| 1016 local core = LibRockModuleCore:HasModule(self) | |
| 1017 if core then | |
| 1018 return core:IsModuleActive(self) | |
| 1019 end | |
| 1020 end | |
| 1021 | |
| 1022 local self_db = self.db | |
| 1023 if self_db then | |
| 1024 local disabled | |
| 1025 local self_db_raw = self_db.raw | |
| 1026 if self_db_raw then | |
| 1027 local self_db_raw_disabled = self_db_raw.disabled | |
| 1028 if self_db_raw_disabled then | |
| 1029 local profile = type(self.GetProfile) == "function" and select(2, self:GetProfile()) or false | |
| 1030 disabled = self_db_raw_disabled[profile] | |
| 1031 end | |
| 1032 else | |
| 1033 return false | |
| 1034 end | |
| 1035 return not disabled | |
| 1036 end | |
| 1037 | |
| 1038 return not inactiveAddons[self] | |
| 1039 end | |
| 1040 --[[--------------------------------------------------------------------------- | |
| 1041 #FORCE_DOC | |
| 1042 Notes: | |
| 1043 * This is exported to all addons. | |
| 1044 * If it enables the addon, it will call :OnEnable(first) on the addon and :OnEmbedEnable(addon, first) on all its mixins. | |
| 1045 * If it disables the addon, it will call :OnDisable(first) on the addon and :OnEmbedDisable(addon, first) on all its mixins. | |
| 1046 * This information is stored by LibRockModuleCore-1.0 if it is a module, otherwise from LibRockDB-1.0 if it uses that as a mixin, otherwise it keeps a variable locally. | |
| 1047 Arguments: | |
| 1048 [optional] boolean - whether the addon should be in an active state or not. Default: not :IsActive() | |
| 1049 Returns: | |
| 1050 boolean - whether the addon is in an active state or not. | |
| 1051 Example: | |
| 1052 MyAddon:ToggleActive() -- switch | |
| 1053 MyAddon:ToggleActive(true) -- force on | |
| 1054 MyAddon:ToggleActive(false) -- force off | |
| 1055 -----------------------------------------------------------------------------]] | |
| 1056 function addon_mt___index:ToggleActive(state) | |
| 1057 if state and state ~= true then | |
| 1058 error(("Bad argument #2 to `ToggleActive'. Expected %q or %q, got %q."):format("boolean", "nil", type(state)), 2) | |
| 1059 end | |
| 1060 if LibRockModuleCore then | |
| 1061 local core = LibRockModuleCore:HasModule(self) | |
| 1062 if core then | |
| 1063 return core:ToggleModuleActive(self, state) | |
| 1064 end | |
| 1065 end | |
| 1066 | |
| 1067 local self_db = self.db | |
| 1068 if self_db then | |
| 1069 local self_db_raw = self_db.raw | |
| 1070 if not self_db_raw then | |
| 1071 error("Error saving to database with `ToggleActive'. db.raw not available.", 2) | |
| 1072 end | |
| 1073 local self_db_raw_disabled = self_db_raw.disabled | |
| 1074 if not self_db_raw_disabled then | |
| 1075 self_db_raw_disabled = newList() | |
| 1076 self_db_raw.disabled = self_db_raw_disabled | |
| 1077 end | |
| 1078 local profile = type(self.GetProfile) == "function" and select(2, self:GetProfile()) or false | |
| 1079 if state == nil then | |
| 1080 state = not not self_db_raw_disabled[profile] | |
| 1081 elseif (not self_db_raw_disabled[profile]) == state then | |
| 1082 return | |
| 1083 end | |
| 1084 self_db_raw_disabled[profile] = not state or nil | |
| 1085 if next(self_db_raw_disabled) == nil then | |
| 1086 self_db_raw.disabled = del(self_db_raw_disabled) | |
| 1087 end | |
| 1088 else | |
| 1089 if state == nil then | |
| 1090 state = not not inactiveAddons[self] | |
| 1091 elseif (not inactiveAddons[self]) == state then | |
| 1092 return | |
| 1093 end | |
| 1094 inactiveAddons[self] = not state or nil | |
| 1095 end | |
| 1096 | |
| 1097 Rock:RecheckEnabledStates() | |
| 1098 | |
| 1099 return state | |
| 1100 end | |
| 1101 | |
| 1102 local function noop() end | |
| 1103 | |
| 1104 do | |
| 1105 local preconditions = setmetatable({}, weakKey) | |
| 1106 local postconditions = setmetatable({}, weakKey) | |
| 1107 local postconditionsOld = setmetatable({}, weakKey) | |
| 1108 | |
| 1109 local currentMethod = nil | |
| 1110 | |
| 1111 local function hook(object, method) | |
| 1112 local object_method = object[method] | |
| 1113 object[method] = function(...) | |
| 1114 local pre = preconditions[object_method] | |
| 1115 local post = postconditions[object_method] | |
| 1116 if pre then | |
| 1117 local old_currentMethod = currentMethod | |
| 1118 currentMethod = method | |
| 1119 pre(...) | |
| 1120 currentMethod = old_currentMethod | |
| 1121 end | |
| 1122 if not post then | |
| 1123 return object_method(...) | |
| 1124 end | |
| 1125 local oldFunc = postconditionsOld[object_method] | |
| 1126 local old | |
| 1127 if oldFunc then | |
| 1128 old = newList() | |
| 1129 oldFunc(old, ...) | |
| 1130 end | |
| 1131 | |
| 1132 local old_currentMethod = currentMethod | |
| 1133 currentMethod = nil | |
| 1134 local ret, n = newList(object_method(...)) | |
| 1135 | |
| 1136 currentMethod = method | |
| 1137 if old then | |
| 1138 post(old, ret, ...) | |
| 1139 old = del(old) | |
| 1140 else | |
| 1141 post(ret, ...) | |
| 1142 end | |
| 1143 currentMethod = old_currentMethod | |
| 1144 return unpackListAndDel(ret, 1, n) | |
| 1145 end | |
| 1146 end | |
| 1147 | |
| 1148 local function precondition(object, method, func) | |
| 1149 if type(object) ~= "table" then | |
| 1150 error(("Bad argument #1 to `precondition'. Expected %q, got %q."):format("table", type(object)), 2) | |
| 1151 end | |
| 1152 if type(object[method]) ~= "function" then | |
| 1153 error(("Method %q not found on object %s. Expected %q, got %q."):format(tostring(method), tostring(object), "function", type(object[method])), 2) | |
| 1154 end | |
| 1155 if type(func) ~= "function" then | |
| 1156 error(("Bad argument #3 to `precondition'. Expected %q, got %q."):format("function", type(func)), 2) | |
| 1157 end | |
| 1158 | |
| 1159 local object_method = object[method] | |
| 1160 if preconditions[object_method] then | |
| 1161 error("Cannot call `preconditon' on the same method twice.", 2) | |
| 1162 end | |
| 1163 preconditions[object_method] = func | |
| 1164 | |
| 1165 if not postconditions[object_method] then | |
| 1166 hook(object, method) | |
| 1167 end | |
| 1168 end | |
| 1169 | |
| 1170 local function postcondition(object, method, func, fillOld) | |
| 1171 if type(object) ~= "table" then | |
| 1172 error(("Bad argument #1 to `postcondition'. Expected %q, got %q."):format("table", type(object)), 2) | |
| 1173 end | |
| 1174 if type(object[method]) ~= "function" then | |
| 1175 error(("Method %q not found on object %s. Expected %q, got %q."):format(tostring(method), tostring(object), "function", type(object[method])), 2) | |
| 1176 end | |
| 1177 if type(func) ~= "function" then | |
| 1178 error(("Bad argument #3 to `postcondition'. Expected %q, got %q."):format("function", type(func)), 2) | |
| 1179 end | |
| 1180 if fillOld and type(fillOld) ~= "function" then | |
| 1181 error(("Bad argument #4 to `postcondition'. Expected %q or %q, got %q."):format("function", "nil", type(func)), 2) | |
| 1182 end | |
| 1183 | |
| 1184 local object_method = object[method] | |
| 1185 if postconditions[object_method] then | |
| 1186 error("Cannot call `postcondition' on the same method twice.", 2) | |
| 1187 end | |
| 1188 postconditions[object_method] = func | |
| 1189 postconditionsOld[object_method] = fillOld | |
| 1190 | |
| 1191 if not preconditions[object_method] then | |
| 1192 hook(object, method) | |
| 1193 end | |
| 1194 end | |
| 1195 | |
| 1196 local function argCheck(value, position, ...) | |
| 1197 if not currentMethod then | |
| 1198 error("Cannot call `argCheck' outside of a pre/post-condition.", 2) | |
| 1199 end | |
| 1200 if type(position) ~= "number" then | |
| 1201 error(("Bad argument #2 to `argCheck'. Expected %q, got %q"):format("number", type(position)), 2) | |
| 1202 end | |
| 1203 local type_value = type(value) | |
| 1204 for i = 1, select('#', ...) do | |
| 1205 local v = select(i, ...) | |
| 1206 if type(v) ~= "string" then | |
| 1207 error(("Bad argument #%d to `argCheck'. Expected %q, got %q"):format(i+1, "string", type(v)), 2) | |
| 1208 end | |
| 1209 if v == type_value then | |
| 1210 return | |
| 1211 end | |
| 1212 end | |
| 1213 local t = newList(...) | |
| 1214 t[#t] = nil | |
| 1215 for i,v in ipairs(t) do | |
| 1216 t[i] = ("%q"):format(v) | |
| 1217 end | |
| 1218 local s | |
| 1219 if #t == 0 then | |
| 1220 s = ("%q"):format((...)) | |
| 1221 elseif #t == 1 then | |
| 1222 s = ("%q or %q"):format(...) | |
| 1223 else | |
| 1224 s = table_concat(t, ", ") .. ", or " .. ("%q"):format(select(#t+1, ...)) | |
| 1225 end | |
| 1226 t = del(t) | |
| 1227 | |
| 1228 error(("Bad argument #%d to `%s'. Expected %s, got %q."):format(position, tostring(currentMethod), s, type_value), 4) | |
| 1229 end | |
| 1230 | |
| 1231 --[[--------------------------------------------------------------------------- | |
| 1232 Notes: | |
| 1233 * Returns functions for the specified namespace based on what is provided. | |
| 1234 * function types: | |
| 1235 ; "precondition" : to set the pre-condition for a method. | |
| 1236 ; "postcondition" : to set the post-condition for a method. | |
| 1237 ; "argCheck" : to check the type of an argument, to be executed within a pre-condition. | |
| 1238 * preconditon is in the form of <tt>precondition(object, "methodName", func(self, ...))</tt> | |
| 1239 * postcondition is in the form of either <tt>postcondition(object, "methodName", func(returnValues, self, ...))</tt> or <tt>postcondition(object, "methodName", func(oldValues, returnValues, self, ...), populateOld(oldValues, self, ...))</tt> | |
| 1240 ** returnValues is the list of return values, empty if no return values were sent. | |
| 1241 ** if the populateOld function is provided, then the empty oldValues table is provided and expected to be filled, and then given to the func. | |
| 1242 * argCheck is in the form of <tt>argCheck(value, n, "type1" [, "type2", ...])</tt> | |
| 1243 ** value is the value provided to the function you're checking. | |
| 1244 ** n is the argument position. ''Note: 1 is the position of `self'. 2 would be the first "real" position.'' | |
| 1245 ** the tuple of types can be any string, but specifically "nil", "boolean", "string", "number", "function", "userdata", "table", etc. | |
| 1246 Arguments: | |
| 1247 string - the namespace. ''Note: this doesn't necessarily have to be a string.'' | |
| 1248 Example: | |
| 1249 local precondition, postcondition, argCheck = Rock:GetRecyclingFunctions("Stack", "precondition", "postcondition", "argCheck") | |
| 1250 | |
| 1251 local stack = {} | |
| 1252 stack.IsEmpty = function(self) | |
| 1253 return self[1] == nil | |
| 1254 end | |
| 1255 stack.GetLength = function(self) | |
| 1256 return #self | |
| 1257 end | |
| 1258 stack.Push = function(self, value) | |
| 1259 self[#self+1] = value | |
| 1260 end | |
| 1261 precondition(stack, "Push", function(self, value) | |
| 1262 argCheck(value, 2, "string") -- only accept strings, no other values | |
| 1263 end) | |
| 1264 postcondition(stack, "Push", function(old, ret, self, value) | |
| 1265 assert(self:GetLength() == old.length+1) | |
| 1266 assert(not self:IsEmpty()) | |
| 1267 end, function(old, self) | |
| 1268 old.length = self:GetLength() | |
| 1269 end) | |
| 1270 stack.Pop = function(self) | |
| 1271 local value = self[#self] | |
| 1272 self[#self] = nil | |
| 1273 return value | |
| 1274 end | |
| 1275 precondition(stack, "Pop", function(self) | |
| 1276 assert(self:GetLength() >= 1) | |
| 1277 end) | |
| 1278 postcondition(stack, "Pop", function(old, ret, self) | |
| 1279 assert(self:GetLength() == old.length-1) | |
| 1280 end, function(old, self) | |
| 1281 old.length = self:GetLength() | |
| 1282 end) | |
| 1283 stack.Peek = function(self) | |
| 1284 return self[#self] | |
| 1285 end | |
| 1286 precondition(stack, "Peek", function(self) | |
| 1287 assert(self:GetLength() >= 1) | |
| 1288 end) | |
| 1289 postcondition(stack, "Peek", function(old, ret, self) | |
| 1290 assert(self:GetLength() == old.length) | |
| 1291 end, function(old, self) | |
| 1292 old.length = self:GetLength() | |
| 1293 end) | |
| 1294 | |
| 1295 local t = setmetatable({}, {__index=stack}) | |
| 1296 t:Push("Alpha") | |
| 1297 t:Push("Bravo") | |
| 1298 t:Push(5) -- error, only strings | |
| 1299 assert(t:Pop() == "Bravo") | |
| 1300 assert(t:Pop() == "Alpha") | |
| 1301 t:Pop() -- error, out of values | |
| 1302 -----------------------------------------------------------------------------]] | |
| 1303 function Rock:GetContractFunctions(namespace, ...) | |
| 1304 if namespace == "precondition" or namespace == "postcondition" or namespace == "argCheck" then | |
| 1305 error(("Bad argument #2 to `GetContractFunctions'. Cannot be %q."):format(namespace), 2) | |
| 1306 end | |
| 1307 local t = newList() | |
| 1308 if enableContracts then | |
| 1309 for i = 1, select('#', ...) do | |
| 1310 local v = select(i, ...) | |
| 1311 if v == "precondition" then | |
| 1312 t[i] = precondition | |
| 1313 elseif v == "postcondition" then | |
| 1314 t[i] = postcondition | |
| 1315 elseif v == "argCheck" then | |
| 1316 t[i] = argCheck | |
| 1317 else | |
| 1318 error(("Bad argument #%d to `GetContractFunctions'. Expected %q, %q, or %q, got %q."):format(i+2, "precondition", "postcondition", "argCheck", tostring(v))) | |
| 1319 end | |
| 1320 end | |
| 1321 else | |
| 1322 for i = 1, select('#', ...) do | |
| 1323 t[i] = noop | |
| 1324 end | |
| 1325 end | |
| 1326 return unpackListAndDel(t) | |
| 1327 end | |
| 1328 end | |
| 1329 | |
| 1330 --[[--------------------------------------------------------------------------- | |
| 1331 Notes: | |
| 1332 * convert a revision string to a number | |
| 1333 Arguments: | |
| 1334 string - revision string | |
| 1335 Returns: | |
| 1336 string or number - the string given or the number retrieved from it. | |
| 1337 -----------------------------------------------------------------------------]] | |
| 1338 local function coerceRevisionToNumber(version) | |
| 1339 if type(version) == "string" then | |
| 1340 return tonumber(version:match("(%-?%d+)")) or version | |
| 1341 else | |
| 1342 return version | |
| 1343 end | |
| 1344 end | |
| 1345 | |
| 1346 --[[--------------------------------------------------------------------------- | |
| 1347 Notes: | |
| 1348 * try to enable the standalone library specified | |
| 1349 Arguments: | |
| 1350 string - name of the library. | |
| 1351 Returns: | |
| 1352 boolean - whether the library is properly enabled and loadable. | |
| 1353 -----------------------------------------------------------------------------]] | |
| 1354 local function TryToEnable(addon) | |
| 1355 local islod = IsAddOnLoadOnDemand(addon) | |
| 1356 if islod then | |
| 1357 local _, _, _, enabled = GetAddOnInfo(addon) | |
| 1358 EnableAddOn(addon) | |
| 1359 local _, _, _, _, loadable = GetAddOnInfo(addon) | |
| 1360 if not loadable and not enabled then | |
| 1361 DisableAddOn(addon) | |
| 1362 end | |
| 1363 | |
| 1364 return loadable | |
| 1365 end | |
| 1366 end | |
| 1367 | |
| 1368 --[[--------------------------------------------------------------------------- | |
| 1369 Notes: | |
| 1370 * try to load the standalone library specified | |
| 1371 Arguments: | |
| 1372 string - name of the library. | |
| 1373 Returns: | |
| 1374 boolean - whether the library is loaded. | |
| 1375 -----------------------------------------------------------------------------]] | |
| 1376 local function TryToLoadStandalone(major) | |
| 1377 major = major:lower() | |
| 1378 if scannedLibraries[major] then | |
| 1379 return | |
| 1380 end | |
| 1381 scannedLibraries[major] = true | |
| 1382 local name, _, _, enabled, loadable, state = GetAddOnInfo(major) | |
| 1383 if state == "MISSING" or not IsAddOnLoadOnDemand(major) then | |
| 1384 -- backwards compatibility for X-AceLibrary | |
| 1385 local field = "X-AceLibrary-" .. major | |
| 1386 local loaded | |
| 1387 for i = 1, GetNumAddOns() do | |
| 1388 if GetAddOnMetadata(i, field) then | |
| 1389 name, _, _, enabled, loadable = GetAddOnInfo(i) | |
| 1390 | |
| 1391 loadable = (enabled and loadable) or TryToEnable(name) | |
| 1392 if loadable then | |
| 1393 loaded = true | |
| 1394 LoadAddOn(name) | |
| 1395 end | |
| 1396 end | |
| 1397 end | |
| 1398 | |
| 1399 return loaded | |
| 1400 elseif (enabled and loadable) or TryToEnable(major) then | |
| 1401 LoadAddOn(major) | |
| 1402 return true | |
| 1403 else | |
| 1404 return false | |
| 1405 end | |
| 1406 end | |
| 1407 | |
| 1408 --[[--------------------------------------------------------------------------- | |
| 1409 Notes: | |
| 1410 * Return the LibStub library, casing is unimportant. | |
| 1411 Arguments: | |
| 1412 string - name of the library. | |
| 1413 Returns: | |
| 1414 table or nil - library | |
| 1415 number - minor version | |
| 1416 -----------------------------------------------------------------------------]] | |
| 1417 local function GetLibStubLibrary(major) | |
| 1418 local lib, minor = LibStub:GetLibrary(major, true) | |
| 1419 if lib then | |
| 1420 return lib, minor | |
| 1421 end | |
| 1422 major = major:lower() | |
| 1423 for m, lib in LibStub:IterateLibraries() do | |
| 1424 if m:lower() == major then | |
| 1425 return LibStub:GetLibrary(m) | |
| 1426 end | |
| 1427 end | |
| 1428 return nil, nil | |
| 1429 end | |
| 1430 | |
| 1431 local finishLibraryRegistration | |
| 1432 --[[--------------------------------------------------------------------------- | |
| 1433 Notes: | |
| 1434 * create a new library if the version provided is not out of date. | |
| 1435 Arguments: | |
| 1436 string - name of the library. | |
| 1437 number - version of the library. | |
| 1438 Returns: | |
| 1439 library, oldLibrary | |
| 1440 * table or nil - the library with which to manipulate | |
| 1441 * table or nil - the old version of the library to upgrade from | |
| 1442 Example: | |
| 1443 local LibMonkey, oldLib = Rock:NewLibrary("LibMonkey-1.0", 50) | |
| 1444 if not LibMonkey then | |
| 1445 -- opt out now, out of date | |
| 1446 return | |
| 1447 end | |
| 1448 -----------------------------------------------------------------------------]] | |
| 1449 function Rock:NewLibrary(major, version) | |
| 1450 if type(major) ~= "string" then | |
| 1451 error(("Bad argument #2 to `NewLibrary'. Expected %q, got %q."):format("string", type(major)), 2) | |
| 1452 end | |
| 1453 if not major:match("^Lib[A-Z][A-Za-z%d%-]*%-%d+%.%d+$") then | |
| 1454 error(("Bad argument #2 to `NewLibrary'. Must match %q, got %q."):format("^Lib[A-Z][A-Za-z%d%-]*%-%d+%.%d+$", major), 2) | |
| 1455 end | |
| 1456 TryToLoadStandalone(major) | |
| 1457 version = coerceRevisionToNumber(version) | |
| 1458 if type(version) ~= "number" then | |
| 1459 error(("Bad argument #3 to `NewLibrary'. Expected %q, got %q."):format("number", type(version)), 2) | |
| 1460 end | |
| 1461 local library, oldMinor = LibStub:GetLibrary(major, true) | |
| 1462 if oldMinor and oldMinor >= version then | |
| 1463 -- in case LibStub is acting funny | |
| 1464 return nil, nil | |
| 1465 end | |
| 1466 local library, oldMinor = LibStub:NewLibrary(major, version) | |
| 1467 if not library then | |
| 1468 return nil, nil | |
| 1469 end | |
| 1470 local unitTests_major = unitTests[major] | |
| 1471 if unitTests_major then | |
| 1472 for k,v in pairs(unitTests_major) do | |
| 1473 unitTests_major[k] = nil | |
| 1474 end | |
| 1475 end | |
| 1476 for k, v in pairs(recycleData) do | |
| 1477 v[major] = nil | |
| 1478 end | |
| 1479 local mixinToObject_library = mixinToObject[library] | |
| 1480 | |
| 1481 local oldLib | |
| 1482 if oldMinor then | |
| 1483 -- previous version exists | |
| 1484 local mixins = newList() | |
| 1485 for mixin, objectSet in pairs(mixinToObject) do | |
| 1486 if objectSet[library] then | |
| 1487 mixins[mixin] = true | |
| 1488 end | |
| 1489 end | |
| 1490 for mixin in pairs(mixins) do | |
| 1491 mixin:Unembed(library) | |
| 1492 end | |
| 1493 mixins = del(mixins) | |
| 1494 oldLib = newList() | |
| 1495 for k, v in pairs(library) do | |
| 1496 oldLib[k] = v | |
| 1497 library[k] = nil | |
| 1498 end | |
| 1499 setmetatable(oldLib, getmetatable(library)) | |
| 1500 setmetatable(library, nil) | |
| 1501 end | |
| 1502 finishLibraryRegistration(major, version, library, figureCurrentAddon(2)) | |
| 1503 | |
| 1504 return library, oldLib | |
| 1505 end | |
| 1506 function finishLibraryRegistration(major, version, library, folder) | |
| 1507 library.name = major | |
| 1508 | |
| 1509 libraries[major] = library | |
| 1510 pendingLibraries[library] = folder | |
| 1511 local exportedMethods_library = exportedMethods[library] | |
| 1512 if exportedMethods_library then | |
| 1513 local mixinToObject_library = mixinToObject[library] | |
| 1514 if mixinToObject_library then | |
| 1515 for object in pairs(mixinToObject_library) do | |
| 1516 for _,v in ipairs(exportedMethods_library) do | |
| 1517 object[v] = nil | |
| 1518 end | |
| 1519 end | |
| 1520 end | |
| 1521 exportedMethods[library] = del(exportedMethods_library) | |
| 1522 end | |
| 1523 if library ~= Rock then | |
| 1524 Rock:Embed(library) | |
| 1525 end | |
| 1526 | |
| 1527 frame:Show() | |
| 1528 end | |
| 1529 if not oldRock then | |
| 1530 finishLibraryRegistration(MAJOR_VERSION, MINOR_VERSION, Rock, figureCurrentAddon(1)) | |
| 1531 end | |
| 1532 | |
| 1533 -- #NODOC | |
| 1534 local function __removeLibrary(libName) | |
| 1535 libraries[libName] = nil | |
| 1536 if LibStub.libs then | |
| 1537 LibStub.libs[libName] = nil | |
| 1538 end | |
| 1539 if LibStub.minors then | |
| 1540 LibStub.minors[libName] = nil | |
| 1541 end | |
| 1542 local lastCount | |
| 1543 repeat | |
| 1544 lastCount = collectgarbage('count') | |
| 1545 collectgarbage('collect') | |
| 1546 until lastCount == collectgarbage('count') | |
| 1547 end | |
| 1548 local function run(_,a) | |
| 1549 if a < 1/30 then | |
| 1550 collectgarbage('step') | |
| 1551 end | |
| 1552 end | |
| 1553 | |
| 1554 --[[--------------------------------------------------------------------------- | |
| 1555 Notes: | |
| 1556 * properly finalizes the library, essentially stating that it has loaded properly. | |
| 1557 * This will call :OnLibraryLoad("major", library) on every other library | |
| 1558 * This will also call :OnLibraryLoad("major", library) on the library provided, using every other library as the arguments. | |
| 1559 * An error will occur if this is not done before ADDON_LOADED. | |
| 1560 Arguments: | |
| 1561 string - name of the library. | |
| 1562 Example: | |
| 1563 local LibMonkey, oldLib = Rock:NewLibrary("LibMonkey-1.0", 50) | |
| 1564 if not LibMonkey then | |
| 1565 -- opt out now, out of date | |
| 1566 return | |
| 1567 end | |
| 1568 Rock:FinalizeLibrary("LibMonkey-1.0") | |
| 1569 -----------------------------------------------------------------------------]] | |
| 1570 function Rock:FinalizeLibrary(major) | |
| 1571 if type(major) ~= "string" then | |
| 1572 error(("Bad argument #2 to `FinalizeLibrary'. Expected %q, got %q."):format("string", type(major)), 2) | |
| 1573 end | |
| 1574 local library = libraries[major] | |
| 1575 if not library then | |
| 1576 error(("Bad argument #2 to `FinalizeLibrary'. %q is not a library."):format("string", major), 2) | |
| 1577 end | |
| 1578 pendingLibraries[library] = nil | |
| 1579 local library_OnLibraryLoad = library.OnLibraryLoad | |
| 1580 if library_OnLibraryLoad then | |
| 1581 for maj, lib in LibStub:IterateLibraries() do -- for all libraries | |
| 1582 if maj ~= major then | |
| 1583 local success, ret = pcall(library_OnLibraryLoad, library, maj, lib) | |
| 1584 if not success then | |
| 1585 geterrorhandler()(ret) | |
| 1586 break | |
| 1587 end | |
| 1588 end | |
| 1589 end | |
| 1590 end | |
| 1591 if finalizedLibraries[library] then | |
| 1592 return | |
| 1593 end | |
| 1594 finalizedLibraries[library] = true | |
| 1595 for maj, lib in pairs(libraries) do -- just Rock libraries | |
| 1596 if maj ~= major then | |
| 1597 local lib_OnLibraryLoad = lib.OnLibraryLoad | |
| 1598 if lib_OnLibraryLoad then | |
| 1599 local success, ret = pcall(lib_OnLibraryLoad, lib, major, library) | |
| 1600 if not success then | |
| 1601 geterrorhandler()(ret) | |
| 1602 end | |
| 1603 end | |
| 1604 end | |
| 1605 end | |
| 1606 if LibRockEvent then | |
| 1607 self:DispatchEvent("LibraryLoad", major, library) | |
| 1608 end | |
| 1609 end | |
| 1610 | |
| 1611 local function manualFinalize(major, library) | |
| 1612 if libraries[major] then -- non-Rock libraries only | |
| 1613 return | |
| 1614 end | |
| 1615 if finalizedLibraries[library] then -- don't do it twice | |
| 1616 return | |
| 1617 end | |
| 1618 finalizedLibraries[library] = true | |
| 1619 for maj, lib in pairs(libraries) do -- just Rock libraries | |
| 1620 if maj ~= major then | |
| 1621 local lib_OnLibraryLoad = lib.OnLibraryLoad | |
| 1622 if lib_OnLibraryLoad then | |
| 1623 local success, ret = pcall(lib_OnLibraryLoad, lib, major, library) | |
| 1624 if not success then | |
| 1625 geterrorhandler()(ret) | |
| 1626 end | |
| 1627 end | |
| 1628 end | |
| 1629 end | |
| 1630 if LibRockEvent then | |
| 1631 Rock:DispatchEvent("LibraryLoad", major, library) | |
| 1632 end | |
| 1633 end | |
| 1634 | |
| 1635 --[[--------------------------------------------------------------------------- | |
| 1636 Arguments: | |
| 1637 string - name of the library. | |
| 1638 [optional] boolean - whether to not load a library if it is not found. Default: false | |
| 1639 [optional] boolean - whether to not error if a library is not found. Default: false | |
| 1640 Returns: | |
| 1641 library | |
| 1642 * table or nil - the library requested | |
| 1643 Example: | |
| 1644 local LibMonkey = Rock:GetLibrary("LibMonkey-1.0") | |
| 1645 -- or | |
| 1646 local LibMonkey = Rock("LibMonkey-1.0") | |
| 1647 -----------------------------------------------------------------------------]] | |
| 1648 function Rock:GetLibrary(major, dontLoad, dontError) | |
| 1649 if type(major) ~= "string" then | |
| 1650 error(("Bad argument #2 to `GetLibrary'. Expected %q, got %q."):format("string", type(major)), 2) | |
| 1651 end | |
| 1652 if dontLoad and dontLoad ~= true then | |
| 1653 error(("Bad argument #3 to `GetLibrary'. Expected %q or %q, got %q."):format("boolean", "nil", type(dontLoad)), 2) | |
| 1654 end | |
| 1655 if dontError and dontError ~= true then | |
| 1656 error(("Bad argument #4 to `GetLibrary'. Expected %q or %q, got %q."):format("boolean", "nil", type(dontError)), 2) | |
| 1657 end | |
| 1658 if not dontLoad then | |
| 1659 TryToLoadStandalone(major) | |
| 1660 end | |
| 1661 | |
| 1662 local library = GetLibStubLibrary(major) | |
| 1663 if not library then | |
| 1664 if dontError then | |
| 1665 return nil | |
| 1666 end | |
| 1667 error(("Library %q not found."):format(major), 2) | |
| 1668 end | |
| 1669 | |
| 1670 return library | |
| 1671 end | |
| 1672 | |
| 1673 setmetatable(Rock, { __call = Rock.GetLibrary }) | |
| 1674 | |
| 1675 --[[--------------------------------------------------------------------------- | |
| 1676 Arguments: | |
| 1677 string - name of the library. | |
| 1678 Returns: | |
| 1679 boolean - whether the library exists and is a proper mixin which can be embedded. | |
| 1680 Example: | |
| 1681 local isMixin = Rock:IsLibraryMixin("LibMonkey-1.0") | |
| 1682 -----------------------------------------------------------------------------]] | |
| 1683 function Rock:IsLibraryMixin(name) | |
| 1684 local library = self:GetLibrary(name, false, true) | |
| 1685 if not library then | |
| 1686 return false | |
| 1687 end | |
| 1688 return not not exportedMethods[library] | |
| 1689 end | |
| 1690 | |
| 1691 --[[--------------------------------------------------------------------------- | |
| 1692 Arguments: | |
| 1693 string - name of the library. | |
| 1694 [optional] boolean - whether to not load a library if it is not found. Default: false | |
| 1695 Returns: | |
| 1696 library | |
| 1697 * table or nil - the library requested | |
| 1698 Example: | |
| 1699 local hasLibMonkey = Rock:HasLibrary("LibMonkey-1.0") | |
| 1700 -----------------------------------------------------------------------------]] | |
| 1701 function Rock:HasLibrary(major, dontLoad) | |
| 1702 if type(major) ~= "string" then | |
| 1703 error(("Bad argument #2 to `HasLibrary'. Expected %q, got %q."):format("string", type(major)), 2) | |
| 1704 end | |
| 1705 if dontLoad and dontLoad ~= true then | |
| 1706 error(("Bad argument #3 to `HasLibrary'. Expected %q or %q, got %q."):format("boolean", "nil", type(dontLoad)), 2) | |
| 1707 end | |
| 1708 if not dontLoad then | |
| 1709 TryToLoadStandalone(major) | |
| 1710 end | |
| 1711 return not not GetLibStubLibrary(major) | |
| 1712 end | |
| 1713 | |
| 1714 --[[--------------------------------------------------------------------------- | |
| 1715 Notes: | |
| 1716 * This is exported to all libraries | |
| 1717 Returns: | |
| 1718 major, minor | |
| 1719 * string - name of the library | |
| 1720 * number - version of the library | |
| 1721 Example: | |
| 1722 local major, minor = Rock:GetLibraryVersion() -- will be "LibRock-1.0", 12345 | |
| 1723 local major, minor = LibMonkey:GetLibraryVersion() -- will be "LibMonkey-1.0", 50 | |
| 1724 -----------------------------------------------------------------------------]] | |
| 1725 function Rock:GetLibraryVersion() | |
| 1726 if type(self) ~= "table" then | |
| 1727 return nil, nil | |
| 1728 end | |
| 1729 local major | |
| 1730 local name = self.name | |
| 1731 if name and GetLibStubLibrary(name) == self then | |
| 1732 major = name | |
| 1733 else | |
| 1734 for m, instance in LibStub:IterateLibraries() do | |
| 1735 if instance == self then | |
| 1736 major = m | |
| 1737 break | |
| 1738 end | |
| 1739 end | |
| 1740 if not major then | |
| 1741 return nil, nil | |
| 1742 end | |
| 1743 end | |
| 1744 local _, minor = GetLibStubLibrary(major) | |
| 1745 return major, minor | |
| 1746 end | |
| 1747 | |
| 1748 --[[--------------------------------------------------------------------------- | |
| 1749 Returns: | |
| 1750 an iterator to traverse all registered libraries. | |
| 1751 Example: | |
| 1752 for major, library in Rock:IterateLibraries() do | |
| 1753 -- do something with major and library | |
| 1754 end | |
| 1755 -----------------------------------------------------------------------------]] | |
| 1756 function Rock:IterateLibraries() | |
| 1757 return LibStub:IterateLibraries() | |
| 1758 end | |
| 1759 | |
| 1760 --[[--------------------------------------------------------------------------- | |
| 1761 Notes: | |
| 1762 * This is exported to all libraries | |
| 1763 * Allows you to set precisely what methods for the library to export. | |
| 1764 * This automatically turns a library into a mixin. | |
| 1765 Arguments: | |
| 1766 tuple - the list of method names to export. | |
| 1767 Example: | |
| 1768 local LibMonkey = Rock:NewLibrary("LibMonkey-1.0", 50) | |
| 1769 LibMonkey.FlingPoo = function(self) | |
| 1770 return "Splat!" | |
| 1771 end | |
| 1772 LibMonkey:SetExportedMethods("FlingPoo") | |
| 1773 -- later | |
| 1774 local Darwin = Rock:NewAddon("Darwin", "LibMonkey-1.0") | |
| 1775 assert(Darwin:FlingPoo() == "Splat!") | |
| 1776 -----------------------------------------------------------------------------]] | |
| 1777 function Rock:SetExportedMethods(...) | |
| 1778 if exportedMethods[self] then | |
| 1779 error("Cannot call `SetExportedMethods' more than once.", 2) | |
| 1780 end | |
| 1781 local t = newList(...) | |
| 1782 if #t == 0 then | |
| 1783 error("Must supply at least 1 method to `SetExportedMethods'.", 2) | |
| 1784 end | |
| 1785 for i,v in ipairs(t) do | |
| 1786 if type(self[v]) ~= "function" then | |
| 1787 error(("Bad argument #%d to `SetExportedMethods'. Method %q does not exist."):format(i+1, tostring(v)), 2) | |
| 1788 end | |
| 1789 end | |
| 1790 exportedMethods[self] = t | |
| 1791 | |
| 1792 local mixinToObject_library = mixinToObject[self] | |
| 1793 if mixinToObject_library then | |
| 1794 for object in pairs(mixinToObject_library) do | |
| 1795 for _,method in ipairs(t) do | |
| 1796 object[method] = self[method] | |
| 1797 end | |
| 1798 end | |
| 1799 end | |
| 1800 end | |
| 1801 | |
| 1802 --[[--------------------------------------------------------------------------- | |
| 1803 Notes: | |
| 1804 * This is exported to all libraries | |
| 1805 * Embeds all the methods previously set to export onto a table. | |
| 1806 * This will call :OnEmbed(object) on the library if it is available. | |
| 1807 Arguments: | |
| 1808 table - the table with which to export methods onto. | |
| 1809 Returns: | |
| 1810 The table provided, after embedding. | |
| 1811 Example: | |
| 1812 local LibMonkey = Rock:NewLibrary("LibMonkey-1.0", 50) | |
| 1813 LibMonkey.FlingPoo = function(self) | |
| 1814 return "Splat!" | |
| 1815 end | |
| 1816 LibMonkey:SetExportedMethods("FlingPoo") | |
| 1817 -- later | |
| 1818 local Darwin = {} | |
| 1819 Rock("LibMonkey-1.0"):Embed(Darwin) | |
| 1820 assert(Darwin:FlingPoo() == "Splat!") | |
| 1821 -----------------------------------------------------------------------------]] | |
| 1822 function Rock:Embed(object) | |
| 1823 if not exportedMethods[self] then | |
| 1824 error(("Cannot call `Embed' for library %q if `SetExportedMethods' has not been called."):format(tostring(self.name)), 2) | |
| 1825 end | |
| 1826 if type(object) ~= "table" then | |
| 1827 error(("Bad argument #2 to `Embed'. Expected %q, got %q."):format("table", type(object)), 2) | |
| 1828 end | |
| 1829 | |
| 1830 for i,v in ipairs(exportedMethods[self]) do | |
| 1831 if type(self[v]) ~= "function" then | |
| 1832 error(("Problem embedding method %q from library %q. Expected %q, got %q."):format(tostring(v), better_tostring(self), "function", type(self[v]))) | |
| 1833 end | |
| 1834 object[v] = self[v] | |
| 1835 end | |
| 1836 | |
| 1837 if not mixinToObject[self] then | |
| 1838 -- weak because objects come and go | |
| 1839 mixinToObject[self] = setmetatable(newList(), weakKey) | |
| 1840 end | |
| 1841 if mixinToObject[self][object] then | |
| 1842 error(("Cannot embed library %q into the same object %q more than once."):format(better_tostring(self), better_tostring(object)), 2) | |
| 1843 end | |
| 1844 mixinToObject[self][object] = true | |
| 1845 if type(rawget(object, 'mixins')) == "table" then | |
| 1846 object.mixins[self] = true | |
| 1847 end | |
| 1848 | |
| 1849 local self_OnEmbed = self.OnEmbed | |
| 1850 if self_OnEmbed then | |
| 1851 local success, ret = pcall(self_OnEmbed, self, object) | |
| 1852 if not success then | |
| 1853 geterrorhandler()(ret) | |
| 1854 end | |
| 1855 end | |
| 1856 | |
| 1857 return object | |
| 1858 end | |
| 1859 | |
| 1860 --[[--------------------------------------------------------------------------- | |
| 1861 Notes: | |
| 1862 * This is exported to all libraries | |
| 1863 * Unembeds all the methods previously set to export onto a table. | |
| 1864 * This will error if the library is not embedded on the object | |
| 1865 * This will call :OnUnembed(object) on the library if it is available. | |
| 1866 Arguments: | |
| 1867 table - the table with which to export methods onto. | |
| 1868 Returns: | |
| 1869 The table provided, after embedding. | |
| 1870 Example: | |
| 1871 local LibMonkey = Rock:NewLibrary("LibMonkey-1.0", 50) | |
| 1872 LibMonkey.FlingPoo = function(self) | |
| 1873 return "Splat!" | |
| 1874 end | |
| 1875 LibMonkey:SetExportedMethods("FlingPoo") | |
| 1876 -- later | |
| 1877 local Darwin = {} | |
| 1878 Rock("LibMonkey-1.0"):Embed(Darwin) | |
| 1879 assert(Darwin:FlingPoo() == "Splat!") | |
| 1880 Rock("LibMonkey-1.0"):Unembed(Darwin) | |
| 1881 assert(Darwin.FlingPoo == nil) | |
| 1882 -----------------------------------------------------------------------------]] | |
| 1883 function Rock:Unembed(object) | |
| 1884 if not exportedMethods[self] then | |
| 1885 error(("Cannot call `Unembed' for library %q if `SetExportedMethods' has not been called."):format(better_tostring(self)), 2) | |
| 1886 end | |
| 1887 | |
| 1888 if not mixinToObject[self] or not mixinToObject[self][object] then | |
| 1889 error(("Cannot unembed library %q from object %q, since it is not embedded originally."):format(better_tostring(self), better_tostring(object)), 2) | |
| 1890 end | |
| 1891 local mixinToObject_self = mixinToObject[self] | |
| 1892 mixinToObject_self[object] = nil | |
| 1893 if not next(mixinToObject_self) then | |
| 1894 mixinToObject[self] = del(mixinToObject_self) | |
| 1895 end | |
| 1896 | |
| 1897 local mixin_OnUnembed = self.OnUnembed | |
| 1898 if mixin_OnUnembed then | |
| 1899 local success, ret = pcall(mixin_OnUnembed, self, object) | |
| 1900 if not success then | |
| 1901 geterrorhandler()(ret) | |
| 1902 end | |
| 1903 end | |
| 1904 | |
| 1905 for i,v in ipairs(exportedMethods[self]) do | |
| 1906 object[v] = nil | |
| 1907 end | |
| 1908 end | |
| 1909 | |
| 1910 local function embedAce2Mixin(mixin, object) | |
| 1911 if not mixinToObject[mixin] then | |
| 1912 mixinToObject[mixin] = setmetatable(newList(), weakKey) | |
| 1913 end | |
| 1914 mixinToObject[mixin][object] = true | |
| 1915 mixin:embed(object) | |
| 1916 end | |
| 1917 | |
| 1918 local function embedLibStubMixin(mixin, object) | |
| 1919 if not mixinToObject[mixin] then | |
| 1920 mixinToObject[mixin] = setmetatable(newList(), weakKey) | |
| 1921 end | |
| 1922 mixinToObject[mixin][object] = true | |
| 1923 mixin:Embed(object) | |
| 1924 end | |
| 1925 | |
| 1926 --[[--------------------------------------------------------------------------- | |
| 1927 Notes: | |
| 1928 * create a new addon with the specified name. | |
| 1929 Arguments: | |
| 1930 string - name of the addon. | |
| 1931 tuple - list of mixins with which to embed into this addon. | |
| 1932 Returns: | |
| 1933 addon | |
| 1934 * table - the addon with which to manipulate | |
| 1935 Example: | |
| 1936 local MyAddon = Rock:NewAddon("MyAddon", "Mixin-1.0", "OtherMixin-2.0") | |
| 1937 -----------------------------------------------------------------------------]] | |
| 1938 function Rock:NewAddon(name, ...) | |
| 1939 if type(name) ~= "string" then | |
| 1940 error(("Bad argument #2 to `NewAddon'. Expected %q, got %q"):format("string", type(name)), 2) | |
| 1941 end | |
| 1942 if name:match("^Lib[A-Z]") then | |
| 1943 error(("Bad argument #2 to `NewAddon'. Cannot start with %q, got %q."):format("Lib", name), 2) | |
| 1944 end | |
| 1945 if self == Rock and name:match("_") then | |
| 1946 error(("Bad argument #2 to `NewAddon'. Cannot contain underscores, got %q."):format(name), 2) | |
| 1947 end | |
| 1948 | |
| 1949 if addons[name] then | |
| 1950 error(("Bad argument #2 to `NewAddon'. Addon %q already created."):format(name), 2) | |
| 1951 end | |
| 1952 local addon = setmetatable(newList(), addon_mt) | |
| 1953 addon.name = name | |
| 1954 | |
| 1955 local mixinSet = newList() | |
| 1956 | |
| 1957 for i = 1, select('#', ...) do | |
| 1958 local libName = select(i, ...) | |
| 1959 if mixinSet[libName] then | |
| 1960 error(("Bad argument #%d to `NewAddon'. %q already stated."):format(i+2, tostring(libName)), 2) | |
| 1961 end | |
| 1962 mixinSet[libName] = true | |
| 1963 TryToLoadStandalone(libName) | |
| 1964 local library = Rock:GetLibrary(libName, false, true) | |
| 1965 if not library then | |
| 1966 error(("Bad argument #%d to `NewAddon'. Library %q is not found."):format(i+2, tostring(libName)), 2) | |
| 1967 end | |
| 1968 | |
| 1969 local style = 'rock' | |
| 1970 | |
| 1971 if not exportedMethods[library] then | |
| 1972 local good = false | |
| 1973 if AceLibrary then | |
| 1974 local AceOO = AceLibrary:HasInstance("AceOO-2.0", false) and AceLibrary("AceOO-2.0") | |
| 1975 if AceOO.inherits(library, AceOO.Mixin) then | |
| 1976 good = true | |
| 1977 style = 'ace2' | |
| 1978 end | |
| 1979 end | |
| 1980 if not good and type(rawget(library, 'Embed')) == "function" then | |
| 1981 good = true | |
| 1982 style = 'libstub' | |
| 1983 end | |
| 1984 if not good then | |
| 1985 error(("Bad argument #%d to `NewAddon'. Library %q is not a mixin."):format(i+2, tostring(libName)), 2) | |
| 1986 end | |
| 1987 end | |
| 1988 | |
| 1989 if library == Rock then | |
| 1990 error(("Bad argument #%d to `NewAddon'. Cannot use %q as a mixin."):format(i+2, tostring(libName)), 2) | |
| 1991 end | |
| 1992 | |
| 1993 if style == 'rock' then | |
| 1994 library:Embed(addon) | |
| 1995 elseif style == 'ace2' then | |
| 1996 embedAce2Mixin(library, addon) | |
| 1997 elseif style == 'libstub' then | |
| 1998 embedLibStubMixin(library, addon) | |
| 1999 end | |
| 2000 end | |
| 2001 | |
| 2002 mixinSet = del(mixinSet) | |
| 2003 | |
| 2004 addons[name] = addon | |
| 2005 pendingAddons[#pendingAddons+1] = addon | |
| 2006 pendingAddonsEnable[#pendingAddonsEnable+1] = addon | |
| 2007 addonToFolder[addon] = figureCurrentAddon(self == Rock and 2 or 4) | |
| 2008 | |
| 2009 frame:Show() | |
| 2010 | |
| 2011 return addon | |
| 2012 end | |
| 2013 | |
| 2014 --[[--------------------------------------------------------------------------- | |
| 2015 Arguments: | |
| 2016 string - name of the addon. | |
| 2017 Returns: | |
| 2018 addon | |
| 2019 * table or nil - the addon requested | |
| 2020 Example: | |
| 2021 local MyAddon = Rock:GetAddon("MyAddon") | |
| 2022 -----------------------------------------------------------------------------]] | |
| 2023 function Rock:GetAddon(name) | |
| 2024 if type(name) ~= "string" then | |
| 2025 return nil | |
| 2026 end | |
| 2027 local addon = addons[name] | |
| 2028 if addon then | |
| 2029 return addon | |
| 2030 end | |
| 2031 name = name:lower() | |
| 2032 for k, v in pairs(addons) do | |
| 2033 if k:lower() == name then | |
| 2034 return v | |
| 2035 end | |
| 2036 end | |
| 2037 return nil | |
| 2038 end | |
| 2039 | |
| 2040 --[[--------------------------------------------------------------------------- | |
| 2041 Arguments: | |
| 2042 string or table - name of the addon or the addon itself. | |
| 2043 Returns: | |
| 2044 boolean - whether the addon requested exists. | |
| 2045 Example: | |
| 2046 local hasMyAddon = Rock:HasAddon("MyAddon") | |
| 2047 -- or | |
| 2048 local hasMyAddon = Rock:HasAddon(MyAddon) | |
| 2049 -----------------------------------------------------------------------------]] | |
| 2050 function Rock:HasAddon(name) | |
| 2051 if type(name) == "string" then | |
| 2052 local addon = addons[name] | |
| 2053 if addon then | |
| 2054 return true | |
| 2055 end | |
| 2056 name = name:lower() | |
| 2057 for k, v in pairs(addons) do | |
| 2058 if k:lower() == name then | |
| 2059 return true | |
| 2060 end | |
| 2061 end | |
| 2062 elseif type(name) == "table" then | |
| 2063 for k,v in pairs(addons) do | |
| 2064 if v == name then | |
| 2065 return true | |
| 2066 end | |
| 2067 end | |
| 2068 end | |
| 2069 return false | |
| 2070 end | |
| 2071 | |
| 2072 --[[--------------------------------------------------------------------------- | |
| 2073 Returns: | |
| 2074 an iterator to traverse all addons created with Rock. | |
| 2075 Example: | |
| 2076 for name, addon in Rock:IterateAddons() do | |
| 2077 -- do something with name and addon | |
| 2078 end | |
| 2079 -----------------------------------------------------------------------------]] | |
| 2080 function Rock:IterateAddons() | |
| 2081 return pairs(addons) | |
| 2082 end | |
| 2083 | |
| 2084 --[[--------------------------------------------------------------------------- | |
| 2085 Arguments: | |
| 2086 string - major version of the mixin library | |
| 2087 Returns: | |
| 2088 an iterator to traverse all objects that the given mixin has embedded into | |
| 2089 Example: | |
| 2090 local LibMonkey = Rock:NewLibrary("LibMonkey-1.0") | |
| 2091 local Darwin = Rock:NewAddon("Darwin", "LibMonkey-1.0") | |
| 2092 for object in LibMonkey:IterateMixinObjects("LibMonkey-1.0") do | |
| 2093 assert(object == Darwin) | |
| 2094 end | |
| 2095 -----------------------------------------------------------------------------]] | |
| 2096 function Rock:IterateMixinObjects(mixinName) | |
| 2097 local mixin | |
| 2098 if type(mixinName) == "table" then | |
| 2099 mixin = mixinName | |
| 2100 else | |
| 2101 if type(mixinName) ~= "string" then | |
| 2102 error(("Bad argument #2 to `IterateMixinObjects'. Expected %q or %q, got %q."):format("table", "string", type(mixinName)), 2) | |
| 2103 end | |
| 2104 mixin = libraries[mixinName] | |
| 2105 end | |
| 2106 local mixinToObject_mixin = mixinToObject[mixin] | |
| 2107 if not mixinToObject_mixin then | |
| 2108 return noop | |
| 2109 end | |
| 2110 return pairs(mixinToObject_mixin) | |
| 2111 end | |
| 2112 | |
| 2113 local function iter(object, mixin) | |
| 2114 mixin = next(mixinToObject, mixin) | |
| 2115 if not mixin then | |
| 2116 return nil | |
| 2117 elseif mixinToObject[mixin][object] then | |
| 2118 return mixin | |
| 2119 end | |
| 2120 return iter(object, mixin) -- try next mixin | |
| 2121 end | |
| 2122 --[[--------------------------------------------------------------------------- | |
| 2123 Returns: | |
| 2124 an iterator to traverse all mixins that an object has embedded | |
| 2125 Example: | |
| 2126 local LibMonkey = Rock:NewLibrary("LibMonkey-1.0") | |
| 2127 local Darwin = Rock:NewAddon("Darwin", "LibMonkey-1.0") | |
| 2128 for mixin in Rock:IterateObjectMixins(Darwin) do | |
| 2129 assert(mixin == LibMonkey) | |
| 2130 end | |
| 2131 -----------------------------------------------------------------------------]] | |
| 2132 function Rock:IterateObjectMixins(object) | |
| 2133 if type(object) ~= "table" then | |
| 2134 error(("Bad argument #2 to `IterateObjectMixins'. Expected %q, got %q."):format("table", type(object)), 2) | |
| 2135 end | |
| 2136 return iter, object, nil | |
| 2137 end | |
| 2138 | |
| 2139 --[[--------------------------------------------------------------------------- | |
| 2140 Arguments: | |
| 2141 table - the object to check | |
| 2142 string - the mixin to check | |
| 2143 Returns: | |
| 2144 boolean - whether the object has the given mixin embedded into it. | |
| 2145 Example: | |
| 2146 local LibMonkey = Rock:NewLibrary("LibMonkey-1.0") | |
| 2147 local Darwin = Rock:NewAddon("Darwin", "LibMonkey-1.0") | |
| 2148 assert(Rock:DoesObjectUseMixin(Darwin, "LibMonkey-1.0")) | |
| 2149 -----------------------------------------------------------------------------]] | |
| 2150 function Rock:DoesObjectUseMixin(object, mixinName) | |
| 2151 if type(object) ~= "table" then | |
| 2152 error(("Bad argument #2 to `IterateObjectMixins'. Expected %q, got %q."):format("table", type(object)), 2) | |
| 2153 end | |
| 2154 local mixin | |
| 2155 if type(mixinName) == "table" then | |
| 2156 mixin = mixinName | |
| 2157 else | |
| 2158 if type(mixinName) ~= "string" then | |
| 2159 error(("Bad argument #3 to `IterateMiDoesObjectUseMixininObjects'. Expected %q or %q, got %q."):format("table", "string", type(mixinName)), 2) | |
| 2160 end | |
| 2161 mixin = libraries[mixinName] | |
| 2162 end | |
| 2163 if not mixin then | |
| 2164 return false | |
| 2165 end | |
| 2166 | |
| 2167 local mixinToObject_mixin = mixinToObject[mixin] | |
| 2168 if not mixinToObject_mixin then | |
| 2169 return false | |
| 2170 end | |
| 2171 return not not mixinToObject_mixin[object] | |
| 2172 end | |
| 2173 | |
| 2174 Rock.UID_NUM = oldRock and oldRock.UID_NUM or 0 | |
| 2175 --[[--------------------------------------------------------------------------- | |
| 2176 Notes: | |
| 2177 * This UID is not unique across sessions. If you save a UID in a saved variable, the same UID can be generated in another session. | |
| 2178 Returns: | |
| 2179 number - a unique number. | |
| 2180 Example: | |
| 2181 local UID = Rock:GetUID() | |
| 2182 -----------------------------------------------------------------------------]] | |
| 2183 function Rock:GetUID() | |
| 2184 local num = Rock.UID_NUM + 1 | |
| 2185 Rock.UID_NUM = num | |
| 2186 return num | |
| 2187 end | |
| 2188 | |
| 2189 local function unobfuscateEmail(email) | |
| 2190 return email:gsub(" AT ", "@"):gsub(" DOT ", ".") | |
| 2191 end | |
| 2192 local function fix(char) | |
| 2193 return ("%%%02x"):format(char:byte()) | |
| 2194 end | |
| 2195 local function urlencode(text) | |
| 2196 return text:gsub("[^0-9A-Za-z]", fix) | |
| 2197 end | |
| 2198 | |
| 2199 local url | |
| 2200 local function makeURLFrame() | |
| 2201 makeURLFrame = nil | |
| 2202 local function bumpFrameLevels(frame, amount) | |
| 2203 frame:SetFrameLevel(frame:GetFrameLevel()+amount) | |
| 2204 local children = newList(frame:GetChildren()) | |
| 2205 for _,v in ipairs(children) do | |
| 2206 bumpFrameLevels(v, amount) | |
| 2207 end | |
| 2208 children = del(children) | |
| 2209 end | |
| 2210 -- some code borrowed from Prat here | |
| 2211 StaticPopupDialogs["ROCK_SHOW_URL"] = { | |
| 2212 text = not IsMacClient() and L["Press Ctrl-C to copy, then Alt-Tab out of the game, open your favorite web browser, and paste the link into the address bar."] or L["Press Cmd-C to copy, then Cmd-Tab out of the game, open your favorite web browser, and paste the link into the address bar."], | |
| 2213 button2 = ACCEPT, | |
| 2214 hasEditBox = 1, | |
| 2215 hasWideEditBox = 1, | |
| 2216 showAlert = 1, -- HACK : it's the only way I found to make de StaticPopup have sufficient width to show WideEditBox :( | |
| 2217 | |
| 2218 OnShow = function() | |
| 2219 local editBox = _G[this:GetName() .. "WideEditBox"] | |
| 2220 editBox:SetText(url) | |
| 2221 editBox:SetFocus() | |
| 2222 editBox:HighlightText(0) | |
| 2223 editBox:SetScript("OnTextChanged", function() StaticPopup_EditBoxOnTextChanged() end) | |
| 2224 | |
| 2225 local button = _G[this:GetName() .. "Button2"] | |
| 2226 button:ClearAllPoints() | |
| 2227 button:SetWidth(200) | |
| 2228 button:SetPoint("CENTER", editBox, "CENTER", 0, -30) | |
| 2229 | |
| 2230 _G[this:GetName() .. "AlertIcon"]:Hide() -- HACK : we hide the false AlertIcon | |
| 2231 this:SetFrameStrata("FULLSCREEN_DIALOG") | |
| 2232 bumpFrameLevels(this, 30) | |
| 2233 end, | |
| 2234 OnHide = function() | |
| 2235 local editBox = _G[this:GetName() .. "WideEditBox"] | |
| 2236 editBox:SetScript("OnTextChanged", nil) | |
| 2237 this:SetFrameStrata("DIALOG") | |
| 2238 bumpFrameLevels(this, -30) | |
| 2239 end, | |
| 2240 OnAccept = function() end, | |
| 2241 OnCancel = function() end, | |
| 2242 EditBoxOnEscapePressed = function() this:GetParent():Hide() end, | |
| 2243 EditBoxOnTextChanged = function() | |
| 2244 this:SetText(url) | |
| 2245 this:SetFocus() | |
| 2246 this:HighlightText(0) | |
| 2247 end, | |
| 2248 timeout = 0, | |
| 2249 whileDead = 1, | |
| 2250 hideOnEscape = 1 | |
| 2251 } | |
| 2252 end | |
| 2253 | |
| 2254 function OpenDonationFrame(self) | |
| 2255 if makeURLFrame then | |
| 2256 makeURLFrame() | |
| 2257 end | |
| 2258 | |
| 2259 local donate = self.donate | |
| 2260 if type(donate) ~= "string" then | |
| 2261 donate = "Wowace" | |
| 2262 end | |
| 2263 local style, data = (":"):split(donate, 2) | |
| 2264 style = style:lower() | |
| 2265 if style ~= "website" and style ~= "paypal" then | |
| 2266 style = "wowace" | |
| 2267 end | |
| 2268 if style == "wowace" then | |
| 2269 url = "http://www.wowace.com/wiki/Donations" | |
| 2270 elseif style == "website" then | |
| 2271 url = data | |
| 2272 else -- PayPal | |
| 2273 local text = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=" .. urlencode(unobfuscateEmail(data)) | |
| 2274 local name | |
| 2275 if type(self.title) == "string" then | |
| 2276 name = self.title | |
| 2277 elseif type(self.name) == "string" then | |
| 2278 name = self.name | |
| 2279 end | |
| 2280 if name == MAJOR_VERSION then | |
| 2281 name = "Rock" | |
| 2282 end | |
| 2283 if name then | |
| 2284 name = name:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", "") | |
| 2285 text = text .. "&item_name=" .. urlencode(name) | |
| 2286 end | |
| 2287 url = text | |
| 2288 end | |
| 2289 | |
| 2290 StaticPopup_Show("ROCK_SHOW_URL") | |
| 2291 end | |
| 2292 function OpenIssueFrame(self) | |
| 2293 if makeURLFrame then | |
| 2294 makeURLFrame() | |
| 2295 end | |
| 2296 | |
| 2297 local issueTracker = self.issueTracker | |
| 2298 if type(issueTracker) ~= "string" then | |
| 2299 return | |
| 2300 end | |
| 2301 local style, data = (":"):split(issueTracker, 2) | |
| 2302 style = style:lower() | |
| 2303 if style ~= "website" and style ~= "wowace" then | |
| 2304 return | |
| 2305 end | |
| 2306 if style == "wowace" then | |
| 2307 url = "http://jira.wowace.com/secure/CreateIssue.jspa?pid=" .. data | |
| 2308 elseif style == "website" then | |
| 2309 url = data | |
| 2310 end | |
| 2311 | |
| 2312 StaticPopup_Show("ROCK_SHOW_URL") | |
| 2313 end | |
| 2314 local function donate_hidden(addon) | |
| 2315 return type(addon.donate) ~= "string" | |
| 2316 end | |
| 2317 | |
| 2318 local function issue_hidden(addon) | |
| 2319 return type(addon.issueTracker) ~= "string" | |
| 2320 end | |
| 2321 | |
| 2322 -- #NODOC | |
| 2323 function Rock:GetRockConfigOptions(addon) | |
| 2324 return 'active', { | |
| 2325 type = 'boolean', | |
| 2326 name = L["Enabled"], | |
| 2327 desc = L["Enable or disable this addon."], | |
| 2328 get = 'IsActive', | |
| 2329 set = 'ToggleActive', | |
| 2330 handler = addon, | |
| 2331 order = -1, | |
| 2332 }, 'donate', { | |
| 2333 type = 'execute', | |
| 2334 name = L["Give donation"], | |
| 2335 buttonText = L["Donate"], | |
| 2336 desc = L["Give a much-needed donation to the author of this addon."], | |
| 2337 func = OpenDonationFrame, | |
| 2338 hidden = donate_hidden, | |
| 2339 passValue = addon, | |
| 2340 order = -2, | |
| 2341 }, 'issue', { | |
| 2342 type = 'execute', | |
| 2343 name = L["File issue"], | |
| 2344 buttonText = L["Report"], | |
| 2345 desc = L["File a bug or request a new feature or an improvement to this addon."], | |
| 2346 func = OpenIssueFrame, | |
| 2347 hidden = issue_hidden, | |
| 2348 passValue = addon, | |
| 2349 order = -3, | |
| 2350 } | |
| 2351 end | |
| 2352 | |
| 2353 local function initAddon(addon, name) | |
| 2354 name = addonToFolder[addon] or name or "" | |
| 2355 -- TOC checks | |
| 2356 if addon.title == nil then | |
| 2357 addon.title = GetAddOnMetadata(name, "Title") | |
| 2358 end | |
| 2359 if type(addon.title) == "string" then | |
| 2360 addon.title = addon.title:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):gsub("%-Rock%-$", ""):trim() | |
| 2361 end | |
| 2362 if addon.notes == nil then | |
| 2363 addon.notes = GetAddOnMetadata(name, "Notes") | |
| 2364 end | |
| 2365 if type(addon.notes) == "string" then | |
| 2366 addon.notes = addon.notes:trim() | |
| 2367 end | |
| 2368 if addon.version == nil then | |
| 2369 addon.version = GetAddOnMetadata(name, "Version") | |
| 2370 end | |
| 2371 if type(addon.version) == "string" then | |
| 2372 addon.version = addon.version:trim() | |
| 2373 end | |
| 2374 if addon.author == nil then | |
| 2375 addon.author = GetAddOnMetadata(name, "Author") | |
| 2376 end | |
| 2377 if type(addon.author) == "string" then | |
| 2378 addon.author = addon.author:trim() | |
| 2379 end | |
| 2380 if addon.credits == nil then | |
| 2381 addon.credits = GetAddOnMetadata(name, "X-Credits") | |
| 2382 end | |
| 2383 if type(addon.credits) == "string" then | |
| 2384 addon.credits = addon.credits:trim() | |
| 2385 end | |
| 2386 if addon.donate == nil then | |
| 2387 addon.donate = GetAddOnMetadata(name, "X-Donate") | |
| 2388 end | |
| 2389 if type(addon.donate) == "string" then | |
| 2390 addon.donate = addon.donate:trim() | |
| 2391 end | |
| 2392 if addon.issueTracker == nil then | |
| 2393 addon.issueTracker = GetAddOnMetadata(name, "X-IssueTracker") | |
| 2394 end | |
| 2395 if type(addon.issueTracker) == "string" then | |
| 2396 addon.issueTracker = addon.issueTracker:trim() | |
| 2397 end | |
| 2398 if addon.category == nil then | |
| 2399 addon.category = GetAddOnMetadata(name, "X-Category") | |
| 2400 end | |
| 2401 if type(addon.category) == "string" then | |
| 2402 addon.category = addon.category:trim() | |
| 2403 end | |
| 2404 if addon.email == nil then | |
| 2405 addon.email = GetAddOnMetadata(name, "X-eMail") or GetAddOnMetadata(name, "X-Email") | |
| 2406 end | |
| 2407 if type(addon.email) == "string" then | |
| 2408 addon.email = addon.email:trim() | |
| 2409 end | |
| 2410 if addon.license == nil then | |
| 2411 addon.license = GetAddOnMetadata(name, "X-License") | |
| 2412 end | |
| 2413 if type(addon.license) == "string" then | |
| 2414 addon.license = addon.license:trim() | |
| 2415 end | |
| 2416 if addon.website == nil then | |
| 2417 addon.website = GetAddOnMetadata(name, "X-Website") | |
| 2418 end | |
| 2419 if type(addon.website) == "string" then | |
| 2420 addon.website = addon.website:trim() | |
| 2421 end | |
| 2422 | |
| 2423 for mixin in Rock:IterateObjectMixins(addon) do | |
| 2424 local mixin_OnEmbedInitialize = mixin.OnEmbedInitialize | |
| 2425 if mixin_OnEmbedInitialize then | |
| 2426 local success, ret = pcall(mixin_OnEmbedInitialize, mixin, addon) | |
| 2427 if not success then | |
| 2428 geterrorhandler()(ret) | |
| 2429 end | |
| 2430 end | |
| 2431 end | |
| 2432 | |
| 2433 local addon_OnInitialize = addon.OnInitialize | |
| 2434 if addon_OnInitialize then | |
| 2435 local success, ret = pcall(addon_OnInitialize, addon) | |
| 2436 if not success then | |
| 2437 geterrorhandler()(ret) | |
| 2438 end | |
| 2439 end | |
| 2440 | |
| 2441 if LibRockEvent then | |
| 2442 Rock:DispatchEvent("AddonInitialized", addon) | |
| 2443 end | |
| 2444 end | |
| 2445 | |
| 2446 | |
| 2447 local function manualEnable(addon) | |
| 2448 for i,v in ipairs(pendingAddons) do | |
| 2449 if v == addon then | |
| 2450 return | |
| 2451 end | |
| 2452 end | |
| 2453 if currentlyEnabledAddons[addon] then | |
| 2454 return false | |
| 2455 end | |
| 2456 currentlyEnabledAddons[addon] = true | |
| 2457 | |
| 2458 local first = not addonsAlreadyEnabled[addon] | |
| 2459 addonsAlreadyEnabled[addon] = true | |
| 2460 | |
| 2461 for mixin in Rock:IterateObjectMixins(addon) do | |
| 2462 local mixin_OnEmbedEnable = mixin.OnEmbedEnable | |
| 2463 if mixin_OnEmbedEnable then | |
| 2464 local success, ret = pcall(mixin_OnEmbedEnable, mixin, addon, first) | |
| 2465 if not success then | |
| 2466 geterrorhandler()(ret) | |
| 2467 end | |
| 2468 end | |
| 2469 end | |
| 2470 local addon_OnEnable = addon.OnEnable | |
| 2471 if addon_OnEnable then | |
| 2472 local success, ret = pcall(addon_OnEnable, addon, first) | |
| 2473 if not success then | |
| 2474 geterrorhandler()(ret) | |
| 2475 end | |
| 2476 end | |
| 2477 | |
| 2478 if LibRockEvent then | |
| 2479 Rock:DispatchEvent("AddonEnabled", addon, first) | |
| 2480 end | |
| 2481 | |
| 2482 return true, first | |
| 2483 end | |
| 2484 | |
| 2485 local function manualDisable(addon) | |
| 2486 if not currentlyEnabledAddons[addon] then | |
| 2487 return false | |
| 2488 end | |
| 2489 currentlyEnabledAddons[addon] = nil | |
| 2490 | |
| 2491 for mixin in Rock:IterateObjectMixins(addon) do | |
| 2492 local mixin_OnEmbedDisable = mixin.OnEmbedDisable | |
| 2493 if mixin_OnEmbedDisable then | |
| 2494 local success, ret = pcall(mixin_OnEmbedDisable, mixin, addon) | |
| 2495 if not success then | |
| 2496 geterrorhandler()(ret) | |
| 2497 end | |
| 2498 end | |
| 2499 end | |
| 2500 local addon_OnDisable = addon.OnDisable | |
| 2501 if addon_OnDisable then | |
| 2502 local success, ret = pcall(addon_OnDisable, addon) | |
| 2503 if not success then | |
| 2504 geterrorhandler()(ret) | |
| 2505 end | |
| 2506 end | |
| 2507 | |
| 2508 if LibRockEvent then | |
| 2509 Rock:DispatchEvent("AddonDisabled", addon) | |
| 2510 end | |
| 2511 return true | |
| 2512 end | |
| 2513 | |
| 2514 local function enableAddon(addon) | |
| 2515 for i,v in ipairs(pendingAddons) do | |
| 2516 if v == addon then | |
| 2517 return | |
| 2518 end | |
| 2519 end | |
| 2520 if addon_mt___index.IsActive(addon) then | |
| 2521 manualEnable(addon) | |
| 2522 end | |
| 2523 end | |
| 2524 | |
| 2525 -- #NODOC | |
| 2526 -- This is used by internal Rock libraries after updating the active state. | |
| 2527 function Rock:RecheckEnabledStates() | |
| 2528 local changed = false | |
| 2529 for _,addon in pairs(addons) do | |
| 2530 local good = true | |
| 2531 for _,a in ipairs(pendingAddonsEnable) do | |
| 2532 if addon == a then | |
| 2533 good = false | |
| 2534 break | |
| 2535 end | |
| 2536 end | |
| 2537 if good then | |
| 2538 if addon_mt___index.IsActive(addon) then | |
| 2539 if manualEnable(addon) then | |
| 2540 changed = true | |
| 2541 end | |
| 2542 else | |
| 2543 if manualDisable(addon) then | |
| 2544 changed = true | |
| 2545 end | |
| 2546 end | |
| 2547 end | |
| 2548 end | |
| 2549 if changed then | |
| 2550 return self:RecheckEnabledStates() | |
| 2551 end | |
| 2552 end | |
| 2553 | |
| 2554 frame:UnregisterAllEvents() | |
| 2555 frame:RegisterEvent("ADDON_LOADED") | |
| 2556 frame:RegisterEvent("PLAYER_LOGIN") | |
| 2557 local function runMainAddonLoadedChunk(name) | |
| 2558 local tmp = newList() | |
| 2559 tmp, pendingAddons = pendingAddons, tmp | |
| 2560 for i, addon in ipairs(tmp) do | |
| 2561 local folder = addonToFolder[addon] | |
| 2562 if name and folder and not foldersLoaded[folder] then | |
| 2563 for j = i, #tmp do | |
| 2564 pendingAddons[#pendingAddons+1] = tmp[j] | |
| 2565 tmp[j] = nil | |
| 2566 end | |
| 2567 break | |
| 2568 end | |
| 2569 initAddon(addon, name) | |
| 2570 end | |
| 2571 | |
| 2572 if IsLoggedIn() then | |
| 2573 for i, addon in ipairs(tmp) do | |
| 2574 for j, v in ipairs(pendingAddonsEnable) do | |
| 2575 if v == addon then | |
| 2576 table_remove(pendingAddonsEnable, i) | |
| 2577 break | |
| 2578 end | |
| 2579 end | |
| 2580 enableAddon(addon) | |
| 2581 end | |
| 2582 for i, addon in ipairs(pendingAddonsEnable) do | |
| 2583 local good = true | |
| 2584 for j, v in ipairs(pendingAddons) do | |
| 2585 if v == addon then | |
| 2586 good = false | |
| 2587 break | |
| 2588 end | |
| 2589 end | |
| 2590 if not good then | |
| 2591 break | |
| 2592 end | |
| 2593 pendingAddonsEnable[i] = nil | |
| 2594 enableAddon(addon) | |
| 2595 end | |
| 2596 end | |
| 2597 tmp = del(tmp) | |
| 2598 for library, addonName in pairs(pendingLibraries) do | |
| 2599 if not name or foldersLoaded[addonName] then | |
| 2600 local success, ret = pcall(error, ("Library %q not finalized before ADDON_LOADED."):format(better_tostring(library)), 3) | |
| 2601 geterrorhandler()(ret) | |
| 2602 Rock:FinalizeLibrary((library:GetLibraryVersion())) | |
| 2603 end | |
| 2604 end | |
| 2605 | |
| 2606 if isStandalone then | |
| 2607 local LibRock_1_0DB = _G.LibRock_1_0DB | |
| 2608 if type(LibRock_1_0DB) ~= "table" then | |
| 2609 LibRock_1_0DB = {} | |
| 2610 _G.LibRock_1_0DB = LibRock_1_0DB | |
| 2611 end | |
| 2612 if type(LibRock_1_0DB.unitTests) ~= "table" then | |
| 2613 LibRock_1_0DB.unitTests = {} | |
| 2614 end | |
| 2615 enableContracts = LibRock_1_0DB.contracts or false | |
| 2616 unitTestDB = LibRock_1_0DB.unitTests | |
| 2617 for namespace, data in pairs(unitTests) do | |
| 2618 if not unitTestDB[namespace] then | |
| 2619 if data then | |
| 2620 del(data) | |
| 2621 unitTests[namespace] = false | |
| 2622 end | |
| 2623 elseif data and (not name or data.addon == name) then | |
| 2624 local stats = newList() | |
| 2625 for i,v in ipairs(data) do | |
| 2626 data[i] = nil | |
| 2627 | |
| 2628 local libs = newList() | |
| 2629 for k,v in pairs(libraries) do | |
| 2630 libs[k] = v | |
| 2631 end | |
| 2632 | |
| 2633 local success, ret = pcall(v) | |
| 2634 if not success then | |
| 2635 geterrorhandler()(ret) | |
| 2636 stats[i] = ret | |
| 2637 else | |
| 2638 stats[i] = false | |
| 2639 end | |
| 2640 | |
| 2641 for k in pairs(libraries) do | |
| 2642 if not libs[k] then | |
| 2643 __removeLibrary(k) | |
| 2644 end | |
| 2645 end | |
| 2646 libs = del(libs) | |
| 2647 | |
| 2648 local lastCount | |
| 2649 repeat | |
| 2650 lastCount = collectgarbage('count') | |
| 2651 collectgarbage('collect') | |
| 2652 until lastCount == collectgarbage('count') | |
| 2653 end | |
| 2654 del(data) | |
| 2655 unitTests[namespace] = false | |
| 2656 if #stats >= 1 then | |
| 2657 local pass, fail = 0, 0 | |
| 2658 for i,v in ipairs(stats) do | |
| 2659 if v then | |
| 2660 fail = fail + 1 | |
| 2661 else | |
| 2662 pass = pass + 1 | |
| 2663 end | |
| 2664 end | |
| 2665 | |
| 2666 local color | |
| 2667 if fail == 0 then | |
| 2668 _G.DEFAULT_CHAT_FRAME:AddMessage(("|cff00ff00%s: %d unit test(s) passed."):format(namespace, pass)) | |
| 2669 elseif pass > 0 then | |
| 2670 _G.DEFAULT_CHAT_FRAME:AddMessage(("|cffff0000%s: %d unit test(s) passed, %d unit test(s) failed."):format(namespace, pass, fail)) | |
| 2671 else | |
| 2672 _G.DEFAULT_CHAT_FRAME:AddMessage(("|cffff0000%s: %d unit test(s) failed."):format(namespace, fail)) | |
| 2673 end | |
| 2674 for i,v in ipairs(stats) do | |
| 2675 if v then | |
| 2676 _G.DEFAULT_CHAT_FRAME:AddMessage(("|cffff0000%s|r"):format(tostring(v))) | |
| 2677 end | |
| 2678 end | |
| 2679 if fail > 0 then | |
| 2680 _G.DEFAULT_CHAT_FRAME:AddMessage("|cffff0000----------|r") | |
| 2681 end | |
| 2682 end | |
| 2683 stats = del(stats) | |
| 2684 end | |
| 2685 end | |
| 2686 end | |
| 2687 if isStandalone and name == MAJOR_VERSION then | |
| 2688 Rock("LibRockEvent-1.0", false, true) -- load if possible | |
| 2689 Rock("LibRockConsole-1.0", false, true) -- load if possible - I like the default chat commands | |
| 2690 Rock("LibRockComm-1.0", false, true) -- load if possible - has version checking and the like | |
| 2691 Rock("LibRockConfig-1.0", false, true) -- load if possible - LibRock-1.0 registers with it. | |
| 2692 end | |
| 2693 | |
| 2694 for major, library in LibStub:IterateLibraries() do | |
| 2695 manualFinalize(major, library) | |
| 2696 end | |
| 2697 | |
| 2698 if IsLoggedIn() then | |
| 2699 collectgarbage('collect') | |
| 2700 end | |
| 2701 end | |
| 2702 | |
| 2703 frame:Show() | |
| 2704 frame:SetScript("OnUpdate", function(this, elapsed) | |
| 2705 -- capture all un-initialized addons. | |
| 2706 runMainAddonLoadedChunk() | |
| 2707 collectgarbage('collect') | |
| 2708 this:SetScript("OnUpdate", run) | |
| 2709 end) | |
| 2710 frame:SetScript("OnEvent", function(this, event, ...) | |
| 2711 if event == "ADDON_LOADED" then | |
| 2712 -- this creates a new table and flushes the old in case someone LoDs an addon inside ADDON_LOADED. | |
| 2713 local name = ... | |
| 2714 foldersLoaded[name] = true | |
| 2715 runMainAddonLoadedChunk(name) | |
| 2716 frame:Show() | |
| 2717 elseif event == "PLAYER_LOGIN" then | |
| 2718 for i, addon in ipairs(pendingAddonsEnable) do | |
| 2719 local good = true | |
| 2720 for _, a in ipairs(pendingAddons) do | |
| 2721 if a == addon then | |
| 2722 good = false | |
| 2723 break | |
| 2724 end | |
| 2725 end | |
| 2726 if good then | |
| 2727 pendingAddonsEnable[i] = nil | |
| 2728 enableAddon(addon) | |
| 2729 end | |
| 2730 end | |
| 2731 collectgarbage('collect') | |
| 2732 end | |
| 2733 end) | |
| 2734 | |
| 2735 Rock:SetExportedMethods("SetExportedMethods", "Embed", "Unembed", "GetLibraryVersion") | |
| 2736 | |
| 2737 Rock:FinalizeLibrary(MAJOR_VERSION) | |
| 2738 | |
| 2739 for major, library in LibStub:IterateLibraries() do | |
| 2740 manualFinalize(major, library) | |
| 2741 end | |
| 2742 | |
| 2743 Rock:AddUnitTest(MAJOR_VERSION, function() | |
| 2744 -- test recycling | |
| 2745 local newList, newDict, newSet, del = Rock:GetRecyclingFunctions(MAJOR_VERSION .. "_UnitTest", "newList", "newDict", "newSet", "del", "Debug") | |
| 2746 local t = newList("Alpha", "Bravo", "Charlie") | |
| 2747 assert(t[1] == "Alpha") | |
| 2748 assert(t[2] == "Bravo") | |
| 2749 assert(t[3] == "Charlie") | |
| 2750 t = del(t) | |
| 2751 t = newList("Alpha", "Bravo", "Charlie") | |
| 2752 -- check recycled table | |
| 2753 assert(t[1] == "Alpha") | |
| 2754 assert(t[2] == "Bravo") | |
| 2755 assert(t[3] == "Charlie") | |
| 2756 t = del(t) | |
| 2757 t = newDict("Alpha", "Bravo", "Charlie", "Delta") | |
| 2758 assert(t.Alpha == "Bravo") | |
| 2759 assert(t.Charlie == "Delta") | |
| 2760 t = del(t) | |
| 2761 t = newSet("Alpha", "Bravo", "Charlie") | |
| 2762 assert(t.Alpha) | |
| 2763 assert(t.Bravo) | |
| 2764 assert(t.Charlie) | |
| 2765 t = del(t) | |
| 2766 | |
| 2767 local debug = recycleData.debugPools[MAJOR_VERSION .. "_UnitTest"] | |
| 2768 assert(debug.num == 0) | |
| 2769 t = newList() | |
| 2770 assert(debug.num == 1) | |
| 2771 t[1] = newList() | |
| 2772 assert(debug.num == 2) | |
| 2773 t[2] = newList() | |
| 2774 assert(debug.num == 3) | |
| 2775 t[1] = del(t[1]) | |
| 2776 assert(debug.num == 2) | |
| 2777 t[2] = del(t[2]) | |
| 2778 assert(debug.num == 1) | |
| 2779 t = del(t) | |
| 2780 assert(debug.num == 0) | |
| 2781 end) | |
| 2782 | |
| 2783 Rock:AddUnitTest(MAJOR_VERSION, function() | |
| 2784 -- test :GetUID() | |
| 2785 local t = {} | |
| 2786 for i = 1, 10000 do | |
| 2787 local uid = Rock:GetUID() | |
| 2788 if t[i] then | |
| 2789 error(("UID match for iteration %d, UID %s"):format(i, uid)) | |
| 2790 end | |
| 2791 t[i] = true | |
| 2792 end | |
| 2793 end) | |
| 2794 | |
| 2795 Rock:AddUnitTest(MAJOR_VERSION, function() | |
| 2796 -- test basic creation and deletion | |
| 2797 assert(not LibStub:GetLibrary("LibRockFakeLib-1.0", true)) | |
| 2798 assert(not Rock:HasLibrary("LibRockFakeLib-1.0")) | |
| 2799 local lib = Rock:NewLibrary("LibRockFakeLib-1.0", 1) | |
| 2800 Rock:FinalizeLibrary("LibRockFakeLib-1.0") | |
| 2801 lib = nil | |
| 2802 assert(LibStub:GetLibrary("LibRockFakeLib-1.0", true)) | |
| 2803 assert(Rock:HasLibrary("LibRockFakeLib-1.0")) | |
| 2804 local good = false | |
| 2805 for _, lib in pairs(libraries) do | |
| 2806 if lib.name == "LibRockFakeLib-1.0" then | |
| 2807 good = true | |
| 2808 break | |
| 2809 end | |
| 2810 end | |
| 2811 assert(good) | |
| 2812 __removeLibrary("LibRockFakeLib-1.0") | |
| 2813 for _, lib in pairs(libraries) do | |
| 2814 assert(lib.name ~= "LibRockFakeLib-1.0") | |
| 2815 end | |
| 2816 assert(not LibStub:GetLibrary("LibRockFakeLib-1.0", true)) | |
| 2817 assert(not Rock:HasLibrary("LibRockFakeLib-1.0")) | |
| 2818 end) | |
| 2819 | |
| 2820 Rock:AddUnitTest(MAJOR_VERSION, function() | |
| 2821 -- test library creation and the like | |
| 2822 assert(not Rock:HasLibrary("LibRockFakeLib-1.0")) | |
| 2823 for name in Rock:IterateLibraries() do | |
| 2824 assert(name ~= "LibRockFakeLib-1.0") | |
| 2825 end | |
| 2826 | |
| 2827 local myLib, oldLib = Rock:NewLibrary("LibRockFakeLib-1.0", 1) | |
| 2828 assert(myLib) | |
| 2829 assert(myLib.name == "LibRockFakeLib-1.0") | |
| 2830 assert(not oldLib) | |
| 2831 | |
| 2832 assert(myLib:GetLibraryVersion() == "LibRockFakeLib-1.0") | |
| 2833 assert(select(2, myLib:GetLibraryVersion()) == 1) | |
| 2834 | |
| 2835 local good = false | |
| 2836 for name in Rock:IterateLibraries() do | |
| 2837 if name == "LibRockFakeLib-1.0" then | |
| 2838 good = true | |
| 2839 break | |
| 2840 end | |
| 2841 end | |
| 2842 assert(good) | |
| 2843 assert(Rock:HasLibrary("LibRockFakeLib-1.0")) | |
| 2844 assert(Rock:GetLibrary("LibRockFakeLib-1.0") == myLib) | |
| 2845 assert(Rock("LibRockFakeLib-1.0") == myLib) | |
| 2846 | |
| 2847 assert(not Rock:IsLibraryMixin("LibRockFakeLib-1.0")) | |
| 2848 function myLib:DoSomething() | |
| 2849 return "Something" | |
| 2850 end | |
| 2851 myLib:SetExportedMethods("DoSomething") | |
| 2852 assert(Rock:IsLibraryMixin("LibRockFakeLib-1.0")) | |
| 2853 local t = {} | |
| 2854 assert(not Rock:DoesObjectUseMixin(t, "LibRockFakeLib-1.0")) | |
| 2855 assert(not t.DoSomething) | |
| 2856 for mixin in Rock:IterateObjectMixins(t) do | |
| 2857 assert(false) | |
| 2858 end | |
| 2859 for object in Rock:IterateMixinObjects("LibRockFakeLib-1.0") do | |
| 2860 assert(false) | |
| 2861 end | |
| 2862 myLib:Embed(t) | |
| 2863 assert(t:DoSomething() == "Something") | |
| 2864 assert(Rock:DoesObjectUseMixin(t, "LibRockFakeLib-1.0")) | |
| 2865 for mixin in Rock:IterateObjectMixins(t) do | |
| 2866 assert(mixin == myLib) | |
| 2867 end | |
| 2868 for object in Rock:IterateMixinObjects("LibRockFakeLib-1.0") do | |
| 2869 assert(object == t) | |
| 2870 end | |
| 2871 | |
| 2872 Rock:FinalizeLibrary("LibRockFakeLib-1.0") | |
| 2873 | |
| 2874 local myNewLib, oldLib = Rock:NewLibrary("LibRockFakeLib-1.0", 2) | |
| 2875 assert(myNewLib == myLib) | |
| 2876 assert(oldLib) | |
| 2877 assert(Rock:GetLibrary("LibRockFakeLib-1.0") == myLib) | |
| 2878 | |
| 2879 function myLib:DoSomething() | |
| 2880 return "Something else" | |
| 2881 end | |
| 2882 function myLib:TrySomething() | |
| 2883 return "Blah" | |
| 2884 end | |
| 2885 myLib:SetExportedMethods("DoSomething", "TrySomething") | |
| 2886 assert(Rock:IsLibraryMixin("LibRockFakeLib-1.0")) | |
| 2887 assert(t:DoSomething() == "Something else") | |
| 2888 assert(t:TrySomething() == "Blah") | |
| 2889 assert(Rock:DoesObjectUseMixin(t, "LibRockFakeLib-1.0")) | |
| 2890 for mixin in Rock:IterateObjectMixins(t) do | |
| 2891 assert(mixin == myLib) | |
| 2892 end | |
| 2893 for object in Rock:IterateMixinObjects("LibRockFakeLib-1.0") do | |
| 2894 assert(object == t) | |
| 2895 end | |
| 2896 | |
| 2897 Rock:FinalizeLibrary("LibRockFakeLib-1.0") | |
| 2898 | |
| 2899 local myNewLib, oldLib = Rock:NewLibrary("LibRockFakeLib-1.0", 3) | |
| 2900 assert(myNewLib == myLib) | |
| 2901 assert(oldLib) | |
| 2902 assert(Rock:GetLibrary("LibRockFakeLib-1.0") == myLib) | |
| 2903 | |
| 2904 function myLib:DoSomething() | |
| 2905 return "Something" | |
| 2906 end | |
| 2907 myLib:SetExportedMethods("DoSomething") | |
| 2908 assert(Rock:IsLibraryMixin("LibRockFakeLib-1.0")) | |
| 2909 assert(t:DoSomething() == "Something") | |
| 2910 assert(t.TrySomething == nil) | |
| 2911 assert(Rock:DoesObjectUseMixin(t, "LibRockFakeLib-1.0")) | |
| 2912 for mixin in Rock:IterateObjectMixins(t) do | |
| 2913 assert(mixin == myLib) | |
| 2914 end | |
| 2915 for object in Rock:IterateMixinObjects("LibRockFakeLib-1.0") do | |
| 2916 assert(object == t) | |
| 2917 end | |
| 2918 | |
| 2919 Rock:FinalizeLibrary("LibRockFakeLib-1.0") | |
| 2920 | |
| 2921 assert(not Rock:NewLibrary("LibRockFakeLib-1.0", 2)) -- out of date | |
| 2922 assert(not Rock:NewLibrary("LibRockFakeLib-1.0", 3)) -- same revision | |
| 2923 end) | |
| 2924 | |
| 2925 Rock:AddUnitTest(MAJOR_VERSION, function() | |
| 2926 assert(not Rock:HasAddon("RockFakeAddon")) | |
| 2927 for name in Rock:IterateAddons() do | |
| 2928 assert(name ~= "RockFakeAddon") | |
| 2929 end | |
| 2930 | |
| 2931 local myAddon = Rock:NewAddon("RockFakeAddon") | |
| 2932 | |
| 2933 assert(myAddon) | |
| 2934 assert(myAddon.name == "RockFakeAddon") | |
| 2935 | |
| 2936 local good = false | |
| 2937 for name in Rock:IterateAddons() do | |
| 2938 if name == "RockFakeAddon" then | |
| 2939 good = true | |
| 2940 break | |
| 2941 end | |
| 2942 end | |
| 2943 assert(good) | |
| 2944 assert(Rock:HasAddon("RockFakeAddon")) | |
| 2945 assert(Rock:GetAddon("RockFakeAddon") == myAddon) | |
| 2946 end) | |
| 2947 | |
| 2948 Rock:AddUnitTest(MAJOR_VERSION, function() | |
| 2949 -- test :OnLibraryLoad | |
| 2950 local lib = Rock:NewLibrary("LibRockFakeLib-1.0", 1) | |
| 2951 local triggered = false | |
| 2952 function lib:OnLibraryLoad(major, instance) | |
| 2953 if major == "LibRockFakeLib-2.0" then | |
| 2954 triggered = true | |
| 2955 end | |
| 2956 end | |
| 2957 Rock:FinalizeLibrary("LibRockFakeLib-1.0") | |
| 2958 | |
| 2959 local lib = Rock:NewLibrary("LibRockFakeLib-2.0", 1) | |
| 2960 assert(not triggered) | |
| 2961 Rock:FinalizeLibrary("LibRockFakeLib-2.0") | |
| 2962 assert(triggered) | |
| 2963 triggered = false | |
| 2964 local lib = Rock:NewLibrary("LibRockFakeLib-2.0", 2) | |
| 2965 assert(not triggered) | |
| 2966 Rock:FinalizeLibrary("LibRockFakeLib-2.0") | |
| 2967 assert(not triggered) | |
| 2968 end) |
