summaryrefslogtreecommitdiffstats
path: root/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib')
-rw-r--r--third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/advice.js69
-rw-r--r--third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/component.js308
-rw-r--r--third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/compose.js86
-rw-r--r--third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/index.js30
-rw-r--r--third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/logger.js93
-rw-r--r--third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/registry.js220
-rw-r--r--third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/utils.js236
7 files changed, 1042 insertions, 0 deletions
diff --git a/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/advice.js b/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/advice.js
new file mode 100644
index 0000000000..0f4686929a
--- /dev/null
+++ b/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/advice.js
@@ -0,0 +1,69 @@
+// ==========================================
+// Copyright 2013 Twitter, Inc
+// Licensed under The MIT License
+// http://opensource.org/licenses/MIT
+// ==========================================
+
+"use strict";
+
+define(
+
+ [
+ './utils',
+ './compose'
+ ],
+
+ function (util, compose) {
+
+ var advice = {
+
+ around: function(base, wrapped) {
+ return function composedAround() {
+ // unpacking arguments by hand benchmarked faster
+ var i = 0, l = arguments.length, args = new Array(l + 1);
+ args[0] = base.bind(this);
+ for (; i < l; i++) args[i + 1] = arguments[i];
+
+ return wrapped.apply(this, args);
+ }
+ },
+
+ before: function(base, before) {
+ var beforeFn = (typeof before == 'function') ? before : before.obj[before.fnName];
+ return function composedBefore() {
+ beforeFn.apply(this, arguments);
+ return base.apply(this, arguments);
+ }
+ },
+
+ after: function(base, after) {
+ var afterFn = (typeof after == 'function') ? after : after.obj[after.fnName];
+ return function composedAfter() {
+ var res = (base.unbound || base).apply(this, arguments);
+ afterFn.apply(this, arguments);
+ return res;
+ }
+ },
+
+ // a mixin that allows other mixins to augment existing functions by adding additional
+ // code before, after or around.
+ withAdvice: function() {
+ ['before', 'after', 'around'].forEach(function(m) {
+ this[m] = function(method, fn) {
+
+ compose.unlockProperty(this, method, function() {
+ if (typeof this[method] == 'function') {
+ return this[method] = advice[m](this[method], fn);
+ } else {
+ return this[method] = fn;
+ }
+ });
+
+ };
+ }, this);
+ }
+ };
+
+ return advice;
+ }
+);
diff --git a/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/component.js b/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/component.js
new file mode 100644
index 0000000000..d9a5077ac9
--- /dev/null
+++ b/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/component.js
@@ -0,0 +1,308 @@
+// ==========================================
+// Copyright 2013 Twitter, Inc
+// Licensed under The MIT License
+// http://opensource.org/licenses/MIT
+// ==========================================
+
+"use strict";
+
+define(
+
+ [
+ './advice',
+ './utils',
+ './compose',
+ './registry'
+ ],
+
+ function(advice, utils, compose, registry) {
+
+ var functionNameRegEx = /function (.*?)\s?\(/;
+ var componentId = 0;
+
+ function teardownInstance(instanceInfo){
+ instanceInfo.events.slice().forEach(function(event) {
+ var args = [event.type];
+
+ event.element && args.unshift(event.element);
+ (typeof event.callback == 'function') && args.push(event.callback);
+
+ this.off.apply(this, args);
+ }, instanceInfo.instance);
+ }
+
+
+ function teardown() {
+ teardownInstance(registry.findInstanceInfo(this));
+ }
+
+ //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];
+ info.instance.teardown();
+ });
+ }
+
+ function checkSerializable(type, data) {
+ try {
+ window.postMessage(data, '*');
+ } catch(e) {
+ console.log('unserializable data for event',type,':',data);
+ throw new Error(
+ ["The event", type, "on component", this.toString(), "was triggered with non-serializable data"].join(" ")
+ );
+ }
+ }
+
+ //common mixin allocates basic functionality - used by all component prototypes
+ //callback context is bound to component
+ function withBaseComponent() {
+
+ // delegate trigger, bind and unbind to an element
+ // if $element not supplied, use component's node
+ // other arguments are passed on
+ // event can be either a string specifying the type
+ // of the event, or a hash specifying both the type
+ // and a default function to be called.
+ this.trigger = function() {
+ var $element, type, data, event, defaultFn;
+ var lastIndex = arguments.length - 1, lastArg = arguments[lastIndex];
+
+ if (typeof lastArg != "string" && !(lastArg && lastArg.defaultBehavior)) {
+ lastIndex--;
+ data = lastArg;
+ }
+
+ if (lastIndex == 1) {
+ $element = $(arguments[0]);
+ event = arguments[1];
+ } else {
+ $element = this.$node;
+ event = arguments[0];
+ }
+
+ if (event.defaultBehavior) {
+ defaultFn = event.defaultBehavior;
+ event = $.Event(event.type);
+ }
+
+ type = event.type || event;
+
+ if (window.DEBUG && window.DEBUG.enabled && window.postMessage) {
+ checkSerializable.call(this, type, data);
+ }
+
+ if (typeof this.attr.eventData === 'object') {
+ data = $.extend(true, {}, this.attr.eventData, data);
+ }
+
+ $element.trigger((event || type), data);
+
+ if (defaultFn && !event.isDefaultPrevented()) {
+ (this[defaultFn] || defaultFn).call(this);
+ }
+
+ return $element;
+ };
+
+ this.on = function() {
+ var $element, type, callback, originalCb;
+ var lastIndex = arguments.length - 1, origin = arguments[lastIndex];
+
+ if (typeof origin == "object") {
+ //delegate callback
+ originalCb = utils.delegate(
+ this.resolveDelegateRules(origin)
+ );
+ } else {
+ originalCb = origin;
+ }
+
+ if (lastIndex == 2) {
+ $element = $(arguments[0]);
+ type = arguments[1];
+ } else {
+ $element = this.$node;
+ type = arguments[0];
+ }
+
+ if (typeof originalCb != 'function' && typeof originalCb != 'object') {
+ throw new Error("Unable to bind to '" + type + "' because the given callback is not a function or an object");
+ }
+
+ callback = originalCb.bind(this);
+ callback.target = originalCb;
+
+ // if the original callback is already branded by jQuery's guid, copy it to the context-bound version
+ if (originalCb.guid) {
+ callback.guid = originalCb.guid;
+ }
+
+ $element.on(type, callback);
+
+ // get jquery's guid from our bound fn, so unbinding will work
+ originalCb.guid = callback.guid;
+
+ return callback;
+ };
+
+ this.off = function() {
+ var $element, type, callback;
+ var lastIndex = arguments.length - 1;
+
+ if (typeof arguments[lastIndex] == "function") {
+ callback = arguments[lastIndex];
+ lastIndex -= 1;
+ }
+
+ if (lastIndex == 1) {
+ $element = $(arguments[0]);
+ type = arguments[1];
+ } else {
+ $element = this.$node;
+ type = arguments[0];
+ }
+
+ return $element.off(type, callback);
+ };
+
+ this.resolveDelegateRules = function(ruleInfo) {
+ var rules = {};
+
+ Object.keys(ruleInfo).forEach(function(r) {
+ if (!r in this.attr) {
+ throw new Error('Component "' + this.toString() + '" wants to listen on "' + r + '" but no such attribute was defined.');
+ }
+ rules[this.attr[r]] = ruleInfo[r];
+ }, this);
+
+ return rules;
+ };
+
+ this.defaultAttrs = function(defaults) {
+ utils.push(this.defaults, defaults, true) || (this.defaults = defaults);
+ };
+
+ this.select = function(attributeKey) {
+ return this.$node.find(this.attr[attributeKey]);
+ };
+
+ this.initialize = $.noop;
+ this.teardown = 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);
+
+ $(selector).each(function(i, node) {
+ var rawNode = node.jQuery ? node[0] : node;
+ var componentInfo = registry.findComponentInfo(this)
+ if (componentInfo && componentInfo.isAttachedTo(rawNode)) {
+ //already attached
+ return;
+ }
+
+ new this(node, options);
+ }.bind(this));
+ }
+
+ // 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];
+
+ Component.toString = function() {
+ var prettyPrintMixins = mixins.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 != "withBaseComponent") ? mixin.name : "";
+ }
+ }).filter(Boolean).join(', ');
+ return prettyPrintMixins;
+ };
+
+ if (window.DEBUG && window.DEBUG.enabled) {
+ Component.describe = Component.toString();
+ }
+
+ //'options' is optional hash to be merged with 'defaults' in the component definition
+ function Component(node, options) {
+ options = options || {};
+ this.identity = componentId++;
+
+ if (!node) {
+ throw new Error("Component needs a node");
+ }
+
+ if (node.jquery) {
+ this.node = node[0];
+ this.$node = node;
+ } else {
+ this.node = node;
+ this.$node = $(node);
+ }
+
+ this.toString = Component.toString;
+ if (window.DEBUG && window.DEBUG.enabled) {
+ this.describe = this.toString();
+ }
+
+ //merge defaults with supplied options
+ //put options in attr.__proto__ to avoid merge overhead
+ var attr = Object.create(options);
+ for (var key in this.defaults) {
+ if (!options.hasOwnProperty(key)) {
+ attr[key] = this.defaults[key];
+ }
+ }
+ this.attr = attr;
+
+ Object.keys(this.defaults || {}).forEach(function(key) {
+ if (this.defaults[key] === null && this.attr[key] === null) {
+ throw new Error('Required attribute "' + key + '" not specified in attachTo for component "' + this.toString() + '".');
+ }
+ }, this);
+
+ this.initialize.call(this, options);
+ }
+
+ Component.attachTo = attachTo;
+ Component.teardownAll = teardownAll;
+
+ // prepend common mixins to supplied list, then mixin all flavors
+ mixins.unshift(withBaseComponent, 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;
+ }
+);
diff --git a/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/compose.js b/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/compose.js
new file mode 100644
index 0000000000..c1343e7fe8
--- /dev/null
+++ b/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/compose.js
@@ -0,0 +1,86 @@
+// ==========================================
+// Copyright 2013 Twitter, Inc
+// Licensed under The MIT License
+// http://opensource.org/licenses/MIT
+// ==========================================
+
+"use strict";
+
+define(
+
+ [
+ './utils',
+ '../tools/debug/debug'
+ ],
+
+ function(util, debug) {
+
+ //enumerables are shims - getOwnPropertyDescriptor shim doesn't work
+ var canWriteProtect = debug.enabled && !util.isEnumerable(Object, 'getOwnPropertyDescriptor');
+ //whitelist of unlockable property names
+ var dontLock = ['mixedIn'];
+
+ if (canWriteProtect) {
+ //IE8 getOwnPropertyDescriptor is built-in but throws exeption on non DOM objects
+ try {
+ Object.getOwnPropertyDescriptor(Object, 'keys');
+ } catch(e) {
+ canWriteProtect = false;
+ }
+ }
+
+ function setPropertyWritability(obj, isWritable) {
+ if (!canWriteProtect) {
+ return;
+ }
+
+ var props = Object.create(null);
+
+ Object.keys(obj).forEach(
+ function (key) {
+ if (dontLock.indexOf(key) < 0) {
+ var desc = Object.getOwnPropertyDescriptor(obj, key);
+ desc.writable = isWritable;
+ props[key] = desc;
+ }
+ }
+ );
+
+ Object.defineProperties(obj, props);
+ }
+
+ function unlockProperty(obj, prop, op) {
+ var writable;
+
+ if (!canWriteProtect || !obj.hasOwnProperty(prop)) {
+ op.call(obj);
+ return;
+ }
+
+ writable = Object.getOwnPropertyDescriptor(obj, prop).writable;
+ Object.defineProperty(obj, prop, { writable: true });
+ op.call(obj);
+ Object.defineProperty(obj, prop, { writable: writable });
+ }
+
+ function mixin(base, mixins) {
+ base.mixedIn = base.hasOwnProperty('mixedIn') ? base.mixedIn : [];
+
+ mixins.forEach(function(mixin) {
+ if (base.mixedIn.indexOf(mixin) == -1) {
+ setPropertyWritability(base, false);
+ mixin.call(base);
+ base.mixedIn.push(mixin);
+ }
+ });
+
+ setPropertyWritability(base, true);
+ }
+
+ return {
+ mixin: mixin,
+ unlockProperty: unlockProperty
+ };
+
+ }
+);
diff --git a/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/index.js b/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/index.js
new file mode 100644
index 0000000000..1604b253b9
--- /dev/null
+++ b/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/index.js
@@ -0,0 +1,30 @@
+// ==========================================
+// Copyright 2013 Twitter, Inc
+// Licensed under The MIT License
+// http://opensource.org/licenses/MIT
+// ==========================================
+
+define(
+
+ [
+ './advice',
+ './component',
+ './compose',
+ './logger',
+ './registry',
+ './utils'
+ ],
+
+ function (advice, component, compose, logger, registry, utils) {
+
+ return {
+ advice: advice,
+ component: component,
+ compose: compose,
+ logger: logger,
+ registry: registry,
+ utils: utils
+ };
+
+ }
+);
diff --git a/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/logger.js b/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/logger.js
new file mode 100644
index 0000000000..2c89b1bd20
--- /dev/null
+++ b/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/logger.js
@@ -0,0 +1,93 @@
+// ==========================================
+// Copyright 2013 Twitter, Inc
+// Licensed under The MIT License
+// http://opensource.org/licenses/MIT
+// ==========================================
+
+"use strict";
+
+define(
+
+ [
+ './compose',
+ './utils'
+ ],
+
+ function (compose, util) {
+
+ var actionSymbols = {
+ on:'<-',
+ trigger: '->',
+ off: 'x '
+ };
+
+ function elemToString(elem) {
+ var tagStr = elem.tagName ? elem.tagName.toLowerCase() : elem.toString();
+ var classStr = elem.className ? "." + (elem.className) : "";
+ var result = tagStr + classStr;
+ return elem.tagName ? ['\'', '\''].join(result) : result;
+ }
+
+ function log(action, component, eventArgs) {
+
+ var name, elem, fn, fnName, logFilter, toRegExp, actionLoggable, nameLoggable;
+
+ if (typeof eventArgs[eventArgs.length-1] == 'function') {
+ fn = eventArgs.pop();
+ fn = fn.unbound || fn; //use unbound version if any (better info)
+ }
+
+ if (typeof eventArgs[eventArgs.length - 1] == 'object') {
+ eventArgs.pop(); //trigger data arg - not logged right now
+ }
+
+ if (eventArgs.length == 2) {
+ elem = eventArgs[0];
+ name = eventArgs[1];
+ } else {
+ elem = component.$node[0];
+ name = eventArgs[0];
+ }
+
+ if (window.DEBUG && window.DEBUG.enabled) {
+ logFilter = DEBUG.events.logFilter;
+
+ // no regex for you, actions...
+ actionLoggable = logFilter.actions=="all" || (logFilter.actions.indexOf(action) > -1);
+ // event name filter allow wildcards or regex...
+ toRegExp = function(expr) {
+ return expr.test ? expr : new RegExp("^" + expr.replace(/\*/g, ".*") + "$");
+ };
+ nameLoggable =
+ logFilter.eventNames=="all" ||
+ logFilter.eventNames.some(function(e) {return toRegExp(e).test(name)});
+
+ if (actionLoggable && nameLoggable) {
+ console.info(
+ actionSymbols[action],
+ action,
+ '[' + name + ']',
+ elemToString(elem),
+ component.constructor.toString(),
+ fn && (fnName = fn.name || fn.displayName) && '-> ' + fnName
+ );
+ }
+ }
+ }
+
+
+ function withLogging() {
+ this.before('trigger', function() {
+ log('trigger', this, util.toArray(arguments));
+ });
+ this.before('on', function() {
+ log('on', this, util.toArray(arguments));
+ });
+ this.before('off', function(eventArgs) {
+ log('off', this, util.toArray(arguments));
+ });
+ }
+
+ return withLogging;
+ }
+);
diff --git a/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/registry.js b/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/registry.js
new file mode 100644
index 0000000000..baaa3b10a7
--- /dev/null
+++ b/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/registry.js
@@ -0,0 +1,220 @@
+// ==========================================
+// Copyright 2013 Twitter, Inc
+// Licensed under The MIT License
+// http://opensource.org/licenses/MIT
+// ==========================================
+
+"use strict";
+
+define(
+
+ [
+ './utils'
+ ],
+
+ function (util) {
+
+ function parseEventArgs(instance, args) {
+ var element, type, callback;
+ var end = args.length;
+
+ if (typeof args[end - 1] === 'function') {
+ end -= 1;
+ callback = args[end];
+ }
+
+ if (typeof args[end - 1] === 'object') {
+ end -= 1;
+ }
+
+ if (end == 2) {
+ element = args[0];
+ type = args[1];
+ } else {
+ element = instance.node;
+ type = args[0];
+ }
+
+ return {
+ element: element,
+ type: type,
+ callback: callback
+ };
+ }
+
+ function matchEvent(a, b) {
+ return (
+ (a.element == b.element) &&
+ (a.type == b.type) &&
+ (b.callback == null || (a.callback == b.callback))
+ );
+ }
+
+ function Registry() {
+
+ var registry = this;
+
+ (this.reset = function() {
+ this.components = [];
+ this.allInstances = {};
+ this.events = [];
+ }).call(this);
+
+ function ComponentInfo(component) {
+ this.component = component;
+ this.attachedTo = [];
+ this.instances = {};
+
+ this.addInstance = function(instance) {
+ var instanceInfo = new InstanceInfo(instance);
+ this.instances[instance.identity] = instanceInfo;
+ this.attachedTo.push(instance.node);
+
+ return instanceInfo;
+ }
+
+ this.removeInstance = function(instance) {
+ delete this.instances[instance.identity];
+ var indexOfNode = this.attachedTo.indexOf(instance.node);
+ (indexOfNode > -1) && this.attachedTo.splice(indexOfNode, 1);
+
+ if (!this.instances.length) {
+ //if I hold no more instances remove me from registry
+ registry.removeComponentInfo(this);
+ }
+ }
+
+ this.isAttachedTo = function(node) {
+ return this.attachedTo.indexOf(node) > -1;
+ }
+ }
+
+ function InstanceInfo(instance) {
+ this.instance = instance;
+ this.events = [];
+
+ this.addBind = function(event) {
+ this.events.push(event);
+ registry.events.push(event);
+ };
+
+ this.removeBind = function(event) {
+ for (var i = 0, e; e = this.events[i]; i++) {
+ if (matchEvent(e, event)) {
+ this.events.splice(i, 1);
+ }
+ }
+ }
+ }
+
+ this.addInstance = function(instance) {
+ var component = this.findComponentInfo(instance);
+
+ if (!component) {
+ component = new ComponentInfo(instance.constructor);
+ this.components.push(component);
+ }
+
+ var inst = component.addInstance(instance);
+
+ this.allInstances[instance.identity] = inst;
+
+ return component;
+ };
+
+ this.removeInstance = function(instance) {
+ var index, instInfo = this.findInstanceInfo(instance);
+
+ //remove from component info
+ var componentInfo = this.findComponentInfo(instance);
+ componentInfo && componentInfo.removeInstance(instance);
+
+ //remove from registry
+ delete this.allInstances[instance.identity];
+ };
+
+ this.removeComponentInfo = function(componentInfo) {
+ var index = this.components.indexOf(componentInfo);
+ (index > -1) && this.components.splice(index, 1);
+ };
+
+ this.findComponentInfo = function(which) {
+ var component = which.attachTo ? which : which.constructor;
+
+ for (var i = 0, c; c = this.components[i]; i++) {
+ if (c.component === component) {
+ return c;
+ }
+ }
+
+ return null;
+ };
+
+ this.findInstanceInfo = function(instance) {
+ return this.allInstances[instance.identity] || null;
+ };
+
+ this.findInstanceInfoByNode = function(node) {
+ var result = [];
+ Object.keys(this.allInstances).forEach(function(k) {
+ var thisInstanceInfo = this.allInstances[k];
+ if(thisInstanceInfo.instance.node === node) {
+ result.push(thisInstanceInfo);
+ }
+ }, this);
+ return result;
+ };
+
+ this.on = function(componentOn) {
+ var instance = registry.findInstanceInfo(this), boundCallback;
+
+ // unpacking arguments by hand benchmarked faster
+ var l = arguments.length, i = 1;
+ var otherArgs = new Array(l - 1);
+ for (; i < l; i++) otherArgs[i - 1] = arguments[i];
+
+ if (instance) {
+ boundCallback = componentOn.apply(null, otherArgs);
+ if (boundCallback) {
+ otherArgs[otherArgs.length-1] = boundCallback;
+ }
+ var event = parseEventArgs(this, otherArgs);
+ instance.addBind(event);
+ }
+ };
+
+ this.off = function(el, type, callback) {
+ var event = parseEventArgs(this, arguments),
+ instance = registry.findInstanceInfo(this);
+
+ if (instance) {
+ instance.removeBind(event);
+ }
+ };
+
+ //debug tools may want to add advice to trigger
+ if (window.DEBUG && DEBUG.enabled) {
+ registry.trigger = new Function;
+ }
+
+ this.teardown = function() {
+ registry.removeInstance(this);
+ };
+
+ this.withRegistration = function() {
+ this.before('initialize', function() {
+ registry.addInstance(this);
+ });
+
+ this.around('on', registry.on);
+ this.after('off', registry.off);
+ //debug tools may want to add advice to trigger
+ window.DEBUG && DEBUG.enabled && this.after('trigger', registry.trigger);
+ this.after('teardown', {obj:registry, fnName:'teardown'});
+ };
+
+ }
+
+ return new Registry;
+ }
+);
diff --git a/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/utils.js b/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/utils.js
new file mode 100644
index 0000000000..31af777348
--- /dev/null
+++ b/third_party/webkit/PerformanceTests/Speedometer/resources/flightjs-example-app/components/flight/lib/utils.js
@@ -0,0 +1,236 @@
+// ==========================================
+// Copyright 2013 Twitter, Inc
+// Licensed under The MIT License
+// http://opensource.org/licenses/MIT
+// ==========================================
+
+"use strict";
+
+define(
+
+ [],
+
+ function () {
+
+ var arry = [];
+ var DEFAULT_INTERVAL = 100;
+
+ var utils = {
+
+ isDomObj: function(obj) {
+ return !!(obj.nodeType || (obj === window));
+ },
+
+ toArray: function(obj, from) {
+ return arry.slice.call(obj, from);
+ },
+
+ // returns new object representing multiple objects merged together
+ // optional final argument is boolean which specifies if merge is recursive
+ // original objects are unmodified
+ //
+ // usage:
+ // var base = {a:2, b:6};
+ // var extra = {b:3, c:4};
+ // merge(base, extra); //{a:2, b:3, c:4}
+ // base; //{a:2, b:6}
+ //
+ // var base = {a:2, b:6};
+ // var extra = {b:3, c:4};
+ // var extraExtra = {a:4, d:9};
+ // merge(base, extra, extraExtra); //{a:4, b:3, c:4. d: 9}
+ // base; //{a:2, b:6}
+ //
+ // var base = {a:2, b:{bb:4, cc:5}};
+ // var extra = {a:4, b:{cc:7, dd:1}};
+ // merge(base, extra, true); //{a:4, b:{bb:4, cc:7, dd:1}}
+ // base; //{a:2, b:6}
+
+ merge: function(/*obj1, obj2,....deepCopy*/) {
+ // unpacking arguments by hand benchmarked faster
+ var l = arguments.length,
+ i = 0,
+ args = new Array(l + 1);
+ for (; i < l; i++) args[i + 1] = arguments[i];
+
+ if (l === 0) {
+ return {};
+ }
+
+ //start with empty object so a copy is created
+ args[0] = {};
+
+ if (args[args.length - 1] === true) {
+ //jquery extend requires deep copy as first arg
+ args.pop();
+ args.unshift(true);
+ }
+
+ return $.extend.apply(undefined, args);
+ },
+
+ // updates base in place by copying properties of extra to it
+ // optionally clobber protected
+ // usage:
+ // var base = {a:2, b:6};
+ // var extra = {c:4};
+ // push(base, extra); //{a:2, b:6, c:4}
+ // base; //{a:2, b:6, c:4}
+ //
+ // var base = {a:2, b:6};
+ // var extra = {b: 4 c:4};
+ // push(base, extra, true); //Error ("utils.push attempted to overwrite 'b' while running in protected mode")
+ // base; //{a:2, b:6}
+ //
+ // objects with the same key will merge recursively when protect is false
+ // eg:
+ // var base = {a:16, b:{bb:4, cc:10}};
+ // var extra = {b:{cc:25, dd:19}, c:5};
+ // push(base, extra); //{a:16, {bb:4, cc:25, dd:19}, c:5}
+ //
+ push: function(base, extra, protect) {
+ if (base) {
+ Object.keys(extra || {}).forEach(function(key) {
+ if (base[key] && protect) {
+ throw Error("utils.push attempted to overwrite '" + key + "' while running in protected mode");
+ }
+
+ if (typeof base[key] == "object" && typeof extra[key] == "object") {
+ //recurse
+ this.push(base[key], extra[key]);
+ } else {
+ //no protect, so extra wins
+ base[key] = extra[key];
+ }
+ }, this);
+ }
+
+ return base;
+ },
+
+ isEnumerable: function(obj, property) {
+ return Object.keys(obj).indexOf(property) > -1;
+ },
+
+ //build a function from other function(s)
+ //util.compose(a,b,c) -> a(b(c()));
+ //implementation lifted from underscore.js (c) 2009-2012 Jeremy Ashkenas
+ compose: function() {
+ var funcs = arguments;
+
+ return function() {
+ var args = arguments;
+
+ for (var i = funcs.length-1; i >= 0; i--) {
+ args = [funcs[i].apply(this, args)];
+ }
+
+ return args[0];
+ };
+ },
+
+ // Can only unique arrays of homogeneous primitives, e.g. an array of only strings, an array of only booleans, or an array of only numerics
+ uniqueArray: function(array) {
+ var u = {}, a = [];
+
+ for (var i = 0, l = array.length; i < l; ++i) {
+ if (u.hasOwnProperty(array[i])) {
+ continue;
+ }
+
+ a.push(array[i]);
+ u[array[i]] = 1;
+ }
+
+ return a;
+ },
+
+ debounce: function(func, wait, immediate) {
+ if (typeof wait != 'number') {
+ wait = DEFAULT_INTERVAL;
+ }
+
+ var timeout, result;
+
+ return function() {
+ var context = this, args = arguments;
+ var later = function() {
+ timeout = null;
+ if (!immediate) {
+ result = func.apply(context, args);
+ }
+ };
+ var callNow = immediate && !timeout;
+
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+
+ if (callNow) {
+ result = func.apply(context, args);
+ }
+
+ return result;
+ };
+ },
+
+ throttle: function(func, wait) {
+ if (typeof wait != 'number') {
+ wait = DEFAULT_INTERVAL;
+ }
+
+ var context, args, timeout, throttling, more, result;
+ var whenDone = this.debounce(function(){
+ more = throttling = false;
+ }, wait);
+
+ return function() {
+ context = this; args = arguments;
+ var later = function() {
+ timeout = null;
+ if (more) {
+ result = func.apply(context, args);
+ }
+ whenDone();
+ };
+
+ if (!timeout) {
+ timeout = setTimeout(later, wait);
+ }
+
+ if (throttling) {
+ more = true;
+ } else {
+ throttling = true;
+ result = func.apply(context, args);
+ }
+
+ whenDone();
+ return result;
+ };
+ },
+
+ countThen: function(num, base) {
+ return function() {
+ if (!--num) { return base.apply(this, arguments); }
+ };
+ },
+
+ delegate: function(rules) {
+ return function(e, data) {
+ var target = $(e.target), parent;
+
+ Object.keys(rules).forEach(function(selector) {
+ if ((parent = target.closest(selector)).length) {
+ data = data || {};
+ data.el = parent[0];
+ return rules[selector].apply(this, [e, data]);
+ }
+ }, this);
+ };
+ }
+
+ };
+
+ return utils;
+ }
+);