Skip to content
Browse files

add files to repo

  • Loading branch information...
1 parent b3655de commit 4b3c80f250e2eb07173f9d9d303f80fca90200bf @ryanve committed Sep 3, 2012
Showing with 360 additions and 1 deletion.
  1. +1 −1 README.md
  2. +353 −0 hook.js
  3. +6 −0 hook.min.js
View
2 README.md
@@ -1,4 +1,4 @@
-hook
+[hook](https://github.com/ryanve/hook)
====
remixable hook/bridge/relay API designed for writing highly-extendable modular JavaScript
View
353 hook.js
@@ -0,0 +1,353 @@
+/*!
+ * hook remixable hook/bridge/relay API designed for
+ * writing highly-extendable modular JavaScript
+ * @author Ryan Van Etten (c) 2012
+ * @link http://github.com/ryanve/hook
+ * @license MIT
+ * @version 0.x
+ */
+
+/*jslint browser: true, devel: true, node: true, passfail: false, bitwise: true
+, continue: true, debug: true, eqeq: true, es5: true, forin: true, newcap: true
+, nomen: true, plusplus: true, regexp: true, undef: true, sloppy: true, stupid: true
+, sub: true, white: true, indent: 4, maxerr: 180 */
+
+(function (root, name, factory) {// separate the module logic from its definition ( @ded pattern ;)
+ if (typeof module != 'undefined' && module['exports']) { module['exports'] = factory(); } // node
+ else { root[name] = root[name] || factory(); } // browser
+}(this, 'hook', function () {
+
+ var hook // the export
+ , root = this || window
+ , OP = Object.prototype
+ , owns = OP.hasOwnProperty
+
+ , gnd = function (o) {// for grounding (securing) scope
+ return o == null || o === root ? 0 : o;
+ }
+
+ , ES5lineage = (function ( Object ) {
+ // Feature test: check that Object.create exists and
+ // that it properly implements the full capabilities
+ // of the first param. (Does not test the 2nd param.)
+ // Read @link github.com/kriskowal/es5-shim/pull/118
+ var k = 'k', n, o;
+ function fun () {}
+ fun[k] = 1;
+ try {
+ o = Object.create(null);
+ for ( n in o ) { return false; }
+ if ( Object.getPrototypeOf(o) !== null ) { return false; }
+ o = Object.create(fun);
+ return !!o && 1 === o[k];
+ } catch (e){ return false; }
+ }( Object ))
+
+ , pro = (ES5lineage && Object.getPrototypeOf) || function (ob) {
+ // `Object.getPrototypeOf` fallback
+ if ( void 0 !== ob["__proto__"] ) { return ob["__proto__"]; }
+ return ob.constructor ? ob.constructor.prototype : OP;
+ }
+
+ /**
+ * Create a new empty object whose prototype is set to the supplied arg.
+ * Uses to native Object.create when possible. The fallback supports
+ * the *first arg* only. Adapted from @link github.com/kriskowal/es5-shim
+ * @link bit.ly/mdn-object-create
+ * @link github.com/kriskowal/es5-shim/pull/118
+ * @param {Object|Function|Array|null} parent (typeof "object"|"function")
+ * @return {Object} is an empty object that inherits properties from `parent`
+ */
+ , nu = (ES5lineage && Object.create) || function (parent) {
+ /** @constructor */
+ function F () {} // An empty constructor.
+ var object; // Becomes an `instanceof F`
+ if (null === parent) { return { "__proto__": null }; }
+ F.prototype = parent; // Set F's prototype to the parent Object
+ object = new F; // Get an empty object (instance) that inherits `parent`
+ object["__proto__"] = parent; // ensure `Object.getPrototypeOf` will work in IE
+ return object;
+ }
+
+ ;
+
+ /**
+ * Make new empty object w/ same proto as the `source`. Then
+ * mixin the owned props from the `source` into the new object.
+ * Quasi deep clone (done via inheritance)
+ * @param {(Object|Function|null)=} source
+ * @param {(Object|Function|null)=} opt_parent
+ * @return {Object}
+ */
+ function resample (source, opt_parent) {
+ var n, r;
+ if ( true === source || !arguments.length ) { source = this; }
+ if ( void 0 === opt_parent ) { opt_parent = pro(source); }
+ r = nu( opt_parent );
+ for ( n in source ) {
+ // owned props are enumerated first so stop when unowned
+ if ( !owns.call(source, n) ) { break; }
+ r[n] = source[n];
+ }
+ return r;
+ }
+
+ /**
+ * Multi-purpose extender/augmenter, with simple yet very useful options, called
+ * expand b/c many libs implement extend/augment methods, so this makes it easier
+ * to integrate ( and drop preconceived notions about what it should do ).
+ * Expand your mind ;]
+ *
+ * @param {(Object|Function)} receiver
+ * @param {(Object|Function)} supplier
+ * @param {boolean=} force whether to overwrite existing props
+ * true ==> no typechecking is done.
+ * false ==> receiver[n] must be null|undefined.
+ * supplier[n] must NOT be null|undefined.
+ * @param {boolean|Function=} check whether the prop must be owned (or a custom test)
+ */
+ function expand (receiver, supplier, force, check) {
+ var n;
+ if ( null == receiver ) { throw new TypeError('@expand'); }
+ if ( null == supplier ) { return receiver; }
+ force = force === true;
+ check = check === true ? owns : check;
+ for ( n in supplier ) {
+ if ( force || (receiver[n] == null && supplier[n] != null) ) {
+ if ( !check || check.call(supplier, n) ) {
+ receiver[n] = supplier[n];
+ }
+ }
+ }
+ return receiver;
+ }
+
+ /**
+ * Mixin owned enumerable props from the `supplier` into `this`.
+ * @this {(Object|Function)} is the receiver
+ * @param {(Object|Function)} supplier is the supplier
+ * @param {boolean=} force whether to overwrite existing props
+ */
+ function mixin ( supplier, force ) {
+ if ( !gnd(this) ) { throw new TypeError('@mixin'); }
+ return expand(this, supplier, force, true);
+ }
+
+ /**
+ * bridge() Integrate applicable methods|objects into a host.
+ * Other types (number|string|undefined|boolean|null)
+ * are not bridged. `this` augments the receiver `r`
+ * The bridge() is mainly designed for merging jQuery-like
+ * modules, thus `.fn` properties are bridged one level deep
+ *
+ * method|objects whose `.relay` property is set to `false` get
+ * skipped. If the `.relay` property is a function, then it is
+ * fired with `this` being the method|object and the first arg
+ * being the top-level scope (e.g. $ function) of the receiving
+ * api. This provides a way for the method|object to be adapted to
+ * the receiving api. If the result of the .relay function is a truthy
+ * value (such as new function) then that value will be transferred
+ * instead of the original. If the relay returns `false` then the
+ * method|object is skipped. If it returns any other falsey value or a
+ * type other than the original then the transferred method will default
+ * back to the orig. In effect, the `.relay` property defaults to `true`.
+ * It is not necessary to define a `.relay` property for methods|objects that
+ * are to be transferred as is. See the loop for the full relay signature.
+ *
+ * @this {Object|Function} supplier
+ * @param {Object|Function} r receiver
+ * @param {boolean=} force whether to overwrite existing props (default: false)
+ * @param {(Object|Function)=} $ the top-level of the host api (default: `r`)
+ */
+ function bridge ( r, force, $ ) {
+
+ var v, k, relay, s = this; // s is the supplier
+ if ( r == null || !gnd(s) ) { return; }
+ force = true === force; // require explicit true to force
+ $ = typeof $ == 'function' || (typeof $ == 'object' && $) ? $ : r;
+
+ for ( k in s ) {
+ v = s[k];
+ if ( typeof v == 'function' || typeof v == 'object' && v ) {
+ if ( 'fn' === k && v !== s ) {
+ // 2nd check above prevents infinite loop
+ // from `.fn` having ref to self on it.
+ bridge.call(v, r[k], force, $);
+ } else if ( force ? r[k] !== r && r[k] !== $ : r[k] == null ) {
+ // The check above prevents overwriting receiver's refs to self.
+ // Next, check for a relay prop:
+ relay = v['relay'];
+ if ( typeof relay == 'function' ) {
+ // Fire relay functions. I haven't fully solidified
+ // the relay call sig. This passes the essentials:
+ relay = relay.call(v, $, r[k]);
+ }
+ if ( relay !== false ) {// Provides a way to bypass non-agnostic props.
+ // Transfer the value. Relayed versions must match orig type
+ // to pass. Otherwise default to the orig supplier value:
+ r[k] = typeof relay == typeof v ? relay || v : v;
+ }
+ }
+ }
+ }
+ return r; // receiver
+
+ }// bridge
+
+ // signify that this bridge() is module agnostic
+ bridge['relay'] = true;
+
+ /* = = = = alt version = = = = = = = = = = = = = = = = = = = = = = = = =
+ We could use expand() to make bridge() but opted for the standalone
+ loop above for now cause it's a bit easier to follow and takes basically
+ the same amount of code. Using expand it'd be like:
+
+ function bridge ( r, force, $ ) {
+
+ var s = this; // s is the supplier
+ if ( r == null || !gnd(s) ) { return; }
+ $ = typeof $ == 'function' || typeof $ == 'object' && $ ? $ : r;
+
+ return expand (r, s, force, function (k) {
+ var relay, v = s[k]; // `s === this`
+ if ( !v || typeof v != 'function' && typeof v != 'object' ) { return; }
+ if ( 'fn' === k && v !== s ) {
+ // 2nd check above prevents infinite loop
+ // from `.fn` having ref to self on it.
+ bridge.call(v, r[k], force, $);
+ } else if ( r[k] !== r && r[k] !== $ ) {
+ // The check above prevents overwriting receiver's refs to self.
+ // Next, check for a relay prop:
+ relay = v['relay'];
+ if ( typeof relay == 'function' ) {
+ // Fire relay functions. I haven't fully solidified
+ // the relay call sig. This passes the essentials:
+ relay = relay.call(v, $, r[k]);
+ }
+ if ( relay !== false ) {// Provides a way to bypass non-agnostic props.
+ // Transfer the value. Relayed versions must match orig type
+ // to pass. Otherwise default to the orig supplier value:
+ r[k] = typeof relay == typeof v ? relay || v : v;
+ }
+ }
+ });
+
+ }// bridge
+
+ // signify that this bridge() is module agnostic
+ // bridge['relay'] = true;
+ = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
+
+ /**
+ * Create a new hook() function tied to a clean hash.
+ * @return {Function}
+ */
+ function hookRemix () {
+
+ // Use objects that inherit null for hashes so that we don't need to
+ // test hasOwnProperty on "gets".
+ // Sidenode: ES5's `Object.create(null)` returns `{"__proto__": null}`
+ var defs = nu(null) // defaults' hash
+ , curr = nu(null) // currents' hash
+ , info = nu(null); // status hash (burned hooks cannot be updated)
+
+ /**
+ * Method for setting/getting hooks
+ * @param {*=} k key
+ * @param {*=} v value
+ */
+ function hook (k, v) {
+
+ var n, parlay, prefix, clone;
+ k = typeof k == 'function' ? k.call(this, hook()) : k;
+
+ // optimize for simple usage (esp. GET-simple)
+
+ if ( typeof k == 'string' || typeof k == 'number' ) {
+ if ( void 0 === v ) {
+ return curr[k]; // GET-simple
+ }
+ if ( info ) { // only SET if 'BURN all' has not occured
+ if ( typeof v == 'function' ) {// SET-simple
+ if ( info[k] !== false ) {// update unless burned
+ curr[k] = v;
+ defs[k] = defs[k] || v;
+ }
+ } else if ( v === false ) {// burn it (lock it) at its current state
+ info[k] = v; // false
+ } else if ( v === true ) { // restore default (possible even if burned)
+ curr[k] = defs[k];
+ }
+ }
+ if ( null === v ) {
+ return info[k] !== false; // status
+ }
+
+ return gnd(this) || curr[k]; // curr value
+ }
+
+ if ( typeof k == 'boolean' ) {
+ if ( !k ) {// BURN all (false)
+ info = defs = null; // (nullify both to free up memory)
+ } else if ( info ) {// RESTORE defaults (true) (if 'BURN all' has not occured)
+ curr = nu(null);
+ for ( n in defs ) {
+ curr[n] = defs[n];
+ }
+ }
+ k = void 0; // reset `k` so it parlays into the GET-all block
+ }
+
+ if ( void 0 === k ) { // GET-all
+ clone = nu(null);
+ for ( n in curr ) {
+ clone[n] = curr[n];
+ }
+ return clone;
+ }
+
+ if ( null === k ) { // GET-status-all
+ return !!info;
+ }
+
+ // `k` must be "object" if we get to here
+ if ( info ) {// SET-multi
+ prefix = typeof v == 'string' ? v : '';
+ parlay = typeof v == 'boolean';
+ for ( n in k ) {
+ hook(prefix + n, k[n]); // set each
+ parlay && hook(prefix + n, v);
+ }
+ }
+
+ return gnd(this) || void 0;
+
+ }// hook
+
+
+ // Allow an existing `hook()` to be bridged to another module so that
+ // they can share hooks. (In other words, don't set its relay to false.)
+ // Provide the remix method as a way to explicitly redefine it:
+ hook['remix'] = hookRemix;
+
+ // Include refs to the full api:
+ hook['hook'] = hook; // self ref
+ hook['bridge'] = bridge;
+ hook['pro'] = pro;
+ hook['nu'] = nu;
+ hook['mixin'] = mixin;
+ hook['owns'] = owns;
+ hook['resample'] = resample;
+ hook['expand'] = expand;
+
+ return hook;
+
+ }// hookRemix
+
+ // build the export:
+ hook = hookRemix();
+
+ // return the export:
+ return hook;
+
+}));
View
6 hook.min.js
@@ -0,0 +1,6 @@
+/*! hook 0.x | github.com/ryanve/hook | MIT License */
+(function(i,g,k){"undefined"!=typeof module&&module.exports?module.exports=k():i[g]=i[g]||k()})(this,"hook",function(){function i(a,e){var d,c;if(!0===a||!arguments.length)a=this;void 0===e&&(e=o(a));c=j(e);for(d in a){if(!m.call(a,d))break;c[d]=a[d]}return c}function g(a,e,d,c){var b;if(null==a)throw new TypeError("@expand");if(null==e)return a;d=!0===d;c=!0===c?m:c;for(b in e)if(d||null==a[b]&&null!=e[b])if(!c||c.call(e,b))a[b]=e[b];return a}function k(a,e){if(!l(this))throw new TypeError("@mixin");
+return g(this,a,e,!0)}function n(a,e,d){var c,b,f;if(null!=a&&l(this)){e=!0===e;d="function"==typeof d||"object"==typeof d&&d?d:a;for(b in this)if(c=this[b],"function"==typeof c||"object"==typeof c&&c)if("fn"===b&&c!==this)n.call(c,a[b],e,d);else if(e?a[b]!==a&&a[b]!==d:null==a[b])f=c.relay,"function"==typeof f&&(f=f.call(c,d,a[b])),!1!==f&&(a[b]=typeof f==typeof c?f||c:c);return a}}function p(){function a(b,f){var h,g,i,b="function"==typeof b?b.call(this,a()):b;if("string"==typeof b||"number"==typeof b){if(void 0===
+f)return d[b];c&&("function"==typeof f?!1!==c[b]&&(d[b]=f,e[b]=e[b]||f):!1===f?c[b]=f:!0===f&&(d[b]=e[b]));return null===f?!1!==c[b]:l(this)||d[b]}if("boolean"==typeof b){if(b){if(c)for(h in d=j(null),e)d[h]=e[h]}else c=e=null;b=void 0}if(void 0===b){g=j(null);for(h in d)g[h]=d[h];return g}if(null===b)return!!c;if(c)for(h in i="string"==typeof f?f:"",g="boolean"==typeof f,b)a(i+h,b[h]),g&&a(i+h,f);return l(this)||void 0}var e=j(null),d=j(null),c=j(null);a.remix=p;a.hook=a;a.bridge=n;a.pro=o;a.nu=
+j;a.mixin=k;a.owns=m;a.resample=i;a.expand=g;return a}var s=this||window,q=Object.prototype,m=q.hasOwnProperty,l=function(a){return null==a||a===s?0:a},r=function(a){function e(){}var d,c;e.k=1;try{c=a.create(null);for(d in c)return!1;if(null!==a.getPrototypeOf(c))return!1;c=a.create(e);return!!c&&1===c.k}catch(b){return!1}}(Object),o=r&&Object.getPrototypeOf||function(a){return void 0!==a.__proto__?a.__proto__:a.constructor?a.constructor.prototype:q},j=r&&Object.create||function(a){function e(){}
+var d;if(null===a)return{__proto__:null};e.prototype=a;d=new e;d.__proto__=a;return d};n.relay=!0;return p()});

0 comments on commit 4b3c80f

Please sign in to comment.
Something went wrong with that request. Please try again.