ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

hooks-manager.js (7466B)


      1 ( function( window, undefined ) {
      2 	'use strict';
      3 
      4 	/**
      5 	 * Handles managing all events for whatever you plug it into. Priorities for hooks are based on lowest to highest in
      6 	 * that, lowest priority hooks are fired first.
      7 	 */
      8 	var EventManager = function() {
      9 		var slice = Array.prototype.slice;
     10 		
     11 		/**
     12 		 * Maintain a reference to the object scope so our public methods never get confusing.
     13 		 */
     14 		var MethodsAvailable = {
     15 			removeFilter : removeFilter,
     16 			applyFilters : applyFilters,
     17 			addFilter : addFilter,
     18 			removeAction : removeAction,
     19 			doAction : doAction,
     20 			addAction : addAction
     21 		};
     22 
     23 		/**
     24 		 * Contains the hooks that get registered with this EventManager. The array for storage utilizes a "flat"
     25 		 * object literal such that looking up the hook utilizes the native object literal hash.
     26 		 */
     27 		var STORAGE = {
     28 			actions : {},
     29 			filters : {}
     30 		};
     31 
     32 		/**
     33 		 * Adds an action to the event manager.
     34 		 *
     35 		 * @param action Must contain namespace.identifier
     36 		 * @param callback Must be a valid callback function before this action is added
     37 		 * @param [priority=10] Used to control when the function is executed in relation to other callbacks bound to the same hook
     38 		 * @param [context] Supply a value to be used for this
     39 		 */
     40 		function addAction( action, callback, priority, context ) {
     41 			if( typeof action === 'string' && typeof callback === 'function' ) {
     42 				priority = parseInt( ( priority || 10 ), 10 );
     43 				_addHook( 'actions', action, callback, priority, context );
     44 			}
     45 
     46 			return MethodsAvailable;
     47 		}
     48 
     49 		/**
     50 		 * Performs an action if it exists. You can pass as many arguments as you want to this function; the only rule is
     51 		 * that the first argument must always be the action.
     52 		 */
     53 		function doAction( /* action, arg1, arg2, ... */ ) {
     54 			var args = slice.call( arguments );
     55 			var action = args.shift();
     56 
     57 			if( typeof action === 'string' ) {
     58 				_runHook( 'actions', action, args );
     59 			}
     60 
     61 			return MethodsAvailable;
     62 		}
     63 
     64 		/**
     65 		 * Removes the specified action if it contains a namespace.identifier & exists.
     66 		 *
     67 		 * @param action The action to remove
     68 		 * @param [callback] Callback function to remove
     69 		 */
     70 		function removeAction( action, callback ) {
     71 			if( typeof action === 'string' ) {
     72 				_removeHook( 'actions', action, callback );
     73 			}
     74 
     75 			return MethodsAvailable;
     76 		}
     77 
     78 		/**
     79 		 * Adds a filter to the event manager.
     80 		 *
     81 		 * @param filter Must contain namespace.identifier
     82 		 * @param callback Must be a valid callback function before this action is added
     83 		 * @param [priority=10] Used to control when the function is executed in relation to other callbacks bound to the same hook
     84 		 * @param [context] Supply a value to be used for this
     85 		 */
     86 		function addFilter( filter, callback, priority, context ) {
     87 			if( typeof filter === 'string' && typeof callback === 'function' ) {
     88 				priority = parseInt( ( priority || 10 ), 10 );
     89 				_addHook( 'filters', filter, callback, priority, context );
     90 			}
     91 
     92 			return MethodsAvailable;
     93 		}
     94 
     95 		/**
     96 		 * Performs a filter if it exists. You should only ever pass 1 argument to be filtered. The only rule is that
     97 		 * the first argument must always be the filter.
     98 		 */
     99 		function applyFilters( /* filter, filtered arg, arg2, ... */ ) {
    100 			var args = slice.call( arguments );
    101 			var filter = args.shift();
    102 
    103 			if( typeof filter === 'string' ) {
    104 				return _runHook( 'filters', filter, args );
    105 			}
    106 
    107 			return MethodsAvailable;
    108 		}
    109 
    110 		/**
    111 		 * Removes the specified filter if it contains a namespace.identifier & exists.
    112 		 *
    113 		 * @param filter The action to remove
    114 		 * @param [callback] Callback function to remove
    115 		 */
    116 		function removeFilter( filter, callback ) {
    117 			if( typeof filter === 'string') {
    118 				_removeHook( 'filters', filter, callback );
    119 			}
    120 
    121 			return MethodsAvailable;
    122 		}
    123 
    124 		/**
    125 		 * Removes the specified hook by resetting the value of it.
    126 		 *
    127 		 * @param type Type of hook, either 'actions' or 'filters'
    128 		 * @param hook The hook (namespace.identifier) to remove
    129 		 * @private
    130 		 */
    131 		function _removeHook( type, hook, callback, context ) {
    132 			var handlers, handler, i;
    133 			
    134 			if ( !STORAGE[ type ][ hook ] ) {
    135 				return;
    136 			}
    137 			if ( !callback ) {
    138 				STORAGE[ type ][ hook ] = [];
    139 			} else {
    140 				handlers = STORAGE[ type ][ hook ];
    141 				if ( !context ) {
    142 					for ( i = handlers.length; i--; ) {
    143 						if ( handlers[i].callback === callback ) {
    144 							handlers.splice( i, 1 );
    145 						}
    146 					}
    147 				}
    148 				else {
    149 					for ( i = handlers.length; i--; ) {
    150 						handler = handlers[i];
    151 						if ( handler.callback === callback && handler.context === context) {
    152 							handlers.splice( i, 1 );
    153 						}
    154 					}
    155 				}
    156 			}
    157 		}
    158 
    159 		/**
    160 		 * Adds the hook to the appropriate storage container
    161 		 *
    162 		 * @param type 'actions' or 'filters'
    163 		 * @param hook The hook (namespace.identifier) to add to our event manager
    164 		 * @param callback The function that will be called when the hook is executed.
    165 		 * @param priority The priority of this hook. Must be an integer.
    166 		 * @param [context] A value to be used for this
    167 		 * @private
    168 		 */
    169 		function _addHook( type, hook, callback, priority, context ) {
    170 			var hookObject = {
    171 				callback : callback,
    172 				priority : priority,
    173 				context : context
    174 			};
    175 
    176 			// Utilize 'prop itself' : http://jsperf.com/hasownproperty-vs-in-vs-undefined/19
    177 			var hooks = STORAGE[ type ][ hook ];
    178 			if( hooks ) {
    179 				hooks.push( hookObject );
    180 				hooks = _hookInsertSort( hooks );
    181 			}
    182 			else {
    183 				hooks = [ hookObject ];
    184 			}
    185 
    186 			STORAGE[ type ][ hook ] = hooks;
    187 		}
    188 
    189 		/**
    190 		 * Use an insert sort for keeping our hooks organized based on priority. This function is ridiculously faster
    191 		 * than bubble sort, etc: http://jsperf.com/javascript-sort
    192 		 *
    193 		 * @param hooks The custom array containing all of the appropriate hooks to perform an insert sort on.
    194 		 * @private
    195 		 */
    196 		function _hookInsertSort( hooks ) {
    197 			var tmpHook, j, prevHook;
    198 			for( var i = 1, len = hooks.length; i < len; i++ ) {
    199 				tmpHook = hooks[ i ];
    200 				j = i;
    201 				while( ( prevHook = hooks[ j - 1 ] ) &&  prevHook.priority > tmpHook.priority ) {
    202 					hooks[ j ] = hooks[ j - 1 ];
    203 					--j;
    204 				}
    205 				hooks[ j ] = tmpHook;
    206 			}
    207 
    208 			return hooks;
    209 		}
    210 
    211 		/**
    212 		 * Runs the specified hook. If it is an action, the value is not modified but if it is a filter, it is.
    213 		 *
    214 		 * @param type 'actions' or 'filters'
    215 		 * @param hook The hook ( namespace.identifier ) to be ran.
    216 		 * @param args Arguments to pass to the action/filter. If it's a filter, args is actually a single parameter.
    217 		 * @private
    218 		 */
    219 		function _runHook( type, hook, args ) {
    220 			var handlers = STORAGE[ type ][ hook ], i, len;
    221 			
    222 			if ( !handlers ) {
    223 				return (type === 'filters') ? args[0] : false;
    224 			}
    225 
    226 			len = handlers.length;
    227 			if ( type === 'filters' ) {
    228 				for ( i = 0; i < len; i++ ) {
    229 					args[ 0 ] = handlers[ i ].callback.apply( handlers[ i ].context, args );
    230 				}
    231 			} else {
    232 				for ( i = 0; i < len; i++ ) {
    233 					handlers[ i ].callback.apply( handlers[ i ].context, args );
    234 				}
    235 			}
    236 
    237 			return ( type === 'filters' ) ? args[ 0 ] : true;
    238 		}
    239 
    240 		// return all of the publicly available methods
    241 		return MethodsAvailable;
    242 
    243 	};
    244 	
    245 	
    246 	window.hooksManager = window.hooksManager || new EventManager();
    247 
    248 } )( window );