vanilla-wow-addons – Blame information for rev 1

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