vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[
2 --
3 -- Sea.util
4 --
5 -- Useful data manipulation functions
6 --
7 -- $LastChangedBy: karlkfi $
8 -- $Rev: 2952 $
9 -- $Date: 2006-01-14 01:57:48 -0600 (Sat, 14 Jan 2006) $
10 --]]
11  
12 --Compatible with the following Mini-Libs
13 local SEA_HOOKS_VERSION = 0.60; -- SeaHooks
14  
15 Sea.util = {
16  
17 --[[ Hyperlinks ]] --
18 --
19 -- makeHyperlink(string type, string linkText, Table[r,g,b] color)
20 --
21 -- Creates a hyperlink string which is returned to you.
22 --
23 -- Args:
24 -- (string type, string linkText, Table[r,g,b] color, boolean braces, table[left,right] braceString)
25 -- type - the Hyperlink type.
26 -- linkText - the text shown in the link
27 -- color - color of the link
28 -- braces - if true, add braces
29 -- braceString - table with .left for left brace and .right for right brace
30 --
31 makeHyperlink = function (type, linkText, color, braces, braceString)
32 local link = linkText;
33 if ( braces ) then
34 if ( braceString == nil ) then braceString = {}; end
35 if ( braceString.left == nil ) then braceString.left="["; end
36 if ( braceString.right == nil ) then braceString.right="]"; end
37  
38 link = braceString.left..link..braceString.right;
39 end
40 if (color) then
41 link = "|cFF"..color..link.."|r";
42 end
43 return "|H"..type.."|h"..link.."|h";
44 end;
45 --[[ Candidates for String ]]--
46  
47 --
48 -- join(list,separator)
49 --
50 -- Arguments:
51 -- (table list, String separator)
52 -- list - table of things to join
53 -- separator - the separator to place between objects
54 --
55 -- Returns:
56 -- (string joinedstring)
57 -- joinedstring - the list.toString() joined by separator(s)
58 --
59 -- Written by Thott (thott@thottbot.com)
60 join = function (list, separator)
61 -- Type check
62 if ( type(list) ~= "table" and type(list) ~= nil ) then
63 ChatFrame1:AddMessage("Non-table passed to Sea.util.join");
64 return nil;
65 end
66 if ( not list.n ) then
67 ChatFrame1:AddMessage("Custom table without .n passed to Sea.util.join");
68 return "";
69 end
70 if ( separator == nil ) then separator = ""; end
71  
72 local i;
73 local c = "";
74 local msg = "";
75 for i=1, list.n, 1 do
76 if(type(list[i]) ~= "nil" ) then
77 if(type(list[i]) == "boolean" ) then
78 msg = msg .. c .. "(";
79 if ( list[i] ) then
80 msg = msg .. "true";
81 else
82 msg = msg .. "false";
83 end
84 msg = msg .. ")";
85 elseif(type(list[i]) ~= "string" and type(list[i]) ~= "number") then
86 msg = msg .. c .. "(" .. type(list[i]) .. ")";
87 else
88 msg = msg .. c .. list[i];
89 end
90 else
91 msg = msg .. c .. "(nil)";
92 end
93 c = separator;
94 end
95 return msg;
96 end;
97  
98  
99 --[[ Nil in Array Fixing Functions ]]--
100  
101 --
102 -- fixnil (...)
103 --
104 -- Converts all nils to "(nil)" strings
105 --
106 -- Arguments:
107 -- () arg
108 -- arg - the list
109 --
110 -- Written by Thott (thott@thottbot.com)
111 --
112 fixnil = function(...)
113 return Sea.util.fixnilSub("(nil)", unpack(arg));
114 end;
115  
116  
117 --
118 -- Fixes nils with empty strings
119 --
120 -- Written by Thott (thott@thottbot.com)
121  
122 fixnilEmptyString = function (...)
123 return Sea.util.fixnilSub("", unpack(arg));
124 end;
125  
126 --
127 -- Fixes nils with 0s
128 --
129 -- Written by Thott (thott@thottbot.com)
130 fixnilZero = function (...)
131 return Sea.util.fixnilSub(0, unpack(arg));
132 end;
133  
134 --
135 -- fixnilsub (sub, ... )
136 --
137 -- replaces nils with a substitute
138 --
139 --
140 -- Written by Thott (thott@thottbot.com)
141 fixnilSub = function(sub, ... )
142 for i=1, arg.n, 1 do
143 if(not arg[i]) then
144 arg[i] = sub;
145 end
146 end
147 return arg;
148 end;
149  
150 };
151  
152  
153 --[[ Code from SeaHooks (external embedable) ]]--
154  
155 -- Version control
156 Sea.versions.SeaHooks = SEA_HOOKS_VERSION;
157  
158 -- Initialize Global Tables
159 Sea.util.Hooks = {};
160 Sea.util.valueTable = {};
161 Sea.util.returnArgs = {};
162  
163 -- Reserve Internal Local Namespaces (So that the User functions don't try to call non-existant globals)
164 local SeaHooks_hookInit, SeaHooks_getDynamicHookHandler, SeaHooks_hookHandler, SeaHooks_hookHandlerQuick, SeaHooks_hookHandlerDebug, SeaHooks_assignReturnArgs;
165  
166 ------------------------------------------------------------------------------
167 --[[ Function and Frame Script Element Hooking - User Functions ]]--
168 ------------------------------------------------------------------------------
169  
170 --
171 -- Sea.util.hook( string originalFunctionNameOrFrameName, string newFunction, string hooktype, string scriptElementName )
172 --
173 -- Hooks a function.
174 --
175 -- Example:
176 -- Sea.util.hook("some_function","my_function","hide|before|replace|after");
177 -- Sea.util.hook("some_frame_name","my_function","hide|before|replace|after", "some_script_element_name");
178 --
179 -- Hook types :
180 -- "hide" - call "my_function" instead of "some_function". If you return true, subsequent hooks will be called afterwards, otherwise no further hooks nor the orig function will be called.
181 -- "before" - call "my_function" before "some_function". If you return true, the subsequent args will be fed into the calls of the orig function as well as any other functions that hook "some_function".
182 -- "replace" - call instead of "some_function". If you return true, the orig function will be called afterwards, otherwise subsequent args will be returned by the hooked function call.
183 -- "after" - called after "some_function". If you return true subsequent args will be returned by the hooked function call.
184 --
185 --
186 -- Written by Thott (thott@thottbot.com)
187 -- Rewritten by AnduinLothar (karlkfi@cosmosui.org)
188 Sea.util.hook = function ( orig, new, hooktype, scriptElementName )
189 if(not hooktype) then
190 hooktype = "before";
191 end
192 local compoundOrig = orig;
193 if (scriptElementName) then
194 compoundOrig = orig.."."..scriptElementName;
195 end
196 Sea.io.dprintfc((SEA_HOOKS_DEBUG and (SEA_HOOKS_DEBUG_VERBOSE==compoundOrig)), nil, NORMAL_FONT_COLOR, "SeaHooks Progress: Hooking ", orig, " to ", new, ", hooktype ", hooktype, ", scriptElementName ", scriptElementName);
197 local newFunc = new;
198 if ( type(new) ~= "function" ) then
199 newFunc = Sea.util.getValue(new);
200 end
201  
202 local hookObj = Sea.util.Hooks[compoundOrig];
203 if(not hookObj) then
204 hookObj = SeaHooks_hookInit(orig, compoundOrig, scriptElementName);
205 else
206 for key,value in hookObj[hooktype] do
207 -- NOTE THIS SHOULD BE VALUE! VALUE! *NOT* KEY! (checking if the functions are the same, even if the names are different)
208 -- If the function is found previously inserted, it will not rehook
209 -- If the function is not found, the new function will be inserted at the end
210 if(value == newFunc) then
211 Sea.io.dprintfc((SEA_HOOKS_DEBUG and (SEA_HOOKS_DEBUG_VERBOSE==compoundOrig)), nil, NORMAL_FONT_COLOR, "SeaHooks Progress: Already hooked '", compoundOrig, "' with '", new, "' skipping.");
212 return;
213 end
214 end
215 end
216 -- intentionally will error if bad hooktype is passed
217 local currKey = table.getn(hookObj[hooktype])+1;
218 table.insert(hookObj[hooktype], currKey, newFunc);
219 hookObj.count = hookObj.count + 1; --increment hook counter
220  
221 -- Adds a ["#Info"] table for preserving reverse compatibility while passing the frame name of the frame that called the hook, for debug.
222 local infoKey = currKey.."Info";
223 local embeddedTable = hookObj[hooktype][infoKey];
224 if (type(embeddedTable) ~= "table") then
225 embeddedTable = {};
226 end
227 if (this) and (this:GetName()) then
228 embeddedTable.parent = this:GetName();
229 else
230 embeddedTable.parent = 'unknown';
231 end
232 embeddedTable.name = new;
233 hookObj[hooktype][infoKey] = embeddedTable; --repackage current hook info into hookObj
234  
235 Sea.util.Hooks[compoundOrig] = hookObj; --repackage all hook types
236 end
237  
238 --
239 -- Sea.util.unhook( string originalFunctionNameOrFrameName, string newFunction, string hooktype, string scriptElementName )
240 --
241 -- Unhooks a function
242 --
243 -- Example:
244 -- Sea.util.unhook("some_blizzard_function","my_function","before|after|hide|replace");
245 -- Sea.util.unhook("some_frame_name","my_function","before|after|hide|replace", "some_script_element_name");
246 --
247 -- This will remove a function hooked by Sea.util.hook.
248 --
249 -- Written by Thott (thott@thottbot.com)
250 -- Rewritten by AnduinLothar (karlkfi@cosmosui.org)
251 Sea.util.unhook = function ( orig, new, hooktype, scriptElementName )
252 if(not hooktype) then
253 hooktype = "before";
254 end
255 local compoundOrig = orig;
256 if (scriptElementName) then
257 compoundOrig = orig.."."..scriptElementName;
258 end
259 Sea.io.dprintfc((SEA_HOOKS_DEBUG and (SEA_HOOKS_DEBUG_VERBOSE==compoundOrig)), nil, NORMAL_FONT_COLOR, "SeaHooks Progress: Unhooking ", orig, " to ", new, ", hooktype ", hooktype, ", scriptElementName ", scriptElementName);
260 local newFunc = new;
261 if ( type(new) ~= "function" ) then
262 newFunc = Sea.util.getValue(new);
263 end
264 local hookObj = Sea.util.Hooks[compoundOrig];
265 if(not hookObj) then
266 hookObj = SeaHooks_hookInit(orig, compoundOrig, scriptElementName);
267 else
268 local foundIt;
269 for key,value in hookObj[hooktype] do
270 -- NOTE THIS SHOULD BE VALUE! VALUE! *NOT* KEY! (checking if the functions are the same, even if the names are different)
271 -- If the function is found it will be unhooked
272 if(value == newFunc) then
273 foundIt = true;
274 break;
275 --exit loop since found hook
276 end
277 end
278 if (not foundIt) then
279 Sea.io.dprintfc((SEA_HOOKS_DEBUG and (SEA_HOOKS_DEBUG_VERBOSE==compoundOrig)), nil, NORMAL_FONT_COLOR, "SeaHooks Progress: '", compoundOrig, "' not hooked with '", new, "' skipping.");
280 --hooked function not found so nothing to do
281 return;
282 end
283 end
284 local info = hookObj[hooktype]; --Sea.util.Hooks[compoundOrig][hooktype]
285 for key,value in info do
286 if (type(value) == "function") and (value == newFunc) then
287 info[key] = nil;
288 local embeddedTable = info[key.."Info"];
289 if (type(embeddedTable) == "table") then
290 embeddedTable.parent = nil;
291 embeddedTable.name = nil;
292 end
293 info[key.."Info"] = embeddedTable;
294 hookObj[hooktype] = info;
295 hookObj.count = hookObj.count - 1; --decrement hook counter
296 Sea.util.Hooks[compoundOrig] = hookObj --repackage all hook types
297 Sea.io.dprintfc((SEA_HOOKS_DEBUG and (SEA_HOOKS_DEBUG_VERBOSE==compoundOrig)), nil, NORMAL_FONT_COLOR, "SeaHooks Progress: Found and unhooked '", new, "' from '", compoundOrig, "'.");
298 return;
299 end
300 end
301 -- No Complete Unhooking - Incompatible with Frame Script Element Hooks - Also liable to erase function hooks loaded after the first hook.
302  
303 Sea.util.Hooks[compoundOrig] = hookObj --repackage all hook types
304 end
305  
306 --
307 -- Sea.util.getReturnArgs()
308 --
309 -- Get the current return values of a hooked function from within an 'after' hook.
310 --
311 -- Example:
312 -- local arg1, arg2 = Sea.util.getReturnArgs();
313 --
314 -- This will return nil if called outside of an 'after' hook.
315 -- Also, if you call any function within the 'after' hook that would lead to the call of another 'after' hook call then the global Sea.util.returnArgs would most likely change.
316 -- Thus it is highly recommend you grab whatever return arguments you need at the beginning of the function call and assign them to local variables.
317 -- This method preserves reverse compatibility as well as avoids table creation and thus does not effect GC.
318 --
319 -- Written by AnduinLothar (karlkfi@cosmosui.org)
320 Sea.util.getReturnArgs = function()
321 return unpack(Sea.util.returnArgs);
322 end
323  
324 --
325 -- Sea.util.debugHooks( boolean enable , string verboseFunctionName )
326 --
327 -- Enable standard or verbose error logging. (Prints to the default chat frame)
328 --
329 -- Examples:
330 -- On: Sea.util.debugHooks(1);
331 -- Verbose: Sea.util.debugHooks(1, "ChatFrame_OnLoad");
332 -- Off: Sea.util.debugHooks();
333 --
334 -- Args:
335 -- (boolean) enable - true/false
336 -- (string) verboseFunctionName - the name of a hooked function to enable verbose progress debugging on.
337 --
338 -- /script Sea.util.debugHooks(enabled, verboseHookName)
339 -- enabled - boolean, verboseHookName - string equal to 'orig' passed to hook function (nil to disable)
340 -- Note: enabling debug usually incurs a heavier proc load and can cause slow down when used with OnUpdate hooks.
341 --
342 -- Written by AnduinLothar (karlkfi@cosmosui.org),
343 Sea.util.debugHooks = function ( enable, verboseFunctionName )
344 if (enable) then
345 SEA_HOOKS_DEBUG = true;
346 if (verboseFunctionName) then
347 SEA_HOOKS_DEBUG_VERBOSE = verboseFunctionName;
348 else
349 SEA_HOOKS_DEBUG_VERBOSE = nil;
350 end
351 else
352 SEA_HOOKS_DEBUG = nil;
353 SEA_HOOKS_DEBUG_VERBOSE = nil;
354 end
355 end
356  
357 ------------------------------------------------------------------------------
358 --[[ String Parsing - User Functions ]]--
359 ------------------------------------------------------------------------------
360  
361 --
362 -- Sea.util.split(string text, string separator [, table oldTable [, boolean noPurge] ] )
363 --
364 -- Efficiently splits a string into a table by separators
365 --
366 -- Args:
367 -- (string text, string separator, table oldTable, boolean noPurge)
368 -- text - string containing input
369 -- separator - separators
370 -- oldTable (optional) - table to fill with the results
371 -- noPurge (optional) - do not clear extraneous entries in oldTable
372 --
373 -- Returns:
374 -- (table)
375 -- table - the table containing the exploded strings, which is freshly
376 -- created if oldTable wasn't passed
377 --
378 -- Aliases:
379 -- Sea.string.split
380 --
381 -- Notes:
382 -- In the interests of avoiding garbage generation, whenever possible pass
383 -- a table for split to reuse. Also, dont use [ or % in the separator as it
384 -- will conflict with the regex.
385 --
386 -- Written by Thott (thott@thottbot.com)
387 -- Modified by Legorol (legorol@cosmosui.org)
388 -- Optimized by AnduinLothar (with suggestions from Iriel and krka)
389 Sea.util.split = function ( text, separator, t, noPurge )
390 local value;
391 local mstart, mend = 1;
392 local oldn, numMatches = 0, 0;
393 local regexKey = "([^"..separator.."]+)";
394 local sfind = strfind; -- string.find if not in WoW (n calls)
395  
396 if ( not t ) then
397 t = {};
398 else
399 oldn = table.getn(t);
400 end
401  
402 -- Using string.find instead of string.gfind to avoid garbage generation
403 mstart, mend, value = sfind(text, regexKey, mstart);
404 while (value) do
405 numMatches = numMatches + 1;
406 t[numMatches] = value
407 mstart = mend + 1;
408 mstart, mend, value = sfind(text, regexKey, mstart);
409 end
410  
411 if ( not noPurge ) then
412 for i = numMatches+1, oldn do
413 t[i] = nil;
414 end
415 end
416  
417 table.setn(t, numMatches);
418  
419 return t;
420 end
421  
422  
423 -- Aliasing
424 Sea.string.split = Sea.util.split;
425 Sea.string.explode = Sea.util.split;
426  
427  
428 ------------------------------------------------------------------------------
429 --[[ Indexed Variable Referencing - User Functions ]]--
430 ------------------------------------------------------------------------------
431  
432 --
433 -- Sea.util.getValue( string variableName )
434 --
435 -- Obtains the value of a variable given its name.
436 --
437 -- Examples:
438 -- Sea.util.getValue("ChatFrame_OnLoad");
439 -- Sea.util.getValue("Class.subclass.element");
440 --
441 -- Args:
442 -- (string) variableName - the name of the variable
443 --
444 -- Returns:
445 -- value - the value that variable has
446 --
447 -- This function obtains the value that variableName contains.
448 -- It is able to return the value for both a global variable or for
449 -- the element of a table. If variableName doesn't exist, it returns nil.
450 --
451 -- Concept by Mugendai
452 -- Written by Legorol (legorol@cosmosui.org)
453 -- Optimized by AnduinLothar (with suggestions from Iriel and krka) Increased speed by 170% !
454 Sea.util.getValue = function ( variableName )
455 if ( type(variableName) ~= "string" ) then
456 return;
457 end
458  
459 local sfind = strfind;
460 local strsub = strsub;
461  
462 local sstart = 2;
463 local value;
464 -- Split the variable name at ".", first field is a global name
465 local match = sfind(variableName, '.', sstart, true);
466 if ( match ) then
467 value = getglobal(strsub(variableName, 0, match-1));
468 else
469 return getglobal(variableName);
470 end
471  
472 while true do
473 if (type(value) ~= "table") then
474 -- Returns nil rather than trying to index a non-table
475 return;
476 end
477 sstart = match + 1;
478 match = sfind(variableName, '.', sstart, true);
479  
480 if ( match ) then
481 -- next one (there are more)
482 value = value[strsub(variableName, sstart, match-1)];
483 else
484 -- last one
485 return value[strsub(variableName, sstart)];
486 end
487 end
488 end
489  
490 --
491 -- Sea.util.setValue( string variableName, value )
492 --
493 -- Sets the value of a variable given its name.
494 --
495 -- Examples:
496 -- Sea.util.setValue("ChatFrame_OnLoad", MyChatFrame_OnLoad);
497 -- Sea.util.setValue("Class.subclass.element", 5);
498 -- Sea.util.setValue("Class.subclass.function", function() dostuff; end);
499 -- Args:
500 -- (string) variableName - the name of the variable to change
501 -- value - the new value of the variable
502 --
503 -- Returns:
504 -- (boolean) success - true if the operation succeeded
505 --
506 -- This function sets the value of variableName.
507 -- It is able to set the value for both a global variable or for
508 -- the element of a table, including functions. If variableName
509 -- already exists, it is overwritten.
510 --
511 -- Concept by Mugendai
512 -- Written by Legorol (legorol@cosmosui.org)
513 -- Optimized by AnduinLothar (with suggestions from Iriel and krka)
514 Sea.util.setValue = function ( variableName, newValue )
515 if ( type(variableName) ~= "string" ) then
516 return;
517 end
518  
519 local sfind = strfind;
520 local strsub = strsub;
521  
522 local sstart = 2;
523 local value;
524 -- Split the variable name at ".", first field is a global name
525 local match = sfind(variableName, '.', sstart, true);
526 if ( match ) then
527 value = getglobal(strsub(variableName, 0, match-1));
528 else
529 setglobal(variableName, newValue);
530 return true;
531 end
532  
533 while true do
534 if (type(value) ~= "table") then
535 -- Returns nil rather than trying to index a non-table
536 return false;
537 end
538 sstart = match + 1;
539 match = sfind(variableName, '.', sstart, true);
540  
541 if ( match ) then
542 -- next one (there are more)
543 value = value[strsub(variableName, sstart, match-1)];
544 else
545 -- last one
546 value[strsub(variableName, sstart)] = newValue;
547 return true;
548 end
549 end
550  
551 -- Error occured, subtable is not a table
552 return false;
553 end
554  
555 --------------------------------------------------------------------------------
556 --[[ Function and Frame Script Element Hooking - Internal Functions ]]--
557 --------------------------------------------------------------------------------
558  
559 --
560 -- Hook Initialization
561 --
562 -- Create a database instantiation the first time a hook is registered for a function. Stores the original function and establishes hook tables.
563 --
564 -- Written by AnduinLothar (karlkfi@cosmosui.org)
565 SeaHooks_hookInit = function ( orig, compoundOrig, scriptElementName )
566 Sea.io.dprintfc((SEA_HOOKS_DEBUG and (SEA_HOOKS_DEBUG_VERBOSE==compoundOrig)), nil, NORMAL_FONT_COLOR, "SeaHooks Progress: Hook Init - storing '", compoundOrig, "' orig and replacing with hookHandler.");
567 local hookObj = {
568 name = compoundOrig;
569 count = 0;
570 before = {};
571 after = {};
572 hide = {};
573 replace = {};
574 };
575 -- Set up the hook the first time
576 if (scriptElementName) then
577 local origFrame = Sea.util.getValue(orig);
578 hookObj.orig = origFrame:GetScript(scriptElementName);
579 hookObj.hookFunction = SeaHooks_getDynamicHookHandler(compoundOrig);
580 origFrame:SetScript(scriptElementName, hookObj.hookFunction);
581 Sea.util.setValue(orig, origFrame); --Reasign refrenced and modified origFrame
582 else
583 hookObj.orig = Sea.util.getValue(orig);
584 hookObj.hookFunction = SeaHooks_getDynamicHookHandler(compoundOrig);
585 Sea.util.setValue(orig, hookObj.hookFunction);
586 end
587 return hookObj;
588 end
589  
590 SeaHooks_getDynamicHookHandler = function ( databaseID )
591 return function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) return SeaHooks_hookHandler(databaseID,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) end;
592 end
593  
594 --
595 -- Hook Handler
596 --
597 -- Handles the name and the argument table.
598 -- An instantiated copy of this function is set to all hooked functions, passing the name of the original function staticly and any arguments dynamicly.
599 --
600 -- Written by Thott (thott@thottbot.com)
601 -- Rewritten by AnduinLothar (karlkfi@cosmosui.org)
602 SeaHooks_hookHandler = function (hookInfo,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
603 hookInfo = Sea.util.Hooks[hookInfo] --hookInfo passed in as string, exported as table on demand.
604 if (SEA_HOOKS_DEBUG) then
605 return SeaHooks_hookHandlerDebug(hookInfo,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
606 end
607 return SeaHooks_hookHandlerQuick(hookInfo,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
608 end
609  
610 --Quick (non-debug) Hook Handler. Called for each hook function to iterate over hook functions and original.
611 SeaHooks_hookHandlerQuick = function (hookObj,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
612 if (type(hookObj) ~= "table") then
613 hookObj = Sea.util.Hooks[hookObj];
614 if (not hookObj) then
615 return;
616 end;
617 end
618 if (hookObj.count == 0) then
619 if (hookObj.orig) then
620 return hookObj.orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
621 end
622 return;
623 end
624 local ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20; --return args
625 local toggle;
626 for key, value in hookObj.hide do
627 if(type(value) == "function") then
628 toggle,ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20 = value(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
629 if(not toggle) then
630 return ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20;
631 end
632 end
633 end
634 local ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20; --temp args
635 for key, value in hookObj.before do
636 if(type(value) == "function") then
637 toggle,ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20 = value(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
638 if(toggle) then
639 a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20 = ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20;
640 end
641 end
642 end
643 toggle = true;
644 for key, value in hookObj.replace do
645 if(type(value) == "function") then
646 toggle,ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20 = value(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
647 if(not toggle) then
648 ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20 = ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20;
649 break;
650 end
651 end
652 end
653 if (toggle) and (hookObj.orig) then
654 ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20 = hookObj.orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
655 end
656 for key,value in hookObj.after do
657 if(type(value) == "function") then
658 SeaHooks_assignReturnArgs(true,ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20);
659 toggle,ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20 = value(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
660 SeaHooks_assignReturnArgs();
661 if(toggle) then
662 ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20 = ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20;
663 end
664 end
665 end
666 if ( (type(ra1) == "table") and ( ( ra2 and ra3 and ra4 and ra5 and ra6 and ra7 and ra8 and ra9 and ra10 and ra11 and ra12 and ra13 and ra14 and ra15 and ra16 and ra17 and ra18 and ra19 and ra20 ) == nil ) ) then
667 return unpack(ra1);
668 end
669 return ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20;
670 end
671  
672 --Debug Hook Handler. Called for each hook function to iterate over hook functions and original.
673 SeaHooks_hookHandlerDebug = function (hookObj,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
674 --assumes SEA_HOOKS_DEBUG
675 local name, parent, hookFuncName;
676 if (type(hookObj) ~= "table") then
677 name = hookObj;
678 hookObj = Sea.util.Hooks[hookObj];
679 -- Quick exit since there's nothing to do!
680 if (not hookObj) then
681 Sea.io.dprintfc(true, nil, RED_FONT_COLOR, "SeaHooks Error: SeaHooks_hookHandler called with no defined hook parameters for '", name, "'.");
682 return;
683 end;
684 else
685 name = hookObj.name or 'unknown';
686 end
687 local debugVerbose = (SEA_HOOKS_DEBUG_VERBOSE) and (SEA_HOOKS_DEBUG_VERBOSE == name);
688 if (hookObj.count == 0) then
689 --Quickly Exit if no hooks exist
690 if (hookObj.orig) then
691 Sea.io.dprintfc(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: No known hooks for '", name, "'. Calling orig.");
692 return hookObj.orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
693 end
694 Sea.io.dprintfc(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: No known hooks or orig for '", name, "'. Exiting.");
695 return;
696 end
697 local ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20; --return args
698 local toggle; -- used for first arg returns from hooks
699 -- Itterate over and call 'hide' hooks. If there are none the for loop is skipped.
700 for key, value in hookObj.hide do
701 if(type(value) == "function") then
702 if (type(hookObj.hide[key.."Info"]) == "function") then
703 parent = hookObj.hide[key.."Info"].parent;
704 hookFuncName = hookObj.hide[key.."Info"].name;
705 end
706 Sea.io.dprintfc(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: calling 'hide' hook #", key, ": '", hookFuncName,"' for '", name, "', registered by '", parent,"'.");
707 --toggle (true) used to call orig function
708 toggle,ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20 = value(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
709 if(not toggle) then
710 if (SEA_HOOKS_DEBUG) then
711 Sea.io.dprintfc(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: 'hide' hook #", key, " for '", name, "' has hidden all subsequent hook and original function calls.");
712 local numHideHooks = table.getn(hookObj.hide);
713 Sea.io.dprintfc((numHideHooks > key), nil, RED_FONT_COLOR, "SeaHooks Error: detected ", numHideHooks, " 'hide' hooks for '", name, "', one of which has hidden another. This will most likely cause addon conflicts.");
714 end
715 --exit after first hide unless it says to call orig
716 return ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20;
717 end
718 end
719 end
720 local ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20; --temp args
721 -- Itterate over and call 'before' hooks. If there are none the for loop is skipped.
722 for key, value in hookObj.before do
723 if(type(value) == "function") then
724 Sea.io.dprintfc(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: calling a 'before' hook for '", name, "'.");
725 --toggle (true) used to override the input args
726 toggle,ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20 = value(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
727 if(toggle) then
728 -- Last 'before' hook that modifies input overrides all previous 'before' hooks for input values.
729 -- Keep in mind any previous input modification will modify the input of subsequent 'before' hooks as well as all the subsequent hook and orig function calls
730 a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20 = ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20;
731 Sea.io.dprintfc(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: 'before' hook #", key, " for '", name, "' has returned argument(s) to be passed to subsequent hook and original function calls.");
732 end
733 end
734 end
735 toggle = true; -- if no 'replace' hooks are called, calls the orig
736 -- Itterate over and call 'replace' hooks. If there are none the for loop is skipped.
737 for key, value in hookObj.replace do
738 if(type(value) == "function") then
739 Sea.io.dprintfc(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: calling a 'replace' hook for '", name, "'.");
740 --toggle (true) used to call the orig
741 toggle,ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20 = value(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
742 if(not toggle) then
743 -- Last 'replace' hook that modifies output (not toggle) overrides all previous 'replace' hooks for return values.
744 -- Theorhetically it would be nice if 'replace' hooks that fequently returned true to continue were called before ones that didn't.
745 -- That could be done by reordering the 'replace' list. Is it worth it? It might still conflict on first call. It might be better to just inform the debugger and let the programmer unhook and rehook in the order he wants.
746 ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20 = ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20;
747 --We should only call one replace hook that doesn't request the origional be called. Otherwise we will perform multiple versions of the main function, which would likely be worse, than not calling the extras at all.
748 break;
749 end
750 end
751 end
752 if (toggle) and (hookObj.orig) then --Frame Script Elements do not necissarily have an orig function
753 Sea.io.dprintfc(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: calling the 'orig' function for '", name, "'.");
754 -- If the 'orig' is called use its return values, overrides any 'replace' return values defined before a final continuing one.
755 ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20 = hookObj.orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
756 end
757 -- Itterate over and call 'after' hooks. If there are none the for loop is skipped.
758 for key,value in hookObj.after do
759 if(type(value) == "function") then
760 Sea.io.dprintfc(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: calling an 'after' hook for '", name, "'.");
761 --toggle (true) used to override the return args
762 --Current function return args availible via global pass vars
763 SeaHooks_assignReturnArgs(true,ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20);
764 toggle,ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20 = value(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
765 SeaHooks_assignReturnArgs(); --nil pass vars
766 if(toggle) then
767 -- Last 'after' hook that modifies output overrides all other hooks for return values.
768 ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20 = ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20;
769 Sea.io.dprintfc(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: 'after' hook for '", name, "' has returned argument(s) to be passed to subsequent hook and original function calls.");
770 end
771 end
772 end
773 --Only unpack ra1 if it is the only return argument passed.
774 if ( (type(ra1) == "table") and ( ( ra2 and ra3 and ra4 and ra5 and ra6 and ra7 and ra8 and ra9 and ra10 and ra11 and ra12 and ra13 and ra14 and ra15 and ra16 and ra17 and ra18 and ra19 and ra20 ) == nil ) ) then
775 Sea.io.dprintfc(SEA_HOOKS_DEBUG, nil, RED_FONT_COLOR, "SeaHooks Error: Return argument #1 is a table for '", name, "'. This can be used to pass return arguments from a replace/after hook. If that is not the intention of your hook then pass an additional non-nil argument to avoid unpacking.");
776 return unpack(ra1);
777 end
778 return ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20;
779 end
780  
781 -- Assigns up to 20 arguments to a global table so as to save on table creation and still allow return argument passing to 'after' hooks
782 -- Written by AnduinLothar (karlkfi@cosmosui.org)
783 SeaHooks_assignReturnArgs = function( toggle,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20 )
784 local temp = Sea.util.returnArgs;
785 if (toggle) then
786 temp[1]=a1;temp[2]=a2;temp[3]=a3;temp[4]=a4;temp[5]=a5;temp[6]=a6;temp[7]=a7;temp[8]=a8;temp[9]=a9;temp[10]=a10;
787 temp[11]=a11;temp[12]=a12;temp[13]=a13;temp[14]=a14;temp[15]=a15;temp[16]=a16;temp[17]=a17;temp[18]=a18;temp[19]=a19;temp[20]=a20;
788 local n=0;
789 for i=1, 20 do
790 if temp[i] then
791 n=i;
792 end
793 end
794 table.setn(temp,n);
795 else
796 for i=1, 20 do
797 temp[i]=nil;
798 end
799 table.setn(temp,0);
800 end
801 Sea.util.returnArgs = temp;
802 end