| | 1 | ( function( wp ) { |
| | 2 | 'use strict'; |
| | 3 | |
| | 4 | /** |
| | 5 | * Contains the registered hooks, keyed by hook type. Each hook type is an |
| | 6 | * array of objects with priority and callback of each registered hook. |
| | 7 | */ |
| | 8 | var HOOKS = {}; |
| | 9 | |
| | 10 | /** |
| | 11 | * Returns a function which, when invoked, will add a hook. |
| | 12 | * |
| | 13 | * @param {string} type Type for which hooks are to be added |
| | 14 | * @return {Function} Hook added |
| | 15 | */ |
| | 16 | function createAddHookByType( type ) { |
| | 17 | /** |
| | 18 | * Adds the hook to the appropriate hooks container |
| | 19 | * |
| | 20 | * @param {string} hook Name of hook to add |
| | 21 | * @param {Function} callback Function to call when the hook is run |
| | 22 | * @param {?number} priority Priority of this hook (default=10) |
| | 23 | */ |
| | 24 | return function( hook, callback, priority ) { |
| | 25 | var hookObject, hooks; |
| | 26 | if ( typeof hook !== 'string' || typeof callback !== 'function' ) { |
| | 27 | return; |
| | 28 | } |
| | 29 | |
| | 30 | // Assign default priority |
| | 31 | if ( 'undefined' === typeof priority ) { |
| | 32 | priority = 10; |
| | 33 | } else { |
| | 34 | priority = parseInt( priority, 10 ); |
| | 35 | } |
| | 36 | |
| | 37 | // Validate numeric priority |
| | 38 | if ( isNaN( priority ) ) { |
| | 39 | return; |
| | 40 | } |
| | 41 | |
| | 42 | // Check if adding first of type |
| | 43 | if ( ! HOOKS[ type ] ) { |
| | 44 | HOOKS[ type ] = {}; |
| | 45 | } |
| | 46 | |
| | 47 | hookObject = { |
| | 48 | callback: callback, |
| | 49 | priority: priority |
| | 50 | }; |
| | 51 | |
| | 52 | if ( HOOKS[ type ].hasOwnProperty( hook ) ) { |
| | 53 | // Append and re-sort amongst existing |
| | 54 | hooks = HOOKS[ type ][ hook ]; |
| | 55 | hooks.push( hookObject ); |
| | 56 | hooks = sortHooks( hooks ); |
| | 57 | } else { |
| | 58 | // First of its type needs no sort |
| | 59 | hooks = [ hookObject ]; |
| | 60 | } |
| | 61 | |
| | 62 | HOOKS[ type ][ hook ] = hooks; |
| | 63 | }; |
| | 64 | } |
| | 65 | |
| | 66 | /** |
| | 67 | * Returns a function which, when invoked, will remove a specified hook. |
| | 68 | * |
| | 69 | * @param {string} type Type for which hooks are to be removed |
| | 70 | * @return {Function} Hook remover |
| | 71 | */ |
| | 72 | function createRemoveHookByType( type ) { |
| | 73 | /** |
| | 74 | * Removes the specified hook by resetting its value. |
| | 75 | * |
| | 76 | * @param {string} hook Name of hook to remove |
| | 77 | * @param {?Function} callback The specific callback to be removed. If |
| | 78 | * omitted, clears all callbacks. |
| | 79 | */ |
| | 80 | return function( hook, callback ) { |
| | 81 | var handlers, i; |
| | 82 | |
| | 83 | // Baily early if no hooks exist by this name |
| | 84 | if ( ! HOOKS[ type ] || ! HOOKS[ type ].hasOwnProperty( hook ) ) { |
| | 85 | return; |
| | 86 | } |
| | 87 | |
| | 88 | if ( callback ) { |
| | 89 | // Try to find specified callback to remove |
| | 90 | handlers = HOOKS[ type ][ hook ]; |
| | 91 | for ( i = handlers.length - 1; i >= 0; i-- ) { |
| | 92 | if ( handlers[ i ].callback === callback ) { |
| | 93 | handlers.splice( i, 1 ); |
| | 94 | } |
| | 95 | } |
| | 96 | } else { |
| | 97 | // Reset hooks to empty |
| | 98 | delete HOOKS[ type ][ hook ]; |
| | 99 | } |
| | 100 | }; |
| | 101 | } |
| | 102 | |
| | 103 | /** |
| | 104 | * Returns a function which, when invoked, will execute all registered |
| | 105 | * hooks of the specified type by calling upon runner with its hook name |
| | 106 | * and arguments. |
| | 107 | * |
| | 108 | * @param {string} type Type for which hooks are to be run, one of 'action' or 'filter'. |
| | 109 | * @param {Function} runner Function to invoke for each hook callback |
| | 110 | * @return {Function} Hook runner |
| | 111 | */ |
| | 112 | function createRunHookByType( type, runner ) { |
| | 113 | /** |
| | 114 | * Runs the specified hook. |
| | 115 | * |
| | 116 | * @param {string} hook The hook to run |
| | 117 | * @param {...*} args Arguments to pass to the action/filter |
| | 118 | * @return {*} Return value of runner, if applicable |
| | 119 | * @private |
| | 120 | */ |
| | 121 | return function( /* hook, ...args */ ) { |
| | 122 | var args, hook; |
| | 123 | |
| | 124 | args = Array.prototype.slice.call( arguments ); |
| | 125 | hook = args.shift(); |
| | 126 | |
| | 127 | if ( typeof hook === 'string' ) { |
| | 128 | return runner( hook, args ); |
| | 129 | } |
| | 130 | }; |
| | 131 | } |
| | 132 | |
| | 133 | /** |
| | 134 | * Performs an action if it exists. |
| | 135 | * |
| | 136 | * @param {string} action The action to perform. |
| | 137 | * @param {...*} args Optional args to pass to the action. |
| | 138 | * @private |
| | 139 | */ |
| | 140 | function runDoAction( action, args ) { |
| | 141 | var handlers, i; |
| | 142 | if ( HOOKS.actions ) { |
| | 143 | handlers = HOOKS.actions[ action ]; |
| | 144 | } |
| | 145 | |
| | 146 | if ( ! handlers ) { |
| | 147 | return; |
| | 148 | } |
| | 149 | |
| | 150 | HOOKS.actions.current = action; |
| | 151 | |
| | 152 | for ( i = 0; i < handlers.length; i++ ) { |
| | 153 | handlers[ i ].callback.apply( null, args ); |
| | 154 | HOOKS.actions[ action ].runs = HOOKS.actions[ action ].runs ? HOOKS.actions[ action ].runs + 1 : 1; |
| | 155 | } |
| | 156 | delete( HOOKS.actions.current ); |
| | 157 | |
| | 158 | } |
| | 159 | |
| | 160 | /** |
| | 161 | * Performs a filter if it exists. |
| | 162 | * |
| | 163 | * @param {string} filter The filter to apply. |
| | 164 | * @param {...*} args Optional args to pass to the filter. |
| | 165 | * @return {*} The filtered value |
| | 166 | * @private |
| | 167 | */ |
| | 168 | function runApplyFilters( filter, args ) { |
| | 169 | var handlers, i; |
| | 170 | if ( HOOKS.filters ) { |
| | 171 | handlers = HOOKS.filters[ filter ]; |
| | 172 | } |
| | 173 | |
| | 174 | if ( ! handlers ) { |
| | 175 | return args[ 0 ]; |
| | 176 | } |
| | 177 | |
| | 178 | HOOKS.filters.current = filter; |
| | 179 | HOOKS.filters[ filter ].runs = HOOKS.filters[ filter ].runs ? HOOKS.filters[ filter ].runs + 1 : 1; |
| | 180 | |
| | 181 | for ( i = 0; i < handlers.length; i++ ) { |
| | 182 | args[ 0 ] = handlers[ i ].callback.apply( null, args ); |
| | 183 | } |
| | 184 | delete( HOOKS.actions.current ); |
| | 185 | |
| | 186 | return args[ 0 ]; |
| | 187 | } |
| | 188 | |
| | 189 | /** |
| | 190 | * Use an insert sort for keeping our hooks organized based on priority. |
| | 191 | * |
| | 192 | * @see http://jsperf.com/javascript-sort |
| | 193 | * |
| | 194 | * @param {Array} hooks Array of the hooks to sort |
| | 195 | * @return {Array} The sorted array |
| | 196 | * @private |
| | 197 | */ |
| | 198 | function sortHooks( hooks ) { |
| | 199 | var i, tmpHook, j, prevHook; |
| | 200 | for ( i = 1; i < hooks.length; i++ ) { |
| | 201 | tmpHook = hooks[ i ]; |
| | 202 | j = i; |
| | 203 | while ( ( prevHook = hooks[ j - 1 ] ) && prevHook.priority > tmpHook.priority ) { |
| | 204 | hooks[ j ] = hooks[ j - 1 ]; |
| | 205 | --j; |
| | 206 | } |
| | 207 | hooks[ j ] = tmpHook; |
| | 208 | } |
| | 209 | |
| | 210 | return hooks; |
| | 211 | } |
| | 212 | |
| | 213 | |
| | 214 | /** |
| | 215 | * See what action is currently being executed. |
| | 216 | * |
| | 217 | * @param {string} type Type of hooks to check, one of 'action' or 'filter'. |
| | 218 | * @param {string} action The name of the action to check for. |
| | 219 | * |
| | 220 | * @return {[type]} [description] |
| | 221 | */ |
| | 222 | function createCurrentHookByType( type ) { |
| | 223 | return function( action ) { |
| | 224 | |
| | 225 | // If the action was not passed, check for any current hook. |
| | 226 | if ( 'undefined' === typeof action ) { |
| | 227 | return false; |
| | 228 | } |
| | 229 | |
| | 230 | // Return the current hook. |
| | 231 | return HOOKS[ type ] && HOOKS[ type ].current ? |
| | 232 | HOOKS[ type ].current : |
| | 233 | false; |
| | 234 | }; |
| | 235 | } |
| | 236 | |
| | 237 | |
| | 238 | |
| | 239 | /** |
| | 240 | * Checks to see if an action is currently being executed. |
| | 241 | * |
| | 242 | * @param {string} type Type of hooks to check, one of 'action' or 'filter'. |
| | 243 | * @param {string} action The name of the action to check for, if omitted will check for any action being performed. |
| | 244 | * |
| | 245 | * @return {[type]} [description] |
| | 246 | */ |
| | 247 | function createDoingHookByType( type ) { |
| | 248 | return function( action ) { |
| | 249 | |
| | 250 | // If the action was not passed, check for any current hook. |
| | 251 | if ( 'undefined' === typeof action ) { |
| | 252 | return 'undefined' !== typeof HOOKS[ type ].current; |
| | 253 | } |
| | 254 | |
| | 255 | // Return the current hook. |
| | 256 | return HOOKS[ type ] && HOOKS[ type ].current ? |
| | 257 | action === HOOKS[ type ].current : |
| | 258 | false; |
| | 259 | }; |
| | 260 | } |
| | 261 | |
| | 262 | /** |
| | 263 | * Retrieve the number of times an action is fired. |
| | 264 | * |
| | 265 | * @param {string} type Type for which hooks to check, one of 'action' or 'filter'. |
| | 266 | * @param {string} action The action to check. |
| | 267 | * |
| | 268 | * @return {[type]} [description] |
| | 269 | */ |
| | 270 | function createDidHookByType( type ) { |
| | 271 | return function( action ) { |
| | 272 | return HOOKS[ type ] && HOOKS[ type ][ action ] && HOOKS[ type ][ action ].runs ? |
| | 273 | HOOKS[ type ][ action ].runs : |
| | 274 | 0; |
| | 275 | }; |
| | 276 | } |
| | 277 | |
| | 278 | /** |
| | 279 | * Check to see if an action is registered for a hook. |
| | 280 | * |
| | 281 | * @param {string} type Type for which hooks to check, one of 'action' or 'filter'. |
| | 282 | * @param {string} action The action to check. |
| | 283 | * |
| | 284 | * @return {bool} Whether an action has been registered for a hook. |
| | 285 | */ |
| | 286 | function createHasHookByType( type ) { |
| | 287 | return function( action ) { |
| | 288 | return HOOKS[ type ] && HOOKS[ type ][ action ] ? |
| | 289 | !! HOOKS[ type ][ action ] : |
| | 290 | false; |
| | 291 | }; |
| | 292 | } |
| | 293 | |
| | 294 | /** |
| | 295 | * Remove all the actions registered to a hook, |
| | 296 | */ |
| | 297 | function createRemoveAllByType( type ) { |
| | 298 | return function( action, type ) { |
| | 299 | |
| | 300 | }; |
| | 301 | } |
| | 302 | |
| | 303 | wp.hooks = { |
| | 304 | |
| | 305 | // Remove functions, |
| | 306 | removeFilter: createRemoveHookByType( 'filters' ), |
| | 307 | removeAction: createRemoveHookByType( 'actions' ), |
| | 308 | |
| | 309 | |
| | 310 | // Do action/apply filter functions. |
| | 311 | doAction: createRunHookByType( 'actions', runDoAction ), |
| | 312 | applyFilters: createRunHookByType( 'filters', runApplyFilters ), |
| | 313 | |
| | 314 | // Add functions. |
| | 315 | addAction: createAddHookByType( 'actions' ), |
| | 316 | addFilter: createAddHookByType( 'filters' ), |
| | 317 | |
| | 318 | // Doing functions. |
| | 319 | doingAction: createDoingHookByType( 'actions' ), |
| | 320 | doingFilter: createDoingHookByType( 'filters' ), |
| | 321 | |
| | 322 | // Did functions. |
| | 323 | didAction: createDidHookByType( 'actions' ), |
| | 324 | didFilter: createDidHookByType( 'filters' ), |
| | 325 | |
| | 326 | // Has functions. |
| | 327 | hasAction: createHasHookByType( 'actions' ), |
| | 328 | hasFilter: createHasHookByType( 'filters' ), |
| | 329 | |
| | 330 | // Remove all functions. |
| | 331 | removeAllActions: createRemoveAllByType( 'actions' ), |
| | 332 | removeAllFilters: createRemoveAllByType( 'filters' ), |
| | 333 | |
| | 334 | // Current filter. |
| | 335 | currentFilter: createCurrentHookByType( 'filters' ); |
| | 336 | }; |
| | 337 | } )( window.wp = window.wp || {} ); |