-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #13 from adamsilverstein/add-wp-hooks
Add the WordPress hook library from 21170-core in preparation for beta npm release
- Loading branch information
Showing
15 changed files
with
1,061 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# WP-JS-Hooks | ||
|
||
A lightweight & efficient EventManager for JavaScript in WordPress. | ||
|
||
|
||
### API Usage | ||
API functions can be called via the global `wp.hooks` like this `wp.hooks.addAction()`, etc. | ||
|
||
* `addAction( 'hook', 'vendor/plugin/function', callback, priority )` | ||
* `addFilter( 'hook', 'vendor/plugin/function', callback, priority )` | ||
* `removeAction( 'hook', 'vendor/plugin/function' )` | ||
* `removeFilter( 'hook', 'vendor/plugin/function' )` | ||
* `removeAllActions( 'hook' )` | ||
* `removeAllFilters( 'hook' )` | ||
* `doAction( 'hook', arg1, arg2, moreArgs, finalArg )` | ||
* `applyFilters( 'hook', content, arg1, arg2, moreArgs, finalArg )` | ||
* `doingAction( 'hook' )` | ||
* `doingFilter( 'hook' )` | ||
* `didAction( 'hook' )` | ||
* `didFilter( 'hook' )` | ||
* `hasAction( 'hook' )` | ||
* `hasFilter( 'hook' )` | ||
|
||
|
||
### Background | ||
See ticket [#21170](http://core.trac.wordpress.org/ticket/21170) for more information. | ||
|
||
|
||
### Features | ||
|
||
* Fast and lightweight. | ||
* Priorities system ensures hooks with lower integer priority are fired first. | ||
* Uses native object hash lookup for finding hook callbacks. | ||
* Utilizes insertion sort for keeping priorities correct. Best Case: O(n), worst case: O(n^2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"name": "@wordpress/hooks", | ||
"version": "0.1.0", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/WordPress/packages.git" | ||
}, | ||
"description": "WordPress Hooks library", | ||
"main": "build/index.js", | ||
"module": "build-module/index.js", | ||
"browser": "build-browser/index.js", | ||
"author": "WordPress", | ||
"license": "GPL-2.0+" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import validateNamespace from './validateNamespace.js'; | ||
import validateHookName from './validateHookName.js'; | ||
|
||
/** | ||
* Returns a function which, when invoked, will add a hook. | ||
* | ||
* @param {Object} hooks Stored hooks, keyed by hook name. | ||
* | ||
* @return {Function} Function that adds a new hook. | ||
*/ | ||
function createAddHook( hooks ) { | ||
/** | ||
* Adds the hook to the appropriate hooks container. | ||
* | ||
* @param {string} hookName Name of hook to add | ||
* @param {string} namespace The unique namespace identifying the callback in the form `vendor/plugin/function`. | ||
* @param {Function} callback Function to call when the hook is run | ||
* @param {?number} priority Priority of this hook (default=10) | ||
*/ | ||
return function addHook( hookName, namespace, callback, priority = 10 ) { | ||
|
||
if ( ! validateHookName( hookName ) ) { | ||
return; | ||
} | ||
|
||
if ( ! validateNamespace( namespace ) ) { | ||
return; | ||
} | ||
|
||
if ( 'function' !== typeof callback ) { | ||
console.error( 'The hook callback must be a function.' ); | ||
return; | ||
} | ||
|
||
// Validate numeric priority | ||
if ( 'number' !== typeof priority ) { | ||
console.error( 'If specified, the hook priority must be a number.' ); | ||
return; | ||
} | ||
|
||
const handler = { callback, priority, namespace }; | ||
|
||
if ( hooks.hasOwnProperty( hookName ) ) { | ||
// Find the correct insert index of the new hook. | ||
const handlers = hooks[ hookName ].handlers; | ||
let i = 0; | ||
while ( i < handlers.length ) { | ||
if ( handlers[ i ].priority > priority ) { | ||
break; | ||
} | ||
i++; | ||
} | ||
// Insert (or append) the new hook. | ||
handlers.splice( i, 0, handler ); | ||
// We may also be currently executing this hook. If the callback | ||
// we're adding would come after the current callback, there's no | ||
// problem; otherwise we need to increase the execution index of | ||
// any other runs by 1 to account for the added element. | ||
( hooks.__current || [] ).forEach( hookInfo => { | ||
if ( hookInfo.name === hookName && hookInfo.currentIndex >= i ) { | ||
hookInfo.currentIndex++; | ||
} | ||
} ); | ||
} else { | ||
// This is the first hook of its type. | ||
hooks[ hookName ] = { | ||
handlers: [ handler ], | ||
runs: 0, | ||
}; | ||
} | ||
}; | ||
} | ||
|
||
export default createAddHook; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/** | ||
* Returns a function which, when invoked, will return the name of the | ||
* currently running hook, or `null` if no hook of the given type is currently | ||
* running. | ||
* | ||
* @param {Object} hooks Stored hooks, keyed by hook name. | ||
* | ||
* @return {Function} Function that returns the current hook. | ||
*/ | ||
function createCurrentHook( hooks, returnFirstArg ) { | ||
/** | ||
* Returns the name of the currently running hook, or `null` if no hook of | ||
* the given type is currently running. | ||
* | ||
* @return {?string} The name of the currently running hook, or | ||
* `null` if no hook is currently running. | ||
*/ | ||
return function currentHook() { | ||
if ( ! hooks.__current || ! hooks.__current.length ) { | ||
return null; | ||
} | ||
|
||
return hooks.__current[ hooks.__current.length - 1 ].name; | ||
}; | ||
} | ||
|
||
export default createCurrentHook; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import validateHookName from './validateHookName.js'; | ||
|
||
/** | ||
* Returns a function which, when invoked, will return the number of times a | ||
* hook has been called. | ||
* | ||
* @param {Object} hooks Stored hooks, keyed by hook name. | ||
* | ||
* @return {Function} Function that returns a hook's call count. | ||
*/ | ||
function createDidHook( hooks ) { | ||
/** | ||
* Returns the number of times an action has been fired. | ||
* | ||
* @param {string} hookName The hook name to check. | ||
* | ||
* @return {number} The number of times the hook has run. | ||
*/ | ||
return function didHook( hookName ) { | ||
|
||
if ( ! validateHookName( hookName ) ) { | ||
return; | ||
} | ||
|
||
return hooks.hasOwnProperty( hookName ) && hooks[ hookName ].runs | ||
? hooks[ hookName ].runs | ||
: 0; | ||
}; | ||
} | ||
|
||
export default createDidHook; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/** | ||
* Returns a function which, when invoked, will return whether a hook is | ||
* currently being executed. | ||
* | ||
* @param {Object} hooks Stored hooks, keyed by hook name. | ||
* | ||
* @return {Function} Function that returns whether a hook is currently | ||
* being executed. | ||
*/ | ||
function createDoingHook( hooks ) { | ||
/** | ||
* Returns whether a hook is currently being executed. | ||
* | ||
* @param {?string} hookName The name of the hook to check for. If | ||
* omitted, will check for any hook being executed. | ||
* | ||
* @return {bool} Whether the hook is being executed. | ||
*/ | ||
return function doingHook( hookName ) { | ||
// If the hookName was not passed, check for any current hook. | ||
if ( 'undefined' === typeof hookName ) { | ||
return 'undefined' !== typeof hooks.__current[0]; | ||
} | ||
|
||
// Return the __current hook. | ||
return hooks.__current[0] | ||
? hookName === hooks.__current[0].name | ||
: false; | ||
}; | ||
} | ||
|
||
export default createDoingHook; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/** | ||
* Returns a function which, when invoked, will return whether any handlers are | ||
* attached to a particular hook. | ||
* | ||
* @param {Object} hooks Stored hooks, keyed by hook name. | ||
* | ||
* @return {Function} Function that returns whether any handlers are | ||
* attached to a particular hook. | ||
*/ | ||
function createHasHook( hooks ) { | ||
/** | ||
* Returns how many handlers are attached for the given hook. | ||
* | ||
* @param {string} hookName The name of the hook to check for. | ||
* | ||
* @return {number} The number of handlers that are attached to | ||
* the given hook. | ||
*/ | ||
return function hasHook( hookName ) { | ||
return hooks.hasOwnProperty( hookName ) | ||
? hooks[ hookName ].handlers.length | ||
: 0; | ||
}; | ||
} | ||
|
||
export default createHasHook; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import validateNamespace from './validateNamespace.js'; | ||
import validateHookName from './validateHookName.js'; | ||
|
||
/** | ||
* Returns a function which, when invoked, will remove a specified hook or all | ||
* hooks by the given name. | ||
* | ||
* @param {Object} hooks Stored hooks, keyed by hook name. | ||
* @param {bool} removeAll Whether to remove all callbacks for a hookName, without regard to namespace. Used to create `removeAll*` functions. | ||
* | ||
* @return {Function} Function that removes hooks. | ||
*/ | ||
function createRemoveHook( hooks, removeAll ) { | ||
/** | ||
* Removes the specified callback (or all callbacks) from the hook with a | ||
* given hookName and namespace. | ||
* | ||
* @param {string} hookName The name of the hook to modify. | ||
* @param {string} namespace The unique namespace identifying the callback in the form `vendor/plugin/function`. | ||
* | ||
* @return {number} The number of callbacks removed. | ||
*/ | ||
return function removeHook( hookName, namespace ) { | ||
|
||
if ( ! validateHookName( hookName ) ) { | ||
return; | ||
} | ||
|
||
if ( ! removeAll && ! validateNamespace( namespace ) ) { | ||
return; | ||
} | ||
|
||
// Bail if no hooks exist by this name | ||
if ( ! hooks.hasOwnProperty( hookName ) ) { | ||
return 0; | ||
} | ||
|
||
let handlersRemoved = 0; | ||
|
||
if ( removeAll ) { | ||
handlersRemoved = hooks[ hookName ].handlers.length; | ||
hooks[ hookName ] = { | ||
runs: hooks[ hookName ].runs, | ||
handlers: [], | ||
}; | ||
} else { | ||
// Try to find the specified callback to remove. | ||
const handlers = hooks[ hookName ].handlers; | ||
for ( let i = handlers.length - 1; i >= 0; i-- ) { | ||
if ( | ||
handlers[ i ].namespace === namespace | ||
) { | ||
handlers.splice( i, 1 ); | ||
handlersRemoved++; | ||
// This callback may also be part of a hook that is | ||
// currently executing. If the callback we're removing | ||
// comes after the current callback, there's no problem; | ||
// otherwise we need to decrease the execution index of any | ||
// other runs by 1 to account for the removed element. | ||
( hooks.__current || [] ).forEach( hookInfo => { | ||
if ( hookInfo.name === hookName && hookInfo.currentIndex >= i ) { | ||
hookInfo.currentIndex--; | ||
} | ||
} ); | ||
} | ||
} | ||
} | ||
|
||
return handlersRemoved; | ||
}; | ||
} | ||
|
||
export default createRemoveHook; |
Oops, something went wrong.