Ticket #21170: 21170.5.2.diff
File 21170.5.2.diff, 11.8 KB (added by , 8 years ago) |
---|
-
new file src/wp-includes/js/wp-hooks.js
diff --git src/wp-includes/js/wp-hooks.js src/wp-includes/js/wp-hooks.js new file mode 100644 index 0000000..6d7e763
- + 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 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 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 for ( i = 0; i < handlers.length; i++ ) { 151 handlers[ i ].callback.apply( null, args ); 152 } 153 } 154 155 /** 156 * Performs a filter if it exists. 157 * 158 * @param {string} filter The filter to apply. 159 * @param {...*} args Optional args to pass to the filter. 160 * @return {*} The filtered value 161 * @private 162 */ 163 function runApplyFilters( filter, args ) { 164 var handlers, i; 165 if ( HOOKS.filters ) { 166 handlers = HOOKS.filters[ filter ]; 167 } 168 169 if ( ! handlers ) { 170 return args[ 0 ]; 171 } 172 173 for ( i = 0; i < handlers.length; i++ ) { 174 args[ 0 ] = handlers[ i ].callback.apply( null, args ); 175 } 176 177 return args[ 0 ]; 178 } 179 180 /** 181 * Use an insert sort for keeping our hooks organized based on priority. 182 * 183 * @see http://jsperf.com/javascript-sort 184 * 185 * @param {Array} hooks Array of the hooks to sort 186 * @return {Array} The sorted array 187 * @private 188 */ 189 function sortHooks( hooks ) { 190 var i, tmpHook, j, prevHook; 191 for ( i = 1; i < hooks.length; i++ ) { 192 tmpHook = hooks[ i ]; 193 j = i; 194 while ( ( prevHook = hooks[ j - 1 ] ) && prevHook.priority > tmpHook.priority ) { 195 hooks[ j ] = hooks[ j - 1 ]; 196 --j; 197 } 198 hooks[ j ] = tmpHook; 199 } 200 201 return hooks; 202 } 203 204 wp.hooks = { 205 removeFilter: createRemoveHookByType( 'filters' ), 206 applyFilters: createRunHookByType( 'filters', runApplyFilters ), 207 addFilter: createAddHookByType( 'filters' ), 208 removeAction: createRemoveHookByType( 'actions' ), 209 doAction: createRunHookByType( 'actions', runDoAction ), 210 addAction: createAddHookByType( 'actions' ) 211 }; 212 } )( window.wp = window.wp || {} ); -
src/wp-includes/script-loader.php
diff --git src/wp-includes/script-loader.php src/wp-includes/script-loader.php index b2e24d6..e0e4613 100644
function wp_default_scripts( &$scripts ) { 85 85 86 86 $scripts->add( 'wp-a11y', "/wp-includes/js/wp-a11y$suffix.js", array( 'jquery' ), false, 1 ); 87 87 88 $scripts->add( 'wp-hooks', "/wp-includes/js/wp-hooks$suffix.js", array(), false, 1 ); 89 88 90 $scripts->add( 'sack', "/wp-includes/js/tw-sack$suffix.js", array(), '1.6.1', 1 ); 89 91 90 92 $scripts->add( 'quicktags', "/wp-includes/js/quicktags$suffix.js", array(), false, 1 ); -
tests/qunit/index.html
diff --git tests/qunit/index.html tests/qunit/index.html index 0c9d820..fca4114 100644
50 50 <script src="../../src/wp-includes/js/customize-base.js"></script> 51 51 <script src="../../src/wp-includes/js/customize-models.js"></script> 52 52 <script src="../../src/wp-includes/js/shortcode.js"></script> 53 <script src="../../src/wp-includes/js/wp-hooks.js"></script> 53 54 <script src="../../src/wp-admin/js/customize-controls.js"></script> 54 55 <script src="../../src/wp-includes/js/wp-api.js"></script> 55 56 … … 70 71 <script src="wp-admin/js/customize-base.js"></script> 71 72 <script src="wp-admin/js/customize-header.js"></script> 72 73 <script src="wp-includes/js/shortcode.js"></script> 74 <script src="wp-includes/js/wp-hooks.js"></script> 73 75 <script src="wp-includes/js/wp-api.js"></script> 74 76 <script src="wp-admin/js/customize-controls.js"></script> 75 77 <script src="wp-admin/js/customize-controls-utils.js"></script> -
new file tests/qunit/wp-includes/js/wp-hooks.js
diff --git tests/qunit/wp-includes/js/wp-hooks.js tests/qunit/wp-includes/js/wp-hooks.js new file mode 100644 index 0000000..62458ef
- + 1 /* global wp */ 2 ( function( QUnit ) { 3 QUnit.module( 'wp-hooks' ); 4 5 function filter_a( str ) { 6 return str + 'a'; 7 } 8 function filter_b( str ) { 9 return str + 'b'; 10 } 11 function filter_c( str ) { 12 return str + 'c'; 13 } 14 function action_a() { 15 window.actionValue += 'a'; 16 } 17 function action_b() { 18 window.actionValue += 'b'; 19 } 20 function action_c() { 21 window.actionValue += 'c'; 22 } 23 window.actionValue = ''; 24 25 QUnit.test( 'add and remove a filter', function() { 26 expect( 1 ); 27 wp.hooks.addFilter( 'test.filter', filter_a ); 28 wp.hooks.removeFilter( 'test.filter' ); 29 equal( wp.hooks.applyFilters( 'test.filter', 'test' ), 'test' ); 30 } ); 31 32 QUnit.test( 'add a filter and run it', function() { 33 expect( 1 ); 34 wp.hooks.addFilter( 'test.filter', filter_a ); 35 equal( wp.hooks.applyFilters( 'test.filter', 'test' ), 'testa' ); 36 wp.hooks.removeFilter( 'test.filter' ); 37 } ); 38 39 QUnit.test( 'add 2 filters in a row and run them', function() { 40 expect( 1 ); 41 wp.hooks.addFilter( 'test.filter', filter_a ); 42 wp.hooks.addFilter( 'test.filter', filter_b ); 43 equal( wp.hooks.applyFilters( 'test.filter', 'test' ), 'testab' ); 44 wp.hooks.removeFilter( 'test.filter' ); 45 } ); 46 47 QUnit.test( 'add 3 filters with different priorities and run them', function() { 48 expect( 1 ); 49 wp.hooks.addFilter( 'test.filter', filter_a ); 50 wp.hooks.addFilter( 'test.filter', filter_b, 2 ); 51 wp.hooks.addFilter( 'test.filter', filter_c, 8 ); 52 equal( wp.hooks.applyFilters( 'test.filter', 'test' ), 'testbca' ); 53 wp.hooks.removeFilter( 'test.filter' ); 54 } ); 55 56 QUnit.test( 'add and remove an action', function() { 57 expect( 1 ); 58 window.actionValue = ''; 59 wp.hooks.addAction( 'test.action', action_a ); 60 wp.hooks.removeAction( 'test.action' ); 61 wp.hooks.doAction( 'test.action' ); 62 equal( window.actionValue, '' ); 63 } ); 64 65 QUnit.test( 'add an action and run it', function() { 66 expect( 1 ); 67 window.actionValue = ''; 68 wp.hooks.addAction( 'test.action', action_a ); 69 wp.hooks.doAction( 'test.action' ); 70 equal( window.actionValue, 'a' ); 71 wp.hooks.removeAction( 'test.action' ); 72 } ); 73 74 QUnit.test( 'add 2 actions in a row and then run them', function() { 75 expect( 1 ); 76 window.actionValue = ''; 77 wp.hooks.addAction( 'test.action', action_a ); 78 wp.hooks.addAction( 'test.action', action_b ); 79 wp.hooks.doAction( 'test.action' ); 80 equal( window.actionValue, 'ab' ); 81 wp.hooks.removeAction( 'test.action' ); 82 } ); 83 84 QUnit.test( 'add 3 actions with different priorities and run them', function() { 85 expect( 1 ); 86 window.actionValue = ''; 87 wp.hooks.addAction( 'test.action', action_a ); 88 wp.hooks.addAction( 'test.action', action_b, 2 ); 89 wp.hooks.addAction( 'test.action', action_c, 8 ); 90 wp.hooks.doAction( 'test.action' ); 91 equal( window.actionValue, 'bca' ); 92 wp.hooks.removeAction( 'test.action' ); 93 } ); 94 95 QUnit.test( 'pass in two arguments to an action', function() { 96 var arg1 = 10, 97 arg2 = 20; 98 99 expect( 4 ); 100 101 wp.hooks.addAction( 'test.action', function( a, b ) { 102 equal( arg1, a ); 103 equal( arg2, b ); 104 } ); 105 wp.hooks.doAction( 'test.action', arg1, arg2 ); 106 wp.hooks.removeAction( 'test.action' ); 107 108 equal( arg1, 10 ); 109 equal( arg2, 20 ); 110 } ); 111 112 QUnit.test( 'fire action multiple times', function() { 113 var func; 114 expect( 2 ); 115 116 func = function() { 117 ok( true ); 118 }; 119 120 wp.hooks.addAction( 'test.action', func ); 121 wp.hooks.doAction( 'test.action' ); 122 wp.hooks.doAction( 'test.action' ); 123 wp.hooks.removeAction( 'test.action' ); 124 } ); 125 126 QUnit.test( 'remove specific action callback', function() { 127 window.actionValue = ''; 128 wp.hooks.addAction( 'test.action', action_a ); 129 wp.hooks.addAction( 'test.action', action_b, 2 ); 130 wp.hooks.addAction( 'test.action', action_c, 8 ); 131 132 wp.hooks.removeAction( 'test.action', action_b ); 133 wp.hooks.doAction( 'test.action' ); 134 equal( window.actionValue, 'ca' ); 135 wp.hooks.removeAction( 'test.action' ); 136 } ); 137 138 QUnit.test( 'remove specific filter callback', function() { 139 wp.hooks.addFilter( 'test.filter', filter_a ); 140 wp.hooks.addFilter( 'test.filter', filter_b, 2 ); 141 wp.hooks.addFilter( 'test.filter', filter_c, 8 ); 142 143 wp.hooks.removeFilter( 'test.filter', filter_b ); 144 equal( wp.hooks.applyFilters( 'test.filter', 'test' ), 'testca' ); 145 wp.hooks.removeFilter( 'test.filter' ); 146 } ); 147 } )( window.QUnit );