| /* Copyright 2013 Twitter, Inc. Licensed under The MIT License. http://opensource.org/licenses/MIT */ |
| |
| define( |
| |
| [ |
| './advice', |
| './utils', |
| './compose', |
| './base', |
| './registry', |
| './logger', |
| './debug' |
| ], |
| |
| function(advice, utils, compose, withBase, registry, withLogging, debug) { |
| 'use strict'; |
| |
| var functionNameRegEx = /function (.*?)\s?\(/; |
| |
| // teardown for all instances of this constructor |
| function teardownAll() { |
| var componentInfo = registry.findComponentInfo(this); |
| |
| componentInfo && Object.keys(componentInfo.instances).forEach(function(k) { |
| var info = componentInfo.instances[k]; |
| // It's possible that a previous teardown caused another component to teardown, |
| // so we can't assume that the instances object is as it was. |
| if (info && info.instance) { |
| info.instance.teardown(); |
| } |
| }); |
| } |
| |
| function attachTo(selector/*, options args */) { |
| // unpacking arguments by hand benchmarked faster |
| var l = arguments.length; |
| var args = new Array(l - 1); |
| for (var i = 1; i < l; i++) { |
| args[i - 1] = arguments[i]; |
| } |
| |
| if (!selector) { |
| throw new Error('Component needs to be attachTo\'d a jQuery object, native node or selector string'); |
| } |
| |
| var options = utils.merge.apply(utils, args); |
| var componentInfo = registry.findComponentInfo(this); |
| |
| $(selector).each(function(i, node) { |
| if (componentInfo && componentInfo.isAttachedTo(node)) { |
| // already attached |
| return; |
| } |
| |
| (new this).initialize(node, options); |
| }.bind(this)); |
| } |
| |
| function prettyPrintMixins() { |
| //could be called from constructor or constructor.prototype |
| var mixedIn = this.mixedIn || this.prototype.mixedIn || []; |
| return mixedIn.map(function(mixin) { |
| if (mixin.name == null) { |
| // function name property not supported by this browser, use regex |
| var m = mixin.toString().match(functionNameRegEx); |
| return (m && m[1]) ? m[1] : ''; |
| } else { |
| return (mixin.name != 'withBase') ? mixin.name : ''; |
| } |
| }).filter(Boolean).join(', '); |
| }; |
| |
| |
| // define the constructor for a custom component type |
| // takes an unlimited number of mixin functions as arguments |
| // typical api call with 3 mixins: define(timeline, withTweetCapability, withScrollCapability); |
| function define(/*mixins*/) { |
| // unpacking arguments by hand benchmarked faster |
| var l = arguments.length; |
| var mixins = new Array(l); |
| for (var i = 0; i < l; i++) { |
| mixins[i] = arguments[i]; |
| } |
| |
| var Component = function() {}; |
| |
| Component.toString = Component.prototype.toString = prettyPrintMixins; |
| if (debug.enabled) { |
| Component.describe = Component.prototype.describe = Component.toString(); |
| } |
| |
| // 'options' is optional hash to be merged with 'defaults' in the component definition |
| Component.attachTo = attachTo; |
| // enables extension of existing "base" Components |
| Component.mixin = function() { |
| var newComponent = define(); //TODO: fix pretty print |
| var newPrototype = Object.create(Component.prototype); |
| newPrototype.mixedIn = [].concat(Component.prototype.mixedIn); |
| newPrototype.defaults = utils.merge(Component.prototype.defaults); |
| newPrototype.attrDef = Component.prototype.attrDef; |
| compose.mixin(newPrototype, arguments); |
| newComponent.prototype = newPrototype; |
| newComponent.prototype.constructor = newComponent; |
| return newComponent; |
| }; |
| Component.teardownAll = teardownAll; |
| |
| // prepend common mixins to supplied list, then mixin all flavors |
| if (debug.enabled) { |
| mixins.unshift(withLogging); |
| } |
| mixins.unshift(withBase, advice.withAdvice, registry.withRegistration); |
| compose.mixin(Component.prototype, mixins); |
| |
| return Component; |
| } |
| |
| define.teardownAll = function() { |
| registry.components.slice().forEach(function(c) { |
| c.component.teardownAll(); |
| }); |
| registry.reset(); |
| }; |
| |
| return define; |
| } |
| ); |