32 changed files with 7595 additions and 0 deletions
@ -0,0 +1,326 @@ |
|||||||
|
// Copyright 2009-2012 by contributors, MIT License
|
||||||
|
// vim: ts=4 sts=4 sw=4 expandtab
|
||||||
|
|
||||||
|
// Module systems magic dance
|
||||||
|
(function (definition) { |
||||||
|
// RequireJS
|
||||||
|
if (typeof define == "function") { |
||||||
|
define(definition); |
||||||
|
// YUI3
|
||||||
|
} else if (typeof YUI == "function") { |
||||||
|
YUI.add("es5-sham", definition); |
||||||
|
// CommonJS and <script>
|
||||||
|
} else { |
||||||
|
definition(); |
||||||
|
} |
||||||
|
})(function () { |
||||||
|
|
||||||
|
// ES5 15.2.3.2
|
||||||
|
// http://es5.github.com/#x15.2.3.2
|
||||||
|
if (!Object.getPrototypeOf) { |
||||||
|
// https://github.com/kriskowal/es5-shim/issues#issue/2
|
||||||
|
// http://ejohn.org/blog/objectgetprototypeof/
|
||||||
|
// recommended by fschaefer on github
|
||||||
|
Object.getPrototypeOf = function getPrototypeOf(object) { |
||||||
|
return object.__proto__ || ( |
||||||
|
object.constructor |
||||||
|
? object.constructor.prototype |
||||||
|
: prototypeOfObject |
||||||
|
); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.2.3.3
|
||||||
|
// http://es5.github.com/#x15.2.3.3
|
||||||
|
if (!Object.getOwnPropertyDescriptor) { |
||||||
|
var ERR_NON_OBJECT = "Object.getOwnPropertyDescriptor called on a non-object: "; |
||||||
|
|
||||||
|
Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(object, property) { |
||||||
|
if ((typeof object != "object" && typeof object != "function") || object === null) { |
||||||
|
throw new TypeError(ERR_NON_OBJECT + object); |
||||||
|
} |
||||||
|
// If object does not owns property return undefined immediately.
|
||||||
|
if (!owns(object, property)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// If object has a property then it's for sure both `enumerable` and
|
||||||
|
// `configurable`.
|
||||||
|
var descriptor = { enumerable: true, configurable: true }; |
||||||
|
|
||||||
|
// If JS engine supports accessor properties then property may be a
|
||||||
|
// getter or setter.
|
||||||
|
if (supportsAccessors) { |
||||||
|
// Unfortunately `__lookupGetter__` will return a getter even
|
||||||
|
// if object has own non getter property along with a same named
|
||||||
|
// inherited getter. To avoid misbehavior we temporary remove
|
||||||
|
// `__proto__` so that `__lookupGetter__` will return getter only
|
||||||
|
// if it's owned by an object.
|
||||||
|
var prototype = object.__proto__; |
||||||
|
object.__proto__ = prototypeOfObject; |
||||||
|
|
||||||
|
var getter = lookupGetter(object, property); |
||||||
|
var setter = lookupSetter(object, property); |
||||||
|
|
||||||
|
// Once we have getter and setter we can put values back.
|
||||||
|
object.__proto__ = prototype; |
||||||
|
|
||||||
|
if (getter || setter) { |
||||||
|
if (getter) { |
||||||
|
descriptor.get = getter; |
||||||
|
} |
||||||
|
if (setter) { |
||||||
|
descriptor.set = setter; |
||||||
|
} |
||||||
|
// If it was accessor property we're done and return here
|
||||||
|
// in order to avoid adding `value` to the descriptor.
|
||||||
|
return descriptor; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// If we got this far we know that object has an own property that is
|
||||||
|
// not an accessor so we set it as a value and return descriptor.
|
||||||
|
descriptor.value = object[property]; |
||||||
|
return descriptor; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.2.3.4
|
||||||
|
// http://es5.github.com/#x15.2.3.4
|
||||||
|
if (!Object.getOwnPropertyNames) { |
||||||
|
Object.getOwnPropertyNames = function getOwnPropertyNames(object) { |
||||||
|
return Object.keys(object); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.2.3.5
|
||||||
|
// http://es5.github.com/#x15.2.3.5
|
||||||
|
if (!Object.create) { |
||||||
|
Object.create = function create(prototype, properties) { |
||||||
|
var object; |
||||||
|
if (prototype === null) { |
||||||
|
object = { "__proto__": null }; |
||||||
|
} else { |
||||||
|
if (typeof prototype != "object") { |
||||||
|
throw new TypeError("typeof prototype["+(typeof prototype)+"] != 'object'"); |
||||||
|
} |
||||||
|
var Type = function () {}; |
||||||
|
Type.prototype = prototype; |
||||||
|
object = new Type(); |
||||||
|
// IE has no built-in implementation of `Object.getPrototypeOf`
|
||||||
|
// neither `__proto__`, but this manually setting `__proto__` will
|
||||||
|
// guarantee that `Object.getPrototypeOf` will work as expected with
|
||||||
|
// objects created using `Object.create`
|
||||||
|
object.__proto__ = prototype; |
||||||
|
} |
||||||
|
if (properties !== void 0) { |
||||||
|
Object.defineProperties(object, properties); |
||||||
|
} |
||||||
|
return object; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.2.3.6
|
||||||
|
// http://es5.github.com/#x15.2.3.6
|
||||||
|
|
||||||
|
// Patch for WebKit and IE8 standard mode
|
||||||
|
// Designed by hax <hax.github.com>
|
||||||
|
// related issue: https://github.com/kriskowal/es5-shim/issues#issue/5
|
||||||
|
// IE8 Reference:
|
||||||
|
// http://msdn.microsoft.com/en-us/library/dd282900.aspx
|
||||||
|
// http://msdn.microsoft.com/en-us/library/dd229916.aspx
|
||||||
|
// WebKit Bugs:
|
||||||
|
// https://bugs.webkit.org/show_bug.cgi?id=36423
|
||||||
|
|
||||||
|
function doesDefinePropertyWork(object) { |
||||||
|
try { |
||||||
|
Object.defineProperty(object, "sentinel", {}); |
||||||
|
return "sentinel" in object; |
||||||
|
} catch (exception) { |
||||||
|
// returns falsy
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// check whether defineProperty works if it's given. Otherwise,
|
||||||
|
// shim partially.
|
||||||
|
if (Object.defineProperty) { |
||||||
|
var definePropertyWorksOnObject = doesDefinePropertyWork({}); |
||||||
|
var definePropertyWorksOnDom = typeof document == "undefined" || |
||||||
|
doesDefinePropertyWork(document.createElement("div")); |
||||||
|
if (!definePropertyWorksOnObject || !definePropertyWorksOnDom) { |
||||||
|
var definePropertyFallback = Object.defineProperty; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!Object.defineProperty || definePropertyFallback) { |
||||||
|
var ERR_NON_OBJECT_DESCRIPTOR = "Property description must be an object: "; |
||||||
|
var ERR_NON_OBJECT_TARGET = "Object.defineProperty called on non-object: " |
||||||
|
var ERR_ACCESSORS_NOT_SUPPORTED = "getters & setters can not be defined " + |
||||||
|
"on this javascript engine"; |
||||||
|
|
||||||
|
Object.defineProperty = function defineProperty(object, property, descriptor) { |
||||||
|
if ((typeof object != "object" && typeof object != "function") || object === null) { |
||||||
|
throw new TypeError(ERR_NON_OBJECT_TARGET + object); |
||||||
|
} |
||||||
|
if ((typeof descriptor != "object" && typeof descriptor != "function") || descriptor === null) { |
||||||
|
throw new TypeError(ERR_NON_OBJECT_DESCRIPTOR + descriptor); |
||||||
|
} |
||||||
|
// make a valiant attempt to use the real defineProperty
|
||||||
|
// for I8's DOM elements.
|
||||||
|
if (definePropertyFallback) { |
||||||
|
try { |
||||||
|
return definePropertyFallback.call(Object, object, property, descriptor); |
||||||
|
} catch (exception) { |
||||||
|
// try the shim if the real one doesn't work
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// If it's a data property.
|
||||||
|
if (owns(descriptor, "value")) { |
||||||
|
// fail silently if "writable", "enumerable", or "configurable"
|
||||||
|
// are requested but not supported
|
||||||
|
/* |
||||||
|
// alternate approach:
|
||||||
|
if ( // can't implement these features; allow false but not true
|
||||||
|
!(owns(descriptor, "writable") ? descriptor.writable : true) || |
||||||
|
!(owns(descriptor, "enumerable") ? descriptor.enumerable : true) || |
||||||
|
!(owns(descriptor, "configurable") ? descriptor.configurable : true) |
||||||
|
) |
||||||
|
throw new RangeError( |
||||||
|
"This implementation of Object.defineProperty does not " + |
||||||
|
"support configurable, enumerable, or writable." |
||||||
|
); |
||||||
|
*/ |
||||||
|
|
||||||
|
if (supportsAccessors && (lookupGetter(object, property) || |
||||||
|
lookupSetter(object, property))) |
||||||
|
{ |
||||||
|
// As accessors are supported only on engines implementing
|
||||||
|
// `__proto__` we can safely override `__proto__` while defining
|
||||||
|
// a property to make sure that we don't hit an inherited
|
||||||
|
// accessor.
|
||||||
|
var prototype = object.__proto__; |
||||||
|
object.__proto__ = prototypeOfObject; |
||||||
|
// Deleting a property anyway since getter / setter may be
|
||||||
|
// defined on object itself.
|
||||||
|
delete object[property]; |
||||||
|
object[property] = descriptor.value; |
||||||
|
// Setting original `__proto__` back now.
|
||||||
|
object.__proto__ = prototype; |
||||||
|
} else { |
||||||
|
object[property] = descriptor.value; |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (!supportsAccessors) { |
||||||
|
throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED); |
||||||
|
} |
||||||
|
// If we got that far then getters and setters can be defined !!
|
||||||
|
if (owns(descriptor, "get")) { |
||||||
|
defineGetter(object, property, descriptor.get); |
||||||
|
} |
||||||
|
if (owns(descriptor, "set")) { |
||||||
|
defineSetter(object, property, descriptor.set); |
||||||
|
} |
||||||
|
} |
||||||
|
return object; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.2.3.7
|
||||||
|
// http://es5.github.com/#x15.2.3.7
|
||||||
|
if (!Object.defineProperties) { |
||||||
|
Object.defineProperties = function defineProperties(object, properties) { |
||||||
|
for (var property in properties) { |
||||||
|
if (owns(properties, property) && property != "__proto__") { |
||||||
|
Object.defineProperty(object, property, properties[property]); |
||||||
|
} |
||||||
|
} |
||||||
|
return object; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.2.3.8
|
||||||
|
// http://es5.github.com/#x15.2.3.8
|
||||||
|
if (!Object.seal) { |
||||||
|
Object.seal = function seal(object) { |
||||||
|
// this is misleading and breaks feature-detection, but
|
||||||
|
// allows "securable" code to "gracefully" degrade to working
|
||||||
|
// but insecure code.
|
||||||
|
return object; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.2.3.9
|
||||||
|
// http://es5.github.com/#x15.2.3.9
|
||||||
|
if (!Object.freeze) { |
||||||
|
Object.freeze = function freeze(object) { |
||||||
|
// this is misleading and breaks feature-detection, but
|
||||||
|
// allows "securable" code to "gracefully" degrade to working
|
||||||
|
// but insecure code.
|
||||||
|
return object; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// detect a Rhino bug and patch it
|
||||||
|
try { |
||||||
|
Object.freeze(function () {}); |
||||||
|
} catch (exception) { |
||||||
|
Object.freeze = (function freeze(freezeObject) { |
||||||
|
return function freeze(object) { |
||||||
|
if (typeof object == "function") { |
||||||
|
return object; |
||||||
|
} else { |
||||||
|
return freezeObject(object); |
||||||
|
} |
||||||
|
}; |
||||||
|
})(Object.freeze); |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.2.3.10
|
||||||
|
// http://es5.github.com/#x15.2.3.10
|
||||||
|
if (!Object.preventExtensions) { |
||||||
|
Object.preventExtensions = function preventExtensions(object) { |
||||||
|
// this is misleading and breaks feature-detection, but
|
||||||
|
// allows "securable" code to "gracefully" degrade to working
|
||||||
|
// but insecure code.
|
||||||
|
return object; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.2.3.11
|
||||||
|
// http://es5.github.com/#x15.2.3.11
|
||||||
|
if (!Object.isSealed) { |
||||||
|
Object.isSealed = function isSealed(object) { |
||||||
|
return false; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.2.3.12
|
||||||
|
// http://es5.github.com/#x15.2.3.12
|
||||||
|
if (!Object.isFrozen) { |
||||||
|
Object.isFrozen = function isFrozen(object) { |
||||||
|
return false; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.2.3.13
|
||||||
|
// http://es5.github.com/#x15.2.3.13
|
||||||
|
if (!Object.isExtensible) { |
||||||
|
Object.isExtensible = function isExtensible(object) { |
||||||
|
// 1. If Type(O) is not Object throw a TypeError exception.
|
||||||
|
if (Object(object) !== object) { |
||||||
|
throw new TypeError(); // TODO message
|
||||||
|
} |
||||||
|
// 2. Return the Boolean value of the [[Extensible]] internal property of O.
|
||||||
|
var name = ''; |
||||||
|
while (owns(object, name)) { |
||||||
|
name += '?'; |
||||||
|
} |
||||||
|
object[name] = true; |
||||||
|
var returnValue = owns(object, name); |
||||||
|
delete object[name]; |
||||||
|
return returnValue; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
}); |
||||||
@ -0,0 +1,6 @@ |
|||||||
|
(function(d){"function"==typeof define?define(d):"function"==typeof YUI?YUI.add("es5-sham",d):d()})(function(){function d(a){try{return Object.defineProperty(a,"sentinel",{}),"sentinel"in a}catch(c){}}Object.getPrototypeOf||(Object.getPrototypeOf=function(a){return a.__proto__||(a.constructor?a.constructor.prototype:prototypeOfObject)});Object.getOwnPropertyDescriptor||(Object.getOwnPropertyDescriptor=function(a,c){if(typeof a!="object"&&typeof a!="function"||a===null)throw new TypeError("Object.getOwnPropertyDescriptor called on a non-object: "+ |
||||||
|
a);if(owns(a,c)){var b={enumerable:true,configurable:true};if(supportsAccessors){var d=a.__proto__;a.__proto__=prototypeOfObject;var f=lookupGetter(a,c),e=lookupSetter(a,c);a.__proto__=d;if(f||e){if(f)b.get=f;if(e)b.set=e;return b}}b.value=a[c];return b}});Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(a){return Object.keys(a)});Object.create||(Object.create=function(a,c){var b;if(a===null)b={__proto__:null};else{if(typeof a!="object")throw new TypeError("typeof prototype["+typeof a+ |
||||||
|
"] != 'object'");b=function(){};b.prototype=a;b=new b;b.__proto__=a}c!==void 0&&Object.defineProperties(b,c);return b});if(Object.defineProperty){var g=d({}),h="undefined"==typeof document||d(document.createElement("div"));if(!g||!h)var e=Object.defineProperty}if(!Object.defineProperty||e)Object.defineProperty=function(a,c,b){if(typeof a!="object"&&typeof a!="function"||a===null)throw new TypeError("Object.defineProperty called on non-object: "+a);if(typeof b!="object"&&typeof b!="function"||b=== |
||||||
|
null)throw new TypeError("Property description must be an object: "+b);if(e)try{return e.call(Object,a,c,b)}catch(d){}if(owns(b,"value"))if(supportsAccessors&&(lookupGetter(a,c)||lookupSetter(a,c))){var f=a.__proto__;a.__proto__=prototypeOfObject;delete a[c];a[c]=b.value;a.__proto__=f}else a[c]=b.value;else{if(!supportsAccessors)throw new TypeError("getters & setters can not be defined on this javascript engine");owns(b,"get")&&defineGetter(a,c,b.get);owns(b,"set")&&defineSetter(a,c,b.set)}return a}; |
||||||
|
Object.defineProperties||(Object.defineProperties=function(a,c){for(var b in c)owns(c,b)&&b!="__proto__"&&Object.defineProperty(a,b,c[b]);return a});Object.seal||(Object.seal=function(a){return a});Object.freeze||(Object.freeze=function(a){return a});try{Object.freeze(function(){})}catch(j){var i=Object.freeze;Object.freeze=function(a){return typeof a=="function"?a:i(a)}}Object.preventExtensions||(Object.preventExtensions=function(a){return a});Object.isSealed||(Object.isSealed=function(){return false}); |
||||||
|
Object.isFrozen||(Object.isFrozen=function(){return false});Object.isExtensible||(Object.isExtensible=function(a){if(Object(a)!==a)throw new TypeError;for(var c="";owns(a,c);)c=c+"?";a[c]=true;var b=owns(a,c);delete a[c];return b})}); |
||||||
@ -0,0 +1,778 @@ |
|||||||
|
// Copyright 2009-2012 by contributors, MIT License
|
||||||
|
// vim: ts=4 sts=4 sw=4 expandtab
|
||||||
|
|
||||||
|
// Module systems magic dance
|
||||||
|
(function (definition) { |
||||||
|
// RequireJS
|
||||||
|
if (typeof define == "function") { |
||||||
|
define(definition); |
||||||
|
// YUI3
|
||||||
|
} else if (typeof YUI == "function") { |
||||||
|
YUI.add("es5", definition); |
||||||
|
// CommonJS and <script>
|
||||||
|
} else { |
||||||
|
definition(); |
||||||
|
} |
||||||
|
})(function () { |
||||||
|
|
||||||
|
/** |
||||||
|
* Brings an environment as close to ECMAScript 5 compliance |
||||||
|
* as is possible with the facilities of erstwhile engines. |
||||||
|
* |
||||||
|
* Annotated ES5: http://es5.github.com/ (specific links below)
|
||||||
|
* ES5 Spec: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
|
||||||
|
* Required reading: http://javascriptweblog.wordpress.com/2011/12/05/extending-javascript-natives/
|
||||||
|
*/ |
||||||
|
|
||||||
|
//
|
||||||
|
// Function
|
||||||
|
// ========
|
||||||
|
//
|
||||||
|
|
||||||
|
// ES-5 15.3.4.5
|
||||||
|
// http://es5.github.com/#x15.3.4.5
|
||||||
|
|
||||||
|
if (!Function.prototype.bind) { |
||||||
|
Function.prototype.bind = function bind(that) { // .length is 1
|
||||||
|
// 1. Let Target be the this value.
|
||||||
|
var target = this; |
||||||
|
// 2. If IsCallable(Target) is false, throw a TypeError exception.
|
||||||
|
if (typeof target != "function") { |
||||||
|
throw new TypeError("Function.prototype.bind called on incompatible " + target); |
||||||
|
} |
||||||
|
// 3. Let A be a new (possibly empty) internal list of all of the
|
||||||
|
// argument values provided after thisArg (arg1, arg2 etc), in order.
|
||||||
|
// XXX slicedArgs will stand in for "A" if used
|
||||||
|
var args = slice.call(arguments, 1); // for normal call
|
||||||
|
// 4. Let F be a new native ECMAScript object.
|
||||||
|
// 11. Set the [[Prototype]] internal property of F to the standard
|
||||||
|
// built-in Function prototype object as specified in 15.3.3.1.
|
||||||
|
// 12. Set the [[Call]] internal property of F as described in
|
||||||
|
// 15.3.4.5.1.
|
||||||
|
// 13. Set the [[Construct]] internal property of F as described in
|
||||||
|
// 15.3.4.5.2.
|
||||||
|
// 14. Set the [[HasInstance]] internal property of F as described in
|
||||||
|
// 15.3.4.5.3.
|
||||||
|
var bound = function () { |
||||||
|
|
||||||
|
if (this instanceof bound) { |
||||||
|
// 15.3.4.5.2 [[Construct]]
|
||||||
|
// When the [[Construct]] internal method of a function object,
|
||||||
|
// F that was created using the bind function is called with a
|
||||||
|
// list of arguments ExtraArgs, the following steps are taken:
|
||||||
|
// 1. Let target be the value of F's [[TargetFunction]]
|
||||||
|
// internal property.
|
||||||
|
// 2. If target has no [[Construct]] internal method, a
|
||||||
|
// TypeError exception is thrown.
|
||||||
|
// 3. Let boundArgs be the value of F's [[BoundArgs]] internal
|
||||||
|
// property.
|
||||||
|
// 4. Let args be a new list containing the same values as the
|
||||||
|
// list boundArgs in the same order followed by the same
|
||||||
|
// values as the list ExtraArgs in the same order.
|
||||||
|
// 5. Return the result of calling the [[Construct]] internal
|
||||||
|
// method of target providing args as the arguments.
|
||||||
|
|
||||||
|
var F = function(){}; |
||||||
|
F.prototype = target.prototype; |
||||||
|
var self = new F; |
||||||
|
|
||||||
|
var result = target.apply( |
||||||
|
self, |
||||||
|
args.concat(slice.call(arguments)) |
||||||
|
); |
||||||
|
if (Object(result) === result) { |
||||||
|
return result; |
||||||
|
} |
||||||
|
return self; |
||||||
|
|
||||||
|
} else { |
||||||
|
// 15.3.4.5.1 [[Call]]
|
||||||
|
// When the [[Call]] internal method of a function object, F,
|
||||||
|
// which was created using the bind function is called with a
|
||||||
|
// this value and a list of arguments ExtraArgs, the following
|
||||||
|
// steps are taken:
|
||||||
|
// 1. Let boundArgs be the value of F's [[BoundArgs]] internal
|
||||||
|
// property.
|
||||||
|
// 2. Let boundThis be the value of F's [[BoundThis]] internal
|
||||||
|
// property.
|
||||||
|
// 3. Let target be the value of F's [[TargetFunction]] internal
|
||||||
|
// property.
|
||||||
|
// 4. Let args be a new list containing the same values as the
|
||||||
|
// list boundArgs in the same order followed by the same
|
||||||
|
// values as the list ExtraArgs in the same order.
|
||||||
|
// 5. Return the result of calling the [[Call]] internal method
|
||||||
|
// of target providing boundThis as the this value and
|
||||||
|
// providing args as the arguments.
|
||||||
|
|
||||||
|
// equiv: target.call(this, ...boundArgs, ...args)
|
||||||
|
return target.apply( |
||||||
|
that, |
||||||
|
args.concat(slice.call(arguments)) |
||||||
|
); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
// XXX bound.length is never writable, so don't even try
|
||||||
|
//
|
||||||
|
// 15. If the [[Class]] internal property of Target is "Function", then
|
||||||
|
// a. Let L be the length property of Target minus the length of A.
|
||||||
|
// b. Set the length own property of F to either 0 or L, whichever is
|
||||||
|
// larger.
|
||||||
|
// 16. Else set the length own property of F to 0.
|
||||||
|
// 17. Set the attributes of the length own property of F to the values
|
||||||
|
// specified in 15.3.5.1.
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// 18. Set the [[Extensible]] internal property of F to true.
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// 19. Let thrower be the [[ThrowTypeError]] function Object (13.2.3).
|
||||||
|
// 20. Call the [[DefineOwnProperty]] internal method of F with
|
||||||
|
// arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]:
|
||||||
|
// thrower, [[Enumerable]]: false, [[Configurable]]: false}, and
|
||||||
|
// false.
|
||||||
|
// 21. Call the [[DefineOwnProperty]] internal method of F with
|
||||||
|
// arguments "arguments", PropertyDescriptor {[[Get]]: thrower,
|
||||||
|
// [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false},
|
||||||
|
// and false.
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// NOTE Function objects created using Function.prototype.bind do not
|
||||||
|
// have a prototype property or the [[Code]], [[FormalParameters]], and
|
||||||
|
// [[Scope]] internal properties.
|
||||||
|
// XXX can't delete prototype in pure-js.
|
||||||
|
|
||||||
|
// 22. Return F.
|
||||||
|
return bound; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// Shortcut to an often accessed properties, in order to avoid multiple
|
||||||
|
// dereference that costs universally.
|
||||||
|
// _Please note: Shortcuts are defined after `Function.prototype.bind` as we
|
||||||
|
// us it in defining shortcuts.
|
||||||
|
var call = Function.prototype.call; |
||||||
|
var prototypeOfArray = Array.prototype; |
||||||
|
var prototypeOfObject = Object.prototype; |
||||||
|
var slice = prototypeOfArray.slice; |
||||||
|
// Having a toString local variable name breaks in Opera so use _toString.
|
||||||
|
var _toString = call.bind(prototypeOfObject.toString); |
||||||
|
var owns = call.bind(prototypeOfObject.hasOwnProperty); |
||||||
|
|
||||||
|
// If JS engine supports accessors creating shortcuts.
|
||||||
|
var defineGetter; |
||||||
|
var defineSetter; |
||||||
|
var lookupGetter; |
||||||
|
var lookupSetter; |
||||||
|
var supportsAccessors; |
||||||
|
if ((supportsAccessors = owns(prototypeOfObject, "__defineGetter__"))) { |
||||||
|
defineGetter = call.bind(prototypeOfObject.__defineGetter__); |
||||||
|
defineSetter = call.bind(prototypeOfObject.__defineSetter__); |
||||||
|
lookupGetter = call.bind(prototypeOfObject.__lookupGetter__); |
||||||
|
lookupSetter = call.bind(prototypeOfObject.__lookupSetter__); |
||||||
|
} |
||||||
|
|
||||||
|
//
|
||||||
|
// Array
|
||||||
|
// =====
|
||||||
|
//
|
||||||
|
|
||||||
|
// ES5 15.4.3.2
|
||||||
|
// http://es5.github.com/#x15.4.3.2
|
||||||
|
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray
|
||||||
|
if (!Array.isArray) { |
||||||
|
Array.isArray = function isArray(obj) { |
||||||
|
return _toString(obj) == "[object Array]"; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// The IsCallable() check in the Array functions
|
||||||
|
// has been replaced with a strict check on the
|
||||||
|
// internal class of the object to trap cases where
|
||||||
|
// the provided function was actually a regular
|
||||||
|
// expression literal, which in V8 and
|
||||||
|
// JavaScriptCore is a typeof "function". Only in
|
||||||
|
// V8 are regular expression literals permitted as
|
||||||
|
// reduce parameters, so it is desirable in the
|
||||||
|
// general case for the shim to match the more
|
||||||
|
// strict and common behavior of rejecting regular
|
||||||
|
// expressions.
|
||||||
|
|
||||||
|
// ES5 15.4.4.18
|
||||||
|
// http://es5.github.com/#x15.4.4.18
|
||||||
|
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/forEach
|
||||||
|
if (!Array.prototype.forEach) { |
||||||
|
Array.prototype.forEach = function forEach(fun /*, thisp*/) { |
||||||
|
var self = toObject(this), |
||||||
|
thisp = arguments[1], |
||||||
|
i = -1, |
||||||
|
length = self.length >>> 0; |
||||||
|
|
||||||
|
// If no callback function or if callback is not a callable function
|
||||||
|
if (_toString(fun) != "[object Function]") { |
||||||
|
throw new TypeError(); // TODO message
|
||||||
|
} |
||||||
|
|
||||||
|
while (++i < length) { |
||||||
|
if (i in self) { |
||||||
|
// Invoke the callback function with call, passing arguments:
|
||||||
|
// context, property value, property key, thisArg object context
|
||||||
|
fun.call(thisp, self[i], i, self); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.4.4.19
|
||||||
|
// http://es5.github.com/#x15.4.4.19
|
||||||
|
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map
|
||||||
|
if (!Array.prototype.map) { |
||||||
|
Array.prototype.map = function map(fun /*, thisp*/) { |
||||||
|
var self = toObject(this), |
||||||
|
length = self.length >>> 0, |
||||||
|
result = Array(length), |
||||||
|
thisp = arguments[1]; |
||||||
|
|
||||||
|
// If no callback function or if callback is not a callable function
|
||||||
|
if (_toString(fun) != "[object Function]") { |
||||||
|
throw new TypeError(fun + " is not a function"); |
||||||
|
} |
||||||
|
|
||||||
|
for (var i = 0; i < length; i++) { |
||||||
|
if (i in self) |
||||||
|
result[i] = fun.call(thisp, self[i], i, self); |
||||||
|
} |
||||||
|
return result; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.4.4.20
|
||||||
|
// http://es5.github.com/#x15.4.4.20
|
||||||
|
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter
|
||||||
|
if (!Array.prototype.filter) { |
||||||
|
Array.prototype.filter = function filter(fun /*, thisp */) { |
||||||
|
var self = toObject(this), |
||||||
|
length = self.length >>> 0, |
||||||
|
result = [], |
||||||
|
value, |
||||||
|
thisp = arguments[1]; |
||||||
|
|
||||||
|
// If no callback function or if callback is not a callable function
|
||||||
|
if (_toString(fun) != "[object Function]") { |
||||||
|
throw new TypeError(fun + " is not a function"); |
||||||
|
} |
||||||
|
|
||||||
|
for (var i = 0; i < length; i++) { |
||||||
|
if (i in self) { |
||||||
|
value = self[i]; |
||||||
|
if (fun.call(thisp, value, i, self)) { |
||||||
|
result.push(value); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.4.4.16
|
||||||
|
// http://es5.github.com/#x15.4.4.16
|
||||||
|
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every
|
||||||
|
if (!Array.prototype.every) { |
||||||
|
Array.prototype.every = function every(fun /*, thisp */) { |
||||||
|
var self = toObject(this), |
||||||
|
length = self.length >>> 0, |
||||||
|
thisp = arguments[1]; |
||||||
|
|
||||||
|
// If no callback function or if callback is not a callable function
|
||||||
|
if (_toString(fun) != "[object Function]") { |
||||||
|
throw new TypeError(fun + " is not a function"); |
||||||
|
} |
||||||
|
|
||||||
|
for (var i = 0; i < length; i++) { |
||||||
|
if (i in self && !fun.call(thisp, self[i], i, self)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.4.4.17
|
||||||
|
// http://es5.github.com/#x15.4.4.17
|
||||||
|
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some
|
||||||
|
if (!Array.prototype.some) { |
||||||
|
Array.prototype.some = function some(fun /*, thisp */) { |
||||||
|
var self = toObject(this), |
||||||
|
length = self.length >>> 0, |
||||||
|
thisp = arguments[1]; |
||||||
|
|
||||||
|
// If no callback function or if callback is not a callable function
|
||||||
|
if (_toString(fun) != "[object Function]") { |
||||||
|
throw new TypeError(fun + " is not a function"); |
||||||
|
} |
||||||
|
|
||||||
|
for (var i = 0; i < length; i++) { |
||||||
|
if (i in self && fun.call(thisp, self[i], i, self)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.4.4.21
|
||||||
|
// http://es5.github.com/#x15.4.4.21
|
||||||
|
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce
|
||||||
|
if (!Array.prototype.reduce) { |
||||||
|
Array.prototype.reduce = function reduce(fun /*, initial*/) { |
||||||
|
var self = toObject(this), |
||||||
|
length = self.length >>> 0; |
||||||
|
|
||||||
|
// If no callback function or if callback is not a callable function
|
||||||
|
if (_toString(fun) != "[object Function]") { |
||||||
|
throw new TypeError(fun + " is not a function"); |
||||||
|
} |
||||||
|
|
||||||
|
// no value to return if no initial value and an empty array
|
||||||
|
if (!length && arguments.length == 1) { |
||||||
|
throw new TypeError('reduce of empty array with no initial value'); |
||||||
|
} |
||||||
|
|
||||||
|
var i = 0; |
||||||
|
var result; |
||||||
|
if (arguments.length >= 2) { |
||||||
|
result = arguments[1]; |
||||||
|
} else { |
||||||
|
do { |
||||||
|
if (i in self) { |
||||||
|
result = self[i++]; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
// if array contains no values, no initial value to return
|
||||||
|
if (++i >= length) { |
||||||
|
throw new TypeError('reduce of empty array with no initial value'); |
||||||
|
} |
||||||
|
} while (true); |
||||||
|
} |
||||||
|
|
||||||
|
for (; i < length; i++) { |
||||||
|
if (i in self) { |
||||||
|
result = fun.call(void 0, result, self[i], i, self); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.4.4.22
|
||||||
|
// http://es5.github.com/#x15.4.4.22
|
||||||
|
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight
|
||||||
|
if (!Array.prototype.reduceRight) { |
||||||
|
Array.prototype.reduceRight = function reduceRight(fun /*, initial*/) { |
||||||
|
var self = toObject(this), |
||||||
|
length = self.length >>> 0; |
||||||
|
|
||||||
|
// If no callback function or if callback is not a callable function
|
||||||
|
if (_toString(fun) != "[object Function]") { |
||||||
|
throw new TypeError(fun + " is not a function"); |
||||||
|
} |
||||||
|
|
||||||
|
// no value to return if no initial value, empty array
|
||||||
|
if (!length && arguments.length == 1) { |
||||||
|
throw new TypeError('reduceRight of empty array with no initial value'); |
||||||
|
} |
||||||
|
|
||||||
|
var result, i = length - 1; |
||||||
|
if (arguments.length >= 2) { |
||||||
|
result = arguments[1]; |
||||||
|
} else { |
||||||
|
do { |
||||||
|
if (i in self) { |
||||||
|
result = self[i--]; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
// if array contains no values, no initial value to return
|
||||||
|
if (--i < 0) { |
||||||
|
throw new TypeError('reduceRight of empty array with no initial value'); |
||||||
|
} |
||||||
|
} while (true); |
||||||
|
} |
||||||
|
|
||||||
|
do { |
||||||
|
if (i in this) { |
||||||
|
result = fun.call(void 0, result, self[i], i, self); |
||||||
|
} |
||||||
|
} while (i--); |
||||||
|
|
||||||
|
return result; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.4.4.14
|
||||||
|
// http://es5.github.com/#x15.4.4.14
|
||||||
|
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf
|
||||||
|
if (!Array.prototype.indexOf) { |
||||||
|
Array.prototype.indexOf = function indexOf(sought /*, fromIndex */ ) { |
||||||
|
var self = toObject(this), |
||||||
|
length = self.length >>> 0; |
||||||
|
|
||||||
|
if (!length) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
var i = 0; |
||||||
|
if (arguments.length > 1) { |
||||||
|
i = toInteger(arguments[1]); |
||||||
|
} |
||||||
|
|
||||||
|
// handle negative indices
|
||||||
|
i = i >= 0 ? i : Math.max(0, length + i); |
||||||
|
for (; i < length; i++) { |
||||||
|
if (i in self && self[i] === sought) { |
||||||
|
return i; |
||||||
|
} |
||||||
|
} |
||||||
|
return -1; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.4.4.15
|
||||||
|
// http://es5.github.com/#x15.4.4.15
|
||||||
|
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/lastIndexOf
|
||||||
|
if (!Array.prototype.lastIndexOf) { |
||||||
|
Array.prototype.lastIndexOf = function lastIndexOf(sought /*, fromIndex */) { |
||||||
|
var self = toObject(this), |
||||||
|
length = self.length >>> 0; |
||||||
|
|
||||||
|
if (!length) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
var i = length - 1; |
||||||
|
if (arguments.length > 1) { |
||||||
|
i = Math.min(i, toInteger(arguments[1])); |
||||||
|
} |
||||||
|
// handle negative indices
|
||||||
|
i = i >= 0 ? i : length - Math.abs(i); |
||||||
|
for (; i >= 0; i--) { |
||||||
|
if (i in self && sought === self[i]) { |
||||||
|
return i; |
||||||
|
} |
||||||
|
} |
||||||
|
return -1; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
//
|
||||||
|
// Object
|
||||||
|
// ======
|
||||||
|
//
|
||||||
|
|
||||||
|
// ES5 15.2.3.14
|
||||||
|
// http://es5.github.com/#x15.2.3.14
|
||||||
|
if (!Object.keys) { |
||||||
|
// http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation
|
||||||
|
var hasDontEnumBug = true, |
||||||
|
dontEnums = [ |
||||||
|
"toString", |
||||||
|
"toLocaleString", |
||||||
|
"valueOf", |
||||||
|
"hasOwnProperty", |
||||||
|
"isPrototypeOf", |
||||||
|
"propertyIsEnumerable", |
||||||
|
"constructor" |
||||||
|
], |
||||||
|
dontEnumsLength = dontEnums.length; |
||||||
|
|
||||||
|
for (var key in {"toString": null}) { |
||||||
|
hasDontEnumBug = false; |
||||||
|
} |
||||||
|
|
||||||
|
Object.keys = function keys(object) { |
||||||
|
|
||||||
|
if ((typeof object != "object" && typeof object != "function") || object === null) { |
||||||
|
throw new TypeError("Object.keys called on a non-object"); |
||||||
|
} |
||||||
|
|
||||||
|
var keys = []; |
||||||
|
for (var name in object) { |
||||||
|
if (owns(object, name)) { |
||||||
|
keys.push(name); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (hasDontEnumBug) { |
||||||
|
for (var i = 0, ii = dontEnumsLength; i < ii; i++) { |
||||||
|
var dontEnum = dontEnums[i]; |
||||||
|
if (owns(object, dontEnum)) { |
||||||
|
keys.push(dontEnum); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return keys; |
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
//
|
||||||
|
// Date
|
||||||
|
// ====
|
||||||
|
//
|
||||||
|
|
||||||
|
// ES5 15.9.5.43
|
||||||
|
// http://es5.github.com/#x15.9.5.43
|
||||||
|
// This function returns a String value represent the instance in time
|
||||||
|
// represented by this Date object. The format of the String is the Date Time
|
||||||
|
// string format defined in 15.9.1.15. All fields are present in the String.
|
||||||
|
// The time zone is always UTC, denoted by the suffix Z. If the time value of
|
||||||
|
// this object is not a finite Number a RangeError exception is thrown.
|
||||||
|
if (!Date.prototype.toISOString || (new Date(-62198755200000).toISOString().indexOf('-000001') === -1)) { |
||||||
|
Date.prototype.toISOString = function toISOString() { |
||||||
|
var result, length, value, year; |
||||||
|
if (!isFinite(this)) { |
||||||
|
throw new RangeError("Date.prototype.toISOString called on non-finite value."); |
||||||
|
} |
||||||
|
|
||||||
|
// the date time string format is specified in 15.9.1.15.
|
||||||
|
result = [this.getUTCMonth() + 1, this.getUTCDate(), |
||||||
|
this.getUTCHours(), this.getUTCMinutes(), this.getUTCSeconds()]; |
||||||
|
year = this.getUTCFullYear(); |
||||||
|
year = (year < 0 ? '-' : (year > 9999 ? '+' : '')) + ('00000' + Math.abs(year)).slice(0 <= year && year <= 9999 ? -4 : -6); |
||||||
|
|
||||||
|
length = result.length; |
||||||
|
while (length--) { |
||||||
|
value = result[length]; |
||||||
|
// pad months, days, hours, minutes, and seconds to have two digits.
|
||||||
|
if (value < 10) { |
||||||
|
result[length] = "0" + value; |
||||||
|
} |
||||||
|
} |
||||||
|
// pad milliseconds to have three digits.
|
||||||
|
return year + "-" + result.slice(0, 2).join("-") + "T" + result.slice(2).join(":") + "." + |
||||||
|
("000" + this.getUTCMilliseconds()).slice(-3) + "Z"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.9.4.4
|
||||||
|
// http://es5.github.com/#x15.9.4.4
|
||||||
|
if (!Date.now) { |
||||||
|
Date.now = function now() { |
||||||
|
return new Date().getTime(); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.9.5.44
|
||||||
|
// http://es5.github.com/#x15.9.5.44
|
||||||
|
// This function provides a String representation of a Date object for use by
|
||||||
|
// JSON.stringify (15.12.3).
|
||||||
|
if (!Date.prototype.toJSON) { |
||||||
|
Date.prototype.toJSON = function toJSON(key) { |
||||||
|
// When the toJSON method is called with argument key, the following
|
||||||
|
// steps are taken:
|
||||||
|
|
||||||
|
// 1. Let O be the result of calling ToObject, giving it the this
|
||||||
|
// value as its argument.
|
||||||
|
// 2. Let tv be ToPrimitive(O, hint Number).
|
||||||
|
// 3. If tv is a Number and is not finite, return null.
|
||||||
|
// XXX
|
||||||
|
// 4. Let toISO be the result of calling the [[Get]] internal method of
|
||||||
|
// O with argument "toISOString".
|
||||||
|
// 5. If IsCallable(toISO) is false, throw a TypeError exception.
|
||||||
|
if (typeof this.toISOString != "function") { |
||||||
|
throw new TypeError('toISOString property is not callable'); |
||||||
|
} |
||||||
|
// 6. Return the result of calling the [[Call]] internal method of
|
||||||
|
// toISO with O as the this value and an empty argument list.
|
||||||
|
return this.toISOString(); |
||||||
|
|
||||||
|
// NOTE 1 The argument is ignored.
|
||||||
|
|
||||||
|
// NOTE 2 The toJSON function is intentionally generic; it does not
|
||||||
|
// require that its this value be a Date object. Therefore, it can be
|
||||||
|
// transferred to other kinds of objects for use as a method. However,
|
||||||
|
// it does require that any such object have a toISOString method. An
|
||||||
|
// object is free to use the argument key to filter its
|
||||||
|
// stringification.
|
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// ES5 15.9.4.2
|
||||||
|
// http://es5.github.com/#x15.9.4.2
|
||||||
|
// based on work shared by Daniel Friesen (dantman)
|
||||||
|
// http://gist.github.com/303249
|
||||||
|
if (!Date.parse || Date.parse("+275760-09-13T00:00:00.000Z") !== 8.64e15) { |
||||||
|
// XXX global assignment won't work in embeddings that use
|
||||||
|
// an alternate object for the context.
|
||||||
|
Date = (function(NativeDate) { |
||||||
|
|
||||||
|
// Date.length === 7
|
||||||
|
var Date = function Date(Y, M, D, h, m, s, ms) { |
||||||
|
var length = arguments.length; |
||||||
|
if (this instanceof NativeDate) { |
||||||
|
var date = length == 1 && String(Y) === Y ? // isString(Y)
|
||||||
|
// We explicitly pass it through parse:
|
||||||
|
new NativeDate(Date.parse(Y)) : |
||||||
|
// We have to manually make calls depending on argument
|
||||||
|
// length here
|
||||||
|
length >= 7 ? new NativeDate(Y, M, D, h, m, s, ms) : |
||||||
|
length >= 6 ? new NativeDate(Y, M, D, h, m, s) : |
||||||
|
length >= 5 ? new NativeDate(Y, M, D, h, m) : |
||||||
|
length >= 4 ? new NativeDate(Y, M, D, h) : |
||||||
|
length >= 3 ? new NativeDate(Y, M, D) : |
||||||
|
length >= 2 ? new NativeDate(Y, M) : |
||||||
|
length >= 1 ? new NativeDate(Y) : |
||||||
|
new NativeDate(); |
||||||
|
// Prevent mixups with unfixed Date object
|
||||||
|
date.constructor = Date; |
||||||
|
return date; |
||||||
|
} |
||||||
|
return NativeDate.apply(this, arguments); |
||||||
|
}; |
||||||
|
|
||||||
|
// 15.9.1.15 Date Time String Format.
|
||||||
|
var isoDateExpression = new RegExp("^" + |
||||||
|
"(\\d{4}|[\+\-]\\d{6})" + // four-digit year capture or sign + 6-digit extended year
|
||||||
|
"(?:-(\\d{2})" + // optional month capture
|
||||||
|
"(?:-(\\d{2})" + // optional day capture
|
||||||
|
"(?:" + // capture hours:minutes:seconds.milliseconds
|
||||||
|
"T(\\d{2})" + // hours capture
|
||||||
|
":(\\d{2})" + // minutes capture
|
||||||
|
"(?:" + // optional :seconds.milliseconds
|
||||||
|
":(\\d{2})" + // seconds capture
|
||||||
|
"(?:\\.(\\d{3}))?" + // milliseconds capture
|
||||||
|
")?" + |
||||||
|
"(?:" + // capture UTC offset component
|
||||||
|
"Z|" + // UTC capture
|
||||||
|
"(?:" + // offset specifier +/-hours:minutes
|
||||||
|
"([-+])" + // sign capture
|
||||||
|
"(\\d{2})" + // hours offset capture
|
||||||
|
":(\\d{2})" + // minutes offset capture
|
||||||
|
")" + |
||||||
|
")?)?)?)?" + |
||||||
|
"$"); |
||||||
|
|
||||||
|
// Copy any custom methods a 3rd party library may have added
|
||||||
|
for (var key in NativeDate) { |
||||||
|
Date[key] = NativeDate[key]; |
||||||
|
} |
||||||
|
|
||||||
|
// Copy "native" methods explicitly; they may be non-enumerable
|
||||||
|
Date.now = NativeDate.now; |
||||||
|
Date.UTC = NativeDate.UTC; |
||||||
|
Date.prototype = NativeDate.prototype; |
||||||
|
Date.prototype.constructor = Date; |
||||||
|
|
||||||
|
// Upgrade Date.parse to handle simplified ISO 8601 strings
|
||||||
|
Date.parse = function parse(string) { |
||||||
|
var match = isoDateExpression.exec(string); |
||||||
|
if (match) { |
||||||
|
match.shift(); // kill match[0], the full match
|
||||||
|
// parse months, days, hours, minutes, seconds, and milliseconds
|
||||||
|
for (var i = 1; i < 7; i++) { |
||||||
|
// provide default values if necessary
|
||||||
|
match[i] = +(match[i] || (i < 3 ? 1 : 0)); |
||||||
|
// match[1] is the month. Months are 0-11 in JavaScript
|
||||||
|
// `Date` objects, but 1-12 in ISO notation, so we
|
||||||
|
// decrement.
|
||||||
|
if (i == 1) { |
||||||
|
match[i]--; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// parse the UTC offset component
|
||||||
|
var minuteOffset = +match.pop(), hourOffset = +match.pop(), sign = match.pop(); |
||||||
|
|
||||||
|
// compute the explicit time zone offset if specified
|
||||||
|
var offset = 0; |
||||||
|
if (sign) { |
||||||
|
// detect invalid offsets and return early
|
||||||
|
if (hourOffset > 23 || minuteOffset > 59) { |
||||||
|
return NaN; |
||||||
|
} |
||||||
|
|
||||||
|
// express the provided time zone offset in minutes. The offset is
|
||||||
|
// negative for time zones west of UTC; positive otherwise.
|
||||||
|
offset = (hourOffset * 60 + minuteOffset) * 6e4 * (sign == "+" ? -1 : 1); |
||||||
|
} |
||||||
|
|
||||||
|
// Date.UTC for years between 0 and 99 converts year to 1900 + year
|
||||||
|
// The Gregorian calendar has a 400-year cycle, so
|
||||||
|
// to Date.UTC(year + 400, .... ) - 12622780800000 == Date.UTC(year, ...),
|
||||||
|
// where 12622780800000 - number of milliseconds in Gregorian calendar 400 years
|
||||||
|
var year = +match[0]; |
||||||
|
if (0 <= year && year <= 99) { |
||||||
|
match[0] = year + 400; |
||||||
|
return NativeDate.UTC.apply(this, match) + offset - 12622780800000; |
||||||
|
} |
||||||
|
|
||||||
|
// compute a new UTC date value, accounting for the optional offset
|
||||||
|
return NativeDate.UTC.apply(this, match) + offset; |
||||||
|
} |
||||||
|
return NativeDate.parse.apply(this, arguments); |
||||||
|
}; |
||||||
|
|
||||||
|
return Date; |
||||||
|
})(Date); |
||||||
|
} |
||||||
|
|
||||||
|
//
|
||||||
|
// String
|
||||||
|
// ======
|
||||||
|
//
|
||||||
|
|
||||||
|
// ES5 15.5.4.20
|
||||||
|
// http://es5.github.com/#x15.5.4.20
|
||||||
|
var ws = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003" + |
||||||
|
"\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028" + |
||||||
|
"\u2029\uFEFF"; |
||||||
|
if (!String.prototype.trim || ws.trim()) { |
||||||
|
// http://blog.stevenlevithan.com/archives/faster-trim-javascript
|
||||||
|
// http://perfectionkills.com/whitespace-deviations/
|
||||||
|
ws = "[" + ws + "]"; |
||||||
|
var trimBeginRegexp = new RegExp("^" + ws + ws + "*"), |
||||||
|
trimEndRegexp = new RegExp(ws + ws + "*$"); |
||||||
|
String.prototype.trim = function trim() { |
||||||
|
if (this === undefined || this === null) { |
||||||
|
throw new TypeError("can't convert "+this+" to object"); |
||||||
|
} |
||||||
|
return String(this).replace(trimBeginRegexp, "").replace(trimEndRegexp, ""); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
//
|
||||||
|
// Util
|
||||||
|
// ======
|
||||||
|
//
|
||||||
|
|
||||||
|
// ES5 9.4
|
||||||
|
// http://es5.github.com/#x9.4
|
||||||
|
// http://jsperf.com/to-integer
|
||||||
|
var toInteger = function (n) { |
||||||
|
n = +n; |
||||||
|
if (n !== n) { // isNaN
|
||||||
|
n = 0; |
||||||
|
} else if (n !== 0 && n !== (1/0) && n !== -(1/0)) { |
||||||
|
n = (n > 0 || -1) * Math.floor(Math.abs(n)); |
||||||
|
} |
||||||
|
return n; |
||||||
|
}; |
||||||
|
|
||||||
|
var prepareString = "a"[0] != "a"; |
||||||
|
// ES5 9.9
|
||||||
|
// http://es5.github.com/#x9.9
|
||||||
|
var toObject = function (o) { |
||||||
|
if (o == null) { // this matches both null and undefined
|
||||||
|
throw new TypeError("can't convert "+o+" to object"); |
||||||
|
} |
||||||
|
// If the implementation doesn't support by-index access of
|
||||||
|
// string characters (ex. IE < 9), split the string
|
||||||
|
if (prepareString && typeof o == "string" && o) { |
||||||
|
return o.split(""); |
||||||
|
} |
||||||
|
return Object(o); |
||||||
|
}; |
||||||
|
|
||||||
|
}); |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
(function(f){"function"==typeof define?define(f):"function"==typeof YUI?YUI.add("es5",f):f()})(function(){Function.prototype.bind||(Function.prototype.bind=function(d){var c=this;if("function"!=typeof c)throw new TypeError("Function.prototype.bind called on incompatible "+c);var a=n.call(arguments,1),b=function(){if(this instanceof b){var e=function(){};e.prototype=c.prototype;var e=new e,i=c.apply(e,a.concat(n.call(arguments)));return Object(i)===i?i:e}return c.apply(d,a.concat(n.call(arguments)))}; |
||||||
|
return b});var f=Function.prototype.call,m=Object.prototype,n=Array.prototype.slice,l=f.bind(m.toString),o=f.bind(m.hasOwnProperty);o(m,"__defineGetter__")&&(f.bind(m.__defineGetter__),f.bind(m.__defineSetter__),f.bind(m.__lookupGetter__),f.bind(m.__lookupSetter__));Array.isArray||(Array.isArray=function(d){return l(d)=="[object Array]"});Array.prototype.forEach||(Array.prototype.forEach=function(d,c){var a=j(this),b=-1,e=a.length>>>0;if(l(d)!="[object Function]")throw new TypeError;for(;++b<e;)b in |
||||||
|
a&&d.call(c,a[b],b,a)});Array.prototype.map||(Array.prototype.map=function(d,c){var a=j(this),b=a.length>>>0,e=Array(b);if(l(d)!="[object Function]")throw new TypeError(d+" is not a function");for(var i=0;i<b;i++)i in a&&(e[i]=d.call(c,a[i],i,a));return e});Array.prototype.filter||(Array.prototype.filter=function(d,c){var a=j(this),b=a.length>>>0,e=[],i;if(l(d)!="[object Function]")throw new TypeError(d+" is not a function");for(var f=0;f<b;f++)if(f in a){i=a[f];d.call(c,i,f,a)&&e.push(i)}return e}); |
||||||
|
Array.prototype.every||(Array.prototype.every=function(d,c){var a=j(this),b=a.length>>>0;if(l(d)!="[object Function]")throw new TypeError(d+" is not a function");for(var e=0;e<b;e++)if(e in a&&!d.call(c,a[e],e,a))return false;return true});Array.prototype.some||(Array.prototype.some=function(d,c){var a=j(this),b=a.length>>>0;if(l(d)!="[object Function]")throw new TypeError(d+" is not a function");for(var e=0;e<b;e++)if(e in a&&d.call(c,a[e],e,a))return true;return false});Array.prototype.reduce|| |
||||||
|
(Array.prototype.reduce=function(d){var c=j(this),a=c.length>>>0;if(l(d)!="[object Function]")throw new TypeError(d+" is not a function");if(!a&&arguments.length==1)throw new TypeError("reduce of empty array with no initial value");var b=0,e;if(arguments.length>=2)e=arguments[1];else{do{if(b in c){e=c[b++];break}if(++b>=a)throw new TypeError("reduce of empty array with no initial value");}while(1)}for(;b<a;b++)b in c&&(e=d.call(void 0,e,c[b],b,c));return e});Array.prototype.reduceRight||(Array.prototype.reduceRight= |
||||||
|
function(d){var c=j(this),a=c.length>>>0;if(l(d)!="[object Function]")throw new TypeError(d+" is not a function");if(!a&&arguments.length==1)throw new TypeError("reduceRight of empty array with no initial value");var b,a=a-1;if(arguments.length>=2)b=arguments[1];else{do{if(a in c){b=c[a--];break}if(--a<0)throw new TypeError("reduceRight of empty array with no initial value");}while(1)}do a in this&&(b=d.call(void 0,b,c[a],a,c));while(a--);return b});Array.prototype.indexOf||(Array.prototype.indexOf= |
||||||
|
function(d){var c=j(this),a=c.length>>>0;if(!a)return-1;var b=0;arguments.length>1&&(b=p(arguments[1]));for(b=b>=0?b:Math.max(0,a+b);b<a;b++)if(b in c&&c[b]===d)return b;return-1});Array.prototype.lastIndexOf||(Array.prototype.lastIndexOf=function(d){var c=j(this),a=c.length>>>0;if(!a)return-1;var b=a-1;arguments.length>1&&(b=Math.min(b,p(arguments[1])));for(b=b>=0?b:a-Math.abs(b);b>=0;b--)if(b in c&&d===c[b])return b;return-1});if(!Object.keys){var q=!0,r="toString toLocaleString valueOf hasOwnProperty isPrototypeOf propertyIsEnumerable constructor".split(" "), |
||||||
|
s=r.length,t;for(t in{toString:null})q=!1;Object.keys=function(d){if(typeof d!="object"&&typeof d!="function"||d===null)throw new TypeError("Object.keys called on a non-object");var c=[],a;for(a in d)o(d,a)&&c.push(a);if(q)for(a=0;a<s;a++){var b=r[a];o(d,b)&&c.push(b)}return c}}if(!Date.prototype.toISOString||-1===(new Date(-621987552E5)).toISOString().indexOf("-000001"))Date.prototype.toISOString=function(){var d,c,a,b;if(!isFinite(this))throw new RangeError("Date.prototype.toISOString called on non-finite value."); |
||||||
|
d=[this.getUTCMonth()+1,this.getUTCDate(),this.getUTCHours(),this.getUTCMinutes(),this.getUTCSeconds()];b=this.getUTCFullYear();b=(b<0?"-":b>9999?"+":"")+("00000"+Math.abs(b)).slice(0<=b&&b<=9999?-4:-6);for(c=d.length;c--;){a=d[c];a<10&&(d[c]="0"+a)}return b+"-"+d.slice(0,2).join("-")+"T"+d.slice(2).join(":")+"."+("000"+this.getUTCMilliseconds()).slice(-3)+"Z"};Date.now||(Date.now=function(){return(new Date).getTime()});Date.prototype.toJSON||(Date.prototype.toJSON=function(){if(typeof this.toISOString!= |
||||||
|
"function")throw new TypeError("toISOString property is not callable");return this.toISOString()});if(!Date.parse||864E13!==Date.parse("+275760-09-13T00:00:00.000Z")){var g=Date,f=function c(a,b,e,f,h,j,l){var k=arguments.length;if(this instanceof g){k=k==1&&String(a)===a?new g(c.parse(a)):k>=7?new g(a,b,e,f,h,j,l):k>=6?new g(a,b,e,f,h,j):k>=5?new g(a,b,e,f,h):k>=4?new g(a,b,e,f):k>=3?new g(a,b,e):k>=2?new g(a,b):k>=1?new g(a):new g;k.constructor=c;return k}return g.apply(this,arguments)},u=RegExp("^(\\d{4}|[+-]\\d{6})(?:-(\\d{2})(?:-(\\d{2})(?:T(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.(\\d{3}))?)?(?:Z|(?:([-+])(\\d{2}):(\\d{2})))?)?)?)?$"), |
||||||
|
h;for(h in g)f[h]=g[h];f.now=g.now;f.UTC=g.UTC;f.prototype=g.prototype;f.prototype.constructor=f;f.parse=function(c){var a=u.exec(c);if(a){a.shift();for(var b=1;b<7;b++){a[b]=+(a[b]||(b<3?1:0));b==1&&a[b]--}var e=+a.pop(),f=+a.pop(),h=a.pop(),b=0;if(h){if(f>23||e>59)return NaN;b=(f*60+e)*6E4*(h=="+"?-1:1)}e=+a[0];if(0<=e&&e<=99){a[0]=e+400;return g.UTC.apply(this,a)+b-126227808E5}return g.UTC.apply(this,a)+b}return g.parse.apply(this,arguments)};Date=f}h="\t\n\x0B\f\r \u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\ufeff"; |
||||||
|
if(!String.prototype.trim||h.trim()){h="["+h+"]";var v=RegExp("^"+h+h+"*"),w=RegExp(h+h+"*$");String.prototype.trim=function(){if(this===void 0||this===null)throw new TypeError("can't convert "+this+" to object");return String(this).replace(v,"").replace(w,"")}}var p=function(c){c=+c;c!==c?c=0:c!==0&&(c!==1/0&&c!==-(1/0))&&(c=(c>0||-1)*Math.floor(Math.abs(c)));return c},x="a"!="a"[0],j=function(c){if(c==null)throw new TypeError("can't convert "+c+" to object");return x&&typeof c=="string"&&c?c.split(""): |
||||||
|
Object(c)}}); |
||||||
@ -0,0 +1,31 @@ |
|||||||
|
{ |
||||||
|
"name": "es5-shim", |
||||||
|
"version": "2.0.0", |
||||||
|
"description": "ES5 as implementable on previous engines", |
||||||
|
"homepage": "http://github.com/kriskowal/es5-shim/", |
||||||
|
"contributors": [ |
||||||
|
"Kris Kowal <kris@cixar.com> (http://github.com/kriskowal/)", |
||||||
|
"Sami Samhuri <sami.samhuri@gmail.com> (http://samhuri.net/)", |
||||||
|
"Florian Schäfer <florian.schaefer@gmail.com> (http://github.com/fschaefer)", |
||||||
|
"Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)", |
||||||
|
"Kit Cambridge <kitcambridge@gmail.com> (http://kitcambridge.github.com)" |
||||||
|
], |
||||||
|
"bugs": { |
||||||
|
"mail": "kris@cixar.com", |
||||||
|
"url": "http://github.com/kriskowal/es5-shim/issues" |
||||||
|
}, |
||||||
|
"licenses": [ |
||||||
|
{ |
||||||
|
"type": "MIT", |
||||||
|
"url": "http://github.com/kriskowal/es5-shim/raw/master/LICENSE" |
||||||
|
} |
||||||
|
], |
||||||
|
"main": "es5-shim.js", |
||||||
|
"repository": { |
||||||
|
"type": "git", |
||||||
|
"url": "http://github.com/kriskowal/es5-shim.git" |
||||||
|
}, |
||||||
|
"engines": { |
||||||
|
"node": ">=0.2.0" |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,59 @@ |
|||||||
|
// This methods allows the killing of built-in functions,
|
||||||
|
// so the shim can take over with that implementation
|
||||||
|
var HLP = (function() { |
||||||
|
"use strict"; |
||||||
|
var kill; |
||||||
|
|
||||||
|
kill = function(_class, methods) { |
||||||
|
/*if(!Array.isArray(methods)) |
||||||
|
return;*/ |
||||||
|
if(!_class.originals) |
||||||
|
_class.originals = {}; |
||||||
|
|
||||||
|
for (var i = 0, len = methods.length; i < len; i++) { |
||||||
|
var obj = methods[i]; |
||||||
|
_class.originals[obj] = _class[obj]; |
||||||
|
delete _class[obj]; |
||||||
|
if (obj in _class) { |
||||||
|
// try something more aggressive since V8 at least
|
||||||
|
// appears to ignore the delete.
|
||||||
|
_class[obj] = null; |
||||||
|
if (_class[obj]) { |
||||||
|
console.log("Couln't overwrite", obj, "of", _class); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
return { kill: kill }; |
||||||
|
}()); |
||||||
|
|
||||||
|
HLP.kill(Function.prototype, [ |
||||||
|
'bind' |
||||||
|
]); |
||||||
|
|
||||||
|
HLP.kill(Array, [ |
||||||
|
'isArray' |
||||||
|
]); |
||||||
|
|
||||||
|
HLP.kill(String.prototype, [ |
||||||
|
"trim" |
||||||
|
]); |
||||||
|
|
||||||
|
HLP.kill(Object, [ |
||||||
|
'keys' |
||||||
|
]); |
||||||
|
|
||||||
|
HLP.kill(Date, [ |
||||||
|
'now', 'parse' |
||||||
|
]); |
||||||
|
|
||||||
|
HLP.kill(Date.prototype, [ |
||||||
|
"toJSON", "toISOString" |
||||||
|
]); |
||||||
|
|
||||||
|
HLP.kill(Array.prototype, [ |
||||||
|
'forEach', 'some', 'every',
|
||||||
|
'indexOf', 'lastIndexOf',
|
||||||
|
'map', 'filter',
|
||||||
|
'reduce', 'reduceRight' |
||||||
|
]); |
||||||
@ -0,0 +1,34 @@ |
|||||||
|
beforeEach(function() { |
||||||
|
this.addMatchers({ |
||||||
|
toExactlyMatch: function(expected) { |
||||||
|
var a1, a2, |
||||||
|
l, i, |
||||||
|
key, |
||||||
|
actual = this.actual; |
||||||
|
|
||||||
|
var getKeys = function(o) { |
||||||
|
var a = []; |
||||||
|
for(key in o) { |
||||||
|
if(o.hasOwnProperty(key)) { |
||||||
|
a.push(key); |
||||||
|
} |
||||||
|
} |
||||||
|
return a; |
||||||
|
} |
||||||
|
a1 = getKeys(actual); |
||||||
|
a2 = getKeys(expected); |
||||||
|
|
||||||
|
l = a1.length; |
||||||
|
if(l !== a2.length) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
for(i = 0; i < l; i++) { |
||||||
|
key = a1[i]; |
||||||
|
expect(key).toEqual(a2[i]); |
||||||
|
expect(actual[key]).toEqual(expected[key]); |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
}) |
||||||
|
}); |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
function implement() { |
||||||
|
throw 'Not implemented'; |
||||||
|
} |
||||||
@ -0,0 +1,62 @@ |
|||||||
|
<!DOCTYPE HTML> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title>Jasmine Spec Runner</title> |
||||||
|
|
||||||
|
<link rel="shortcut icon" type="image/png" href="lib/jasmine_favicon.png"> |
||||||
|
|
||||||
|
<link rel="stylesheet" type="text/css" href="lib/jasmine.css"> |
||||||
|
<script type="text/javascript" src="lib/jasmine.js"></script> |
||||||
|
<script type="text/javascript" src="lib/jasmine-html.js"></script> |
||||||
|
<script type="text/javascript" src="lib/json2.js"></script> |
||||||
|
|
||||||
|
<!-- include helper files here... --> |
||||||
|
<script src="helpers/h.js"></script> |
||||||
|
<script src="helpers/h-kill.js"></script> |
||||||
|
<script src="helpers/h-matchers.js"></script> |
||||||
|
|
||||||
|
<!-- include source files here... --> |
||||||
|
<script src="../es5-shim.js"></script> |
||||||
|
|
||||||
|
<!-- include spec files here... --> |
||||||
|
<script src="spec/s-array.js"></script> |
||||||
|
<script src="spec/s-function.js"></script> |
||||||
|
<script src="spec/s-string.js"></script> |
||||||
|
<script src="spec/s-object.js"></script> |
||||||
|
<script src="spec/s-date.js"></script> |
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript"> |
||||||
|
(function() { |
||||||
|
var jasmineEnv = jasmine.getEnv(); |
||||||
|
jasmineEnv.updateInterval = 1000; |
||||||
|
|
||||||
|
var trivialReporter = new jasmine.TrivialReporter(); |
||||||
|
|
||||||
|
jasmineEnv.addReporter(trivialReporter); |
||||||
|
|
||||||
|
jasmineEnv.specFilter = function(spec) { |
||||||
|
return trivialReporter.specFilter(spec); |
||||||
|
}; |
||||||
|
|
||||||
|
var currentWindowOnload = window.onload; |
||||||
|
|
||||||
|
window.onload = function() { |
||||||
|
if (currentWindowOnload) { |
||||||
|
currentWindowOnload(); |
||||||
|
} |
||||||
|
execJasmine(); |
||||||
|
}; |
||||||
|
|
||||||
|
function execJasmine() { |
||||||
|
jasmineEnv.execute(); |
||||||
|
} |
||||||
|
|
||||||
|
})(); |
||||||
|
</script> |
||||||
|
|
||||||
|
</head> |
||||||
|
|
||||||
|
<body> |
||||||
|
</body> |
||||||
|
</html> |
||||||
@ -0,0 +1,190 @@ |
|||||||
|
jasmine.TrivialReporter = function(doc) { |
||||||
|
this.document = doc || document; |
||||||
|
this.suiteDivs = {}; |
||||||
|
this.logRunningSpecs = false; |
||||||
|
}; |
||||||
|
|
||||||
|
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) { |
||||||
|
var el = document.createElement(type); |
||||||
|
|
||||||
|
for (var i = 2; i < arguments.length; i++) { |
||||||
|
var child = arguments[i]; |
||||||
|
|
||||||
|
if (typeof child === 'string') { |
||||||
|
el.appendChild(document.createTextNode(child)); |
||||||
|
} else { |
||||||
|
if (child) { el.appendChild(child); } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for (var attr in attrs) { |
||||||
|
if (attr == "className") { |
||||||
|
el[attr] = attrs[attr]; |
||||||
|
} else { |
||||||
|
el.setAttribute(attr, attrs[attr]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return el; |
||||||
|
}; |
||||||
|
|
||||||
|
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { |
||||||
|
var showPassed, showSkipped; |
||||||
|
|
||||||
|
this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' }, |
||||||
|
this.createDom('div', { className: 'banner' }, |
||||||
|
this.createDom('div', { className: 'logo' }, |
||||||
|
this.createDom('span', { className: 'title' }, "Jasmine"), |
||||||
|
this.createDom('span', { className: 'version' }, runner.env.versionString())), |
||||||
|
this.createDom('div', { className: 'options' }, |
||||||
|
"Show ", |
||||||
|
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }), |
||||||
|
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "), |
||||||
|
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }), |
||||||
|
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped") |
||||||
|
) |
||||||
|
), |
||||||
|
|
||||||
|
this.runnerDiv = this.createDom('div', { className: 'runner running' }, |
||||||
|
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"), |
||||||
|
this.runnerMessageSpan = this.createDom('span', {}, "Running..."), |
||||||
|
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, "")) |
||||||
|
); |
||||||
|
|
||||||
|
this.document.body.appendChild(this.outerDiv); |
||||||
|
|
||||||
|
var suites = runner.suites(); |
||||||
|
for (var i = 0; i < suites.length; i++) { |
||||||
|
var suite = suites[i]; |
||||||
|
var suiteDiv = this.createDom('div', { className: 'suite' }, |
||||||
|
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"), |
||||||
|
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description)); |
||||||
|
this.suiteDivs[suite.id] = suiteDiv; |
||||||
|
var parentDiv = this.outerDiv; |
||||||
|
if (suite.parentSuite) { |
||||||
|
parentDiv = this.suiteDivs[suite.parentSuite.id]; |
||||||
|
} |
||||||
|
parentDiv.appendChild(suiteDiv); |
||||||
|
} |
||||||
|
|
||||||
|
this.startedAt = new Date(); |
||||||
|
|
||||||
|
var self = this; |
||||||
|
showPassed.onclick = function(evt) { |
||||||
|
if (showPassed.checked) { |
||||||
|
self.outerDiv.className += ' show-passed'; |
||||||
|
} else { |
||||||
|
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, ''); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
showSkipped.onclick = function(evt) { |
||||||
|
if (showSkipped.checked) { |
||||||
|
self.outerDiv.className += ' show-skipped'; |
||||||
|
} else { |
||||||
|
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, ''); |
||||||
|
} |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) { |
||||||
|
var results = runner.results(); |
||||||
|
var className = (results.failedCount > 0) ? "runner failed" : "runner passed"; |
||||||
|
this.runnerDiv.setAttribute("class", className); |
||||||
|
//do it twice for IE
|
||||||
|
this.runnerDiv.setAttribute("className", className); |
||||||
|
var specs = runner.specs(); |
||||||
|
var specCount = 0; |
||||||
|
for (var i = 0; i < specs.length; i++) { |
||||||
|
if (this.specFilter(specs[i])) { |
||||||
|
specCount++; |
||||||
|
} |
||||||
|
} |
||||||
|
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s"); |
||||||
|
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"; |
||||||
|
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild); |
||||||
|
|
||||||
|
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString())); |
||||||
|
}; |
||||||
|
|
||||||
|
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) { |
||||||
|
var results = suite.results(); |
||||||
|
var status = results.passed() ? 'passed' : 'failed'; |
||||||
|
if (results.totalCount === 0) { // todo: change this to check results.skipped
|
||||||
|
status = 'skipped'; |
||||||
|
} |
||||||
|
this.suiteDivs[suite.id].className += " " + status; |
||||||
|
}; |
||||||
|
|
||||||
|
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) { |
||||||
|
if (this.logRunningSpecs) { |
||||||
|
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) { |
||||||
|
var results = spec.results(); |
||||||
|
var status = results.passed() ? 'passed' : 'failed'; |
||||||
|
if (results.skipped) { |
||||||
|
status = 'skipped'; |
||||||
|
} |
||||||
|
var specDiv = this.createDom('div', { className: 'spec ' + status }, |
||||||
|
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"), |
||||||
|
this.createDom('a', { |
||||||
|
className: 'description', |
||||||
|
href: '?spec=' + encodeURIComponent(spec.getFullName()), |
||||||
|
title: spec.getFullName() |
||||||
|
}, spec.description)); |
||||||
|
|
||||||
|
|
||||||
|
var resultItems = results.getItems(); |
||||||
|
var messagesDiv = this.createDom('div', { className: 'messages' }); |
||||||
|
for (var i = 0; i < resultItems.length; i++) { |
||||||
|
var result = resultItems[i]; |
||||||
|
|
||||||
|
if (result.type == 'log') { |
||||||
|
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); |
||||||
|
} else if (result.type == 'expect' && result.passed && !result.passed()) { |
||||||
|
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); |
||||||
|
|
||||||
|
if (result.trace.stack) { |
||||||
|
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (messagesDiv.childNodes.length > 0) { |
||||||
|
specDiv.appendChild(messagesDiv); |
||||||
|
} |
||||||
|
|
||||||
|
this.suiteDivs[spec.suite.id].appendChild(specDiv); |
||||||
|
}; |
||||||
|
|
||||||
|
jasmine.TrivialReporter.prototype.log = function() { |
||||||
|
var console = jasmine.getGlobal().console; |
||||||
|
if (console && console.log) { |
||||||
|
if (console.log.apply) { |
||||||
|
console.log.apply(console, arguments); |
||||||
|
} else { |
||||||
|
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
|
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
jasmine.TrivialReporter.prototype.getLocation = function() { |
||||||
|
return this.document.location; |
||||||
|
}; |
||||||
|
|
||||||
|
jasmine.TrivialReporter.prototype.specFilter = function(spec) { |
||||||
|
var paramMap = {}; |
||||||
|
var params = this.getLocation().search.substring(1).split('&'); |
||||||
|
for (var i = 0; i < params.length; i++) { |
||||||
|
var p = params[i].split('='); |
||||||
|
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); |
||||||
|
} |
||||||
|
|
||||||
|
if (!paramMap.spec) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
return spec.getFullName().indexOf(paramMap.spec) === 0; |
||||||
|
}; |
||||||
@ -0,0 +1,166 @@ |
|||||||
|
body { |
||||||
|
font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
.jasmine_reporter a:visited, .jasmine_reporter a { |
||||||
|
color: #303; |
||||||
|
} |
||||||
|
|
||||||
|
.jasmine_reporter a:hover, .jasmine_reporter a:active { |
||||||
|
color: blue; |
||||||
|
} |
||||||
|
|
||||||
|
.run_spec { |
||||||
|
float:right; |
||||||
|
padding-right: 5px; |
||||||
|
font-size: .8em; |
||||||
|
text-decoration: none; |
||||||
|
} |
||||||
|
|
||||||
|
.jasmine_reporter { |
||||||
|
margin: 0 5px; |
||||||
|
} |
||||||
|
|
||||||
|
.banner { |
||||||
|
color: #303; |
||||||
|
background-color: #fef; |
||||||
|
padding: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
.logo { |
||||||
|
float: left; |
||||||
|
font-size: 1.1em; |
||||||
|
padding-left: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
.logo .version { |
||||||
|
font-size: .6em; |
||||||
|
padding-left: 1em; |
||||||
|
} |
||||||
|
|
||||||
|
.runner.running { |
||||||
|
background-color: yellow; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
.options { |
||||||
|
text-align: right; |
||||||
|
font-size: .8em; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.suite { |
||||||
|
border: 1px outset gray; |
||||||
|
margin: 5px 0; |
||||||
|
padding-left: 1em; |
||||||
|
} |
||||||
|
|
||||||
|
.suite .suite { |
||||||
|
margin: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
.suite.passed { |
||||||
|
background-color: #dfd; |
||||||
|
} |
||||||
|
|
||||||
|
.suite.failed { |
||||||
|
background-color: #fdd; |
||||||
|
} |
||||||
|
|
||||||
|
.spec { |
||||||
|
margin: 5px; |
||||||
|
padding-left: 1em; |
||||||
|
clear: both; |
||||||
|
} |
||||||
|
|
||||||
|
.spec.failed, .spec.passed, .spec.skipped { |
||||||
|
padding-bottom: 5px; |
||||||
|
border: 1px solid gray; |
||||||
|
} |
||||||
|
|
||||||
|
.spec.failed { |
||||||
|
background-color: #fbb; |
||||||
|
border-color: red; |
||||||
|
} |
||||||
|
|
||||||
|
.spec.passed { |
||||||
|
background-color: #bfb; |
||||||
|
border-color: green; |
||||||
|
} |
||||||
|
|
||||||
|
.spec.skipped { |
||||||
|
background-color: #bbb; |
||||||
|
} |
||||||
|
|
||||||
|
.messages { |
||||||
|
border-left: 1px dashed gray; |
||||||
|
padding-left: 1em; |
||||||
|
padding-right: 1em; |
||||||
|
} |
||||||
|
|
||||||
|
.passed { |
||||||
|
background-color: #cfc; |
||||||
|
display: none; |
||||||
|
} |
||||||
|
|
||||||
|
.failed { |
||||||
|
background-color: #fbb; |
||||||
|
} |
||||||
|
|
||||||
|
.skipped { |
||||||
|
color: #777; |
||||||
|
background-color: #eee; |
||||||
|
display: none; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*.resultMessage {*/ |
||||||
|
/*white-space: pre;*/ |
||||||
|
/*}*/ |
||||||
|
|
||||||
|
.resultMessage span.result { |
||||||
|
display: block; |
||||||
|
line-height: 2em; |
||||||
|
color: black; |
||||||
|
} |
||||||
|
|
||||||
|
.resultMessage .mismatch { |
||||||
|
color: black; |
||||||
|
} |
||||||
|
|
||||||
|
.stackTrace { |
||||||
|
white-space: pre; |
||||||
|
font-size: .8em; |
||||||
|
margin-left: 10px; |
||||||
|
max-height: 5em; |
||||||
|
overflow: auto; |
||||||
|
border: 1px inset red; |
||||||
|
padding: 1em; |
||||||
|
background: #eef; |
||||||
|
} |
||||||
|
|
||||||
|
.finished-at { |
||||||
|
padding-left: 1em; |
||||||
|
font-size: .6em; |
||||||
|
} |
||||||
|
|
||||||
|
.show-passed .passed, |
||||||
|
.show-skipped .skipped { |
||||||
|
display: block; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#jasmine_content { |
||||||
|
position:fixed; |
||||||
|
right: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
.runner { |
||||||
|
border: 1px solid gray; |
||||||
|
display: block; |
||||||
|
margin: 5px 0; |
||||||
|
padding: 2px 0 2px 10px; |
||||||
|
} |
||||||
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 905 B |
@ -0,0 +1,478 @@ |
|||||||
|
/* |
||||||
|
http://www.JSON.org/json2.js
|
||||||
|
2009-08-17 |
||||||
|
|
||||||
|
Public Domain. |
||||||
|
|
||||||
|
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. |
||||||
|
|
||||||
|
See http://www.JSON.org/js.html
|
||||||
|
|
||||||
|
This file creates a global JSON object containing two methods: stringify |
||||||
|
and parse. |
||||||
|
|
||||||
|
JSON.stringify(value, replacer, space) |
||||||
|
value any JavaScript value, usually an object or array. |
||||||
|
|
||||||
|
replacer an optional parameter that determines how object |
||||||
|
values are stringified for objects. It can be a |
||||||
|
function or an array of strings. |
||||||
|
|
||||||
|
space an optional parameter that specifies the indentation |
||||||
|
of nested structures. If it is omitted, the text will |
||||||
|
be packed without extra whitespace. If it is a number, |
||||||
|
it will specify the number of spaces to indent at each |
||||||
|
level. If it is a string (such as '\t' or ' '), |
||||||
|
it contains the characters used to indent at each level. |
||||||
|
|
||||||
|
This method produces a JSON text from a JavaScript value. |
||||||
|
|
||||||
|
When an object value is found, if the object contains a toJSON |
||||||
|
method, its toJSON method will be called and the result will be |
||||||
|
stringified. A toJSON method does not serialize: it returns the |
||||||
|
value represented by the name/value pair that should be serialized, |
||||||
|
or undefined if nothing should be serialized. The toJSON method |
||||||
|
will be passed the key associated with the value, and this will be |
||||||
|
bound to the value |
||||||
|
|
||||||
|
For example, this would serialize Dates as ISO strings. |
||||||
|
|
||||||
|
Date.prototype.toJSON = function (key) { |
||||||
|
function f(n) { |
||||||
|
// Format integers to have at least two digits.
|
||||||
|
return n < 10 ? '0' + n : n; |
||||||
|
} |
||||||
|
|
||||||
|
return this.getUTCFullYear() + '-' + |
||||||
|
f(this.getUTCMonth() + 1) + '-' + |
||||||
|
f(this.getUTCDate()) + 'T' + |
||||||
|
f(this.getUTCHours()) + ':' + |
||||||
|
f(this.getUTCMinutes()) + ':' + |
||||||
|
f(this.getUTCSeconds()) + 'Z'; |
||||||
|
}; |
||||||
|
|
||||||
|
You can provide an optional replacer method. It will be passed the |
||||||
|
key and value of each member, with this bound to the containing |
||||||
|
object. The value that is returned from your method will be |
||||||
|
serialized. If your method returns undefined, then the member will |
||||||
|
be excluded from the serialization. |
||||||
|
|
||||||
|
If the replacer parameter is an array of strings, then it will be |
||||||
|
used to select the members to be serialized. It filters the results |
||||||
|
such that only members with keys listed in the replacer array are |
||||||
|
stringified. |
||||||
|
|
||||||
|
Values that do not have JSON representations, such as undefined or |
||||||
|
functions, will not be serialized. Such values in objects will be |
||||||
|
dropped; in arrays they will be replaced with null. You can use |
||||||
|
a replacer function to replace those with JSON values. |
||||||
|
JSON.stringify(undefined) returns undefined. |
||||||
|
|
||||||
|
The optional space parameter produces a stringification of the |
||||||
|
value that is filled with line breaks and indentation to make it |
||||||
|
easier to read. |
||||||
|
|
||||||
|
If the space parameter is a non-empty string, then that string will |
||||||
|
be used for indentation. If the space parameter is a number, then |
||||||
|
the indentation will be that many spaces. |
||||||
|
|
||||||
|
Example: |
||||||
|
|
||||||
|
text = JSON.stringify(['e', {pluribus: 'unum'}]); |
||||||
|
// text is '["e",{"pluribus":"unum"}]'
|
||||||
|
|
||||||
|
|
||||||
|
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); |
||||||
|
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
|
||||||
|
|
||||||
|
text = JSON.stringify([new Date()], function (key, value) { |
||||||
|
return this[key] instanceof Date ? |
||||||
|
'Date(' + this[key] + ')' : value; |
||||||
|
}); |
||||||
|
// text is '["Date(---current time---)"]'
|
||||||
|
|
||||||
|
|
||||||
|
JSON.parse(text, reviver) |
||||||
|
This method parses a JSON text to produce an object or array. |
||||||
|
It can throw a SyntaxError exception. |
||||||
|
|
||||||
|
The optional reviver parameter is a function that can filter and |
||||||
|
transform the results. It receives each of the keys and values, |
||||||
|
and its return value is used instead of the original value. |
||||||
|
If it returns what it received, then the structure is not modified. |
||||||
|
If it returns undefined then the member is deleted. |
||||||
|
|
||||||
|
Example: |
||||||
|
|
||||||
|
// Parse the text. Values that look like ISO date strings will
|
||||||
|
// be converted to Date objects.
|
||||||
|
|
||||||
|
myData = JSON.parse(text, function (key, value) { |
||||||
|
var a; |
||||||
|
if (typeof value === 'string') { |
||||||
|
a = |
||||||
|
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); |
||||||
|
if (a) { |
||||||
|
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], |
||||||
|
+a[5], +a[6])); |
||||||
|
} |
||||||
|
} |
||||||
|
return value; |
||||||
|
}); |
||||||
|
|
||||||
|
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { |
||||||
|
var d; |
||||||
|
if (typeof value === 'string' && |
||||||
|
value.slice(0, 5) === 'Date(' && |
||||||
|
value.slice(-1) === ')') { |
||||||
|
d = new Date(value.slice(5, -1)); |
||||||
|
if (d) { |
||||||
|
return d; |
||||||
|
} |
||||||
|
} |
||||||
|
return value; |
||||||
|
}); |
||||||
|
|
||||||
|
|
||||||
|
This is a reference implementation. You are free to copy, modify, or |
||||||
|
redistribute. |
||||||
|
|
||||||
|
This code should be minified before deployment. |
||||||
|
See http://javascript.crockford.com/jsmin.html
|
||||||
|
|
||||||
|
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO |
||||||
|
NOT CONTROL. |
||||||
|
*/ |
||||||
|
|
||||||
|
/*jslint evil: true */ |
||||||
|
|
||||||
|
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, |
||||||
|
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, |
||||||
|
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, |
||||||
|
lastIndex, length, parse, prototype, push, replace, slice, stringify, |
||||||
|
test, toJSON, toString, valueOf |
||||||
|
*/ |
||||||
|
|
||||||
|
"use strict"; |
||||||
|
|
||||||
|
// Create a JSON object only if one does not already exist. We create the
|
||||||
|
// methods in a closure to avoid creating global variables.
|
||||||
|
|
||||||
|
if (!this.JSON) { |
||||||
|
this.JSON = {}; |
||||||
|
} |
||||||
|
|
||||||
|
(function () { |
||||||
|
|
||||||
|
function f(n) { |
||||||
|
// Format integers to have at least two digits.
|
||||||
|
return n < 10 ? '0' + n : n; |
||||||
|
} |
||||||
|
|
||||||
|
if (typeof Date.prototype.toJSON !== 'function') { |
||||||
|
|
||||||
|
Date.prototype.toJSON = function (key) { |
||||||
|
|
||||||
|
return isFinite(this.valueOf()) ? |
||||||
|
this.getUTCFullYear() + '-' + |
||||||
|
f(this.getUTCMonth() + 1) + '-' + |
||||||
|
f(this.getUTCDate()) + 'T' + |
||||||
|
f(this.getUTCHours()) + ':' + |
||||||
|
f(this.getUTCMinutes()) + ':' + |
||||||
|
f(this.getUTCSeconds()) + 'Z' : null; |
||||||
|
}; |
||||||
|
|
||||||
|
String.prototype.toJSON = |
||||||
|
Number.prototype.toJSON = |
||||||
|
Boolean.prototype.toJSON = function (key) { |
||||||
|
return this.valueOf(); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, |
||||||
|
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, |
||||||
|
gap, |
||||||
|
indent, |
||||||
|
meta = { // table of character substitutions
|
||||||
|
'\b': '\\b', |
||||||
|
'\t': '\\t', |
||||||
|
'\n': '\\n', |
||||||
|
'\f': '\\f', |
||||||
|
'\r': '\\r', |
||||||
|
'"' : '\\"', |
||||||
|
'\\': '\\\\' |
||||||
|
}, |
||||||
|
rep; |
||||||
|
|
||||||
|
|
||||||
|
function quote(string) { |
||||||
|
|
||||||
|
// If the string contains no control characters, no quote characters, and no
|
||||||
|
// backslash characters, then we can safely slap some quotes around it.
|
||||||
|
// Otherwise we must also replace the offending characters with safe escape
|
||||||
|
// sequences.
|
||||||
|
|
||||||
|
escapable.lastIndex = 0; |
||||||
|
return escapable.test(string) ? |
||||||
|
'"' + string.replace(escapable, function (a) { |
||||||
|
var c = meta[a]; |
||||||
|
return typeof c === 'string' ? c : |
||||||
|
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); |
||||||
|
}) + '"' : |
||||||
|
'"' + string + '"'; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function str(key, holder) { |
||||||
|
// Produce a string from holder[key].
|
||||||
|
|
||||||
|
var i, // The loop counter.
|
||||||
|
k, // The member key.
|
||||||
|
v, // The member value.
|
||||||
|
length, |
||||||
|
mind = gap, |
||||||
|
partial, |
||||||
|
value = holder[key]; |
||||||
|
|
||||||
|
// If the value has a toJSON method, call it to obtain a replacement value.
|
||||||
|
|
||||||
|
if (value && typeof value === 'object' && |
||||||
|
typeof value.toJSON === 'function') { |
||||||
|
value = value.toJSON(key); |
||||||
|
} |
||||||
|
|
||||||
|
// If we were called with a replacer function, then call the replacer to
|
||||||
|
// obtain a replacement value.
|
||||||
|
|
||||||
|
if (typeof rep === 'function') { |
||||||
|
value = rep.call(holder, key, value); |
||||||
|
} |
||||||
|
|
||||||
|
// What happens next depends on the value's type.
|
||||||
|
|
||||||
|
switch (typeof value) { |
||||||
|
case 'string': |
||||||
|
return quote(value); |
||||||
|
|
||||||
|
case 'number': |
||||||
|
|
||||||
|
// JSON numbers must be finite. Encode non-finite numbers as null.
|
||||||
|
|
||||||
|
return isFinite(value) ? String(value) : 'null'; |
||||||
|
|
||||||
|
case 'boolean': |
||||||
|
case 'null': |
||||||
|
|
||||||
|
// If the value is a boolean or null, convert it to a string. Note:
|
||||||
|
// typeof null does not produce 'null'. The case is included here in
|
||||||
|
// the remote chance that this gets fixed someday.
|
||||||
|
|
||||||
|
return String(value); |
||||||
|
|
||||||
|
// If the type is 'object', we might be dealing with an object or an array or
|
||||||
|
// null.
|
||||||
|
|
||||||
|
case 'object': |
||||||
|
|
||||||
|
// Due to a specification blunder in ECMAScript, typeof null is 'object',
|
||||||
|
// so watch out for that case.
|
||||||
|
|
||||||
|
if (!value) { |
||||||
|
return 'null'; |
||||||
|
} |
||||||
|
|
||||||
|
// Make an array to hold the partial results of stringifying this object value.
|
||||||
|
|
||||||
|
gap += indent; |
||||||
|
partial = []; |
||||||
|
|
||||||
|
// Is the value an array?
|
||||||
|
|
||||||
|
if (Object.prototype.toString.apply(value) === '[object Array]') { |
||||||
|
|
||||||
|
// The value is an array. Stringify every element. Use null as a placeholder
|
||||||
|
// for non-JSON values.
|
||||||
|
|
||||||
|
length = value.length; |
||||||
|
for (i = 0; i < length; i += 1) { |
||||||
|
partial[i] = str(i, value) || 'null'; |
||||||
|
} |
||||||
|
|
||||||
|
// Join all of the elements together, separated with commas, and wrap them in
|
||||||
|
// brackets.
|
||||||
|
|
||||||
|
v = partial.length === 0 ? '[]' : |
||||||
|
gap ? '[\n' + gap + |
||||||
|
partial.join(',\n' + gap) + '\n' + |
||||||
|
mind + ']' : |
||||||
|
'[' + partial.join(',') + ']'; |
||||||
|
gap = mind; |
||||||
|
return v; |
||||||
|
} |
||||||
|
|
||||||
|
// If the replacer is an array, use it to select the members to be stringified.
|
||||||
|
|
||||||
|
if (rep && typeof rep === 'object') { |
||||||
|
length = rep.length; |
||||||
|
for (i = 0; i < length; i += 1) { |
||||||
|
k = rep[i]; |
||||||
|
if (typeof k === 'string') { |
||||||
|
v = str(k, value); |
||||||
|
if (v) { |
||||||
|
partial.push(quote(k) + (gap ? ': ' : ':') + v); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
|
||||||
|
// Otherwise, iterate through all of the keys in the object.
|
||||||
|
|
||||||
|
for (k in value) { |
||||||
|
if (Object.hasOwnProperty.call(value, k)) { |
||||||
|
v = str(k, value); |
||||||
|
if (v) { |
||||||
|
partial.push(quote(k) + (gap ? ': ' : ':') + v); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Join all of the member texts together, separated with commas,
|
||||||
|
// and wrap them in braces.
|
||||||
|
|
||||||
|
v = partial.length === 0 ? '{}' : |
||||||
|
gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + |
||||||
|
mind + '}' : '{' + partial.join(',') + '}'; |
||||||
|
gap = mind; |
||||||
|
return v; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// If the JSON object does not yet have a stringify method, give it one.
|
||||||
|
|
||||||
|
if (typeof JSON.stringify !== 'function') { |
||||||
|
JSON.stringify = function (value, replacer, space) { |
||||||
|
// The stringify method takes a value and an optional replacer, and an optional
|
||||||
|
// space parameter, and returns a JSON text. The replacer can be a function
|
||||||
|
// that can replace values, or an array of strings that will select the keys.
|
||||||
|
// A default replacer method can be provided. Use of the space parameter can
|
||||||
|
// produce text that is more easily readable.
|
||||||
|
|
||||||
|
var i; |
||||||
|
gap = ''; |
||||||
|
indent = ''; |
||||||
|
|
||||||
|
// If the space parameter is a number, make an indent string containing that
|
||||||
|
// many spaces.
|
||||||
|
|
||||||
|
if (typeof space === 'number') { |
||||||
|
for (i = 0; i < space; i += 1) { |
||||||
|
indent += ' '; |
||||||
|
} |
||||||
|
|
||||||
|
// If the space parameter is a string, it will be used as the indent string.
|
||||||
|
|
||||||
|
} else if (typeof space === 'string') { |
||||||
|
indent = space; |
||||||
|
} |
||||||
|
|
||||||
|
// If there is a replacer, it must be a function or an array.
|
||||||
|
// Otherwise, throw an error.
|
||||||
|
|
||||||
|
rep = replacer; |
||||||
|
if (replacer && typeof replacer !== 'function' && |
||||||
|
(typeof replacer !== 'object' || |
||||||
|
typeof replacer.length !== 'number')) { |
||||||
|
throw new Error('JSON.stringify'); |
||||||
|
} |
||||||
|
|
||||||
|
// Make a fake root object containing our value under the key of ''.
|
||||||
|
// Return the result of stringifying the value.
|
||||||
|
|
||||||
|
return str('', {'': value}); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// If the JSON object does not yet have a parse method, give it one.
|
||||||
|
|
||||||
|
if (typeof JSON.parse !== 'function') { |
||||||
|
JSON.parse = function (text, reviver) { |
||||||
|
|
||||||
|
// The parse method takes a text and an optional reviver function, and returns
|
||||||
|
// a JavaScript value if the text is a valid JSON text.
|
||||||
|
|
||||||
|
var j; |
||||||
|
|
||||||
|
function walk(holder, key) { |
||||||
|
|
||||||
|
// The walk method is used to recursively walk the resulting structure so
|
||||||
|
// that modifications can be made.
|
||||||
|
|
||||||
|
var k, v, value = holder[key]; |
||||||
|
if (value && typeof value === 'object') { |
||||||
|
for (k in value) { |
||||||
|
if (Object.hasOwnProperty.call(value, k)) { |
||||||
|
v = walk(value, k); |
||||||
|
if (v !== undefined) { |
||||||
|
value[k] = v; |
||||||
|
} else { |
||||||
|
delete value[k]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return reviver.call(holder, key, value); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// Parsing happens in four stages. In the first stage, we replace certain
|
||||||
|
// Unicode characters with escape sequences. JavaScript handles many characters
|
||||||
|
// incorrectly, either silently deleting them, or treating them as line endings.
|
||||||
|
|
||||||
|
cx.lastIndex = 0; |
||||||
|
if (cx.test(text)) { |
||||||
|
text = text.replace(cx, function (a) { |
||||||
|
return '\\u' + |
||||||
|
('0000' + a.charCodeAt(0).toString(16)).slice(-4); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
// In the second stage, we run the text against regular expressions that look
|
||||||
|
// for non-JSON patterns. We are especially concerned with '()' and 'new'
|
||||||
|
// because they can cause invocation, and '=' because it can cause mutation.
|
||||||
|
// But just to be safe, we want to reject all unexpected forms.
|
||||||
|
|
||||||
|
// We split the second stage into 4 regexp operations in order to work around
|
||||||
|
// crippling inefficiencies in IE's and Safari's regexp engines. First we
|
||||||
|
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
|
||||||
|
// replace all simple value tokens with ']' characters. Third, we delete all
|
||||||
|
// open brackets that follow a colon or comma or that begin the text. Finally,
|
||||||
|
// we look to see that the remaining characters are only whitespace or ']' or
|
||||||
|
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
|
||||||
|
|
||||||
|
if (/^[\],:{}\s]*$/. |
||||||
|
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). |
||||||
|
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). |
||||||
|
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { |
||||||
|
|
||||||
|
// In the third stage we use the eval function to compile the text into a
|
||||||
|
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
|
||||||
|
// in JavaScript: it can begin a block or an object literal. We wrap the text
|
||||||
|
// in parens to eliminate the ambiguity.
|
||||||
|
|
||||||
|
j = eval('(' + text + ')'); |
||||||
|
|
||||||
|
// In the optional fourth stage, we recursively walk the new structure, passing
|
||||||
|
// each name/value pair to a reviver function for possible transformation.
|
||||||
|
|
||||||
|
return typeof reviver === 'function' ? |
||||||
|
walk({'': j}, '') : j; |
||||||
|
} |
||||||
|
|
||||||
|
// If the text is not JSON parseable, then a SyntaxError is thrown.
|
||||||
|
|
||||||
|
throw new SyntaxError('JSON.parse'); |
||||||
|
}; |
||||||
|
} |
||||||
|
}()); |
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,40 @@ |
|||||||
|
describe('Date', function () { |
||||||
|
|
||||||
|
describe('now', function () { |
||||||
|
it('should be the current time', function () { |
||||||
|
expect(Date.now() === new Date().getTime()).toBe(true); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe("parse", function () { |
||||||
|
// TODO: Write the rest of the test.
|
||||||
|
|
||||||
|
it('should support extended years', function () { |
||||||
|
|
||||||
|
expect(Date.parse('0001-01-01T00:00:00Z')).toBe(-62135596800000); |
||||||
|
expect(Date.parse('+275760-09-13T00:00:00.000Z')).toBe(8.64e15); |
||||||
|
expect(Date.parse('+033658-09-27T01:46:40.000Z')).toBe(1e15); |
||||||
|
expect(Date.parse('-000001-01-01T00:00:00Z')).toBe(-62198755200000); |
||||||
|
expect(Date.parse('+002009-12-15T00:00:00Z')).toBe(1260835200000); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
describe("toISOString", function () { |
||||||
|
// TODO: write the rest of the test.
|
||||||
|
|
||||||
|
it('should support extended years', function () { |
||||||
|
expect(new Date(-62198755200000).toISOString().indexOf('-000001-01-01')).toBe(0); |
||||||
|
expect(new Date(8.64e15).toISOString().indexOf('+275760-09-13')).toBe(0); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe("toJSON", function () { |
||||||
|
it('should return the isoString when stringified', function () { |
||||||
|
var date = new Date(); |
||||||
|
expect(JSON.stringify(date.toISOString())).toBe(JSON.stringify(date)); |
||||||
|
})
|
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
@ -0,0 +1,137 @@ |
|||||||
|
|
||||||
|
describe('Function', function() { |
||||||
|
"use strict"; |
||||||
|
describe('bind', function() { |
||||||
|
var actual, expected, |
||||||
|
testSubject; |
||||||
|
|
||||||
|
testSubject = { |
||||||
|
push: function(o) { |
||||||
|
this.a.push(o); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
function func() { |
||||||
|
Array.prototype.forEach.call(arguments, function(a) { |
||||||
|
this.push(a); |
||||||
|
}, this); |
||||||
|
return this; |
||||||
|
}; |
||||||
|
|
||||||
|
beforeEach(function() { |
||||||
|
actual = []; |
||||||
|
testSubject.a = []; |
||||||
|
}); |
||||||
|
|
||||||
|
it('binds properly without a context', function() { |
||||||
|
var context; |
||||||
|
testSubject.func = function() { |
||||||
|
context = this; |
||||||
|
}.bind(); |
||||||
|
testSubject.func(); |
||||||
|
expect(context).toBe(function() {return this}.call()); |
||||||
|
}); |
||||||
|
it('binds properly without a context, and still supplies bound arguments', function() { |
||||||
|
var a, context; |
||||||
|
testSubject.func = function() { |
||||||
|
a = Array.prototype.slice.call(arguments); |
||||||
|
context = this; |
||||||
|
}.bind(undefined, 1,2,3); |
||||||
|
testSubject.func(1,2,3); |
||||||
|
expect(a).toEqual([1,2,3,1,2,3]); |
||||||
|
expect(context).toBe(function() {return this}.call()); |
||||||
|
}); |
||||||
|
it('binds a context properly', function() { |
||||||
|
testSubject.func = func.bind(actual); |
||||||
|
testSubject.func(1,2,3); |
||||||
|
expect(actual).toEqual([1,2,3]); |
||||||
|
expect(testSubject.a).toEqual([]); |
||||||
|
}); |
||||||
|
it('binds a context and supplies bound arguments', function() { |
||||||
|
testSubject.func = func.bind(actual, 1,2,3); |
||||||
|
testSubject.func(4,5,6); |
||||||
|
expect(actual).toEqual([1,2,3,4,5,6]); |
||||||
|
expect(testSubject.a).toEqual([]); |
||||||
|
}); |
||||||
|
|
||||||
|
it('returns properly without binding a context', function() { |
||||||
|
testSubject.func = function() { |
||||||
|
return this; |
||||||
|
}.bind(); |
||||||
|
var context = testSubject.func(); |
||||||
|
expect(context).toBe(function() {return this}.call()); |
||||||
|
}); |
||||||
|
it('returns properly without binding a context, and still supplies bound arguments', function() { |
||||||
|
var context; |
||||||
|
testSubject.func = function() { |
||||||
|
context = this; |
||||||
|
return Array.prototype.slice.call(arguments); |
||||||
|
}.bind(undefined, 1,2,3); |
||||||
|
actual = testSubject.func(1,2,3); |
||||||
|
expect(context).toBe(function() {return this}.call()); |
||||||
|
expect(actual).toEqual([1,2,3,1,2,3]); |
||||||
|
}); |
||||||
|
it('returns properly while binding a context properly', function() { |
||||||
|
var ret; |
||||||
|
testSubject.func = func.bind(actual); |
||||||
|
ret = testSubject.func(1,2,3); |
||||||
|
expect(ret).toBe(actual); |
||||||
|
expect(ret).not.toBe(testSubject); |
||||||
|
}); |
||||||
|
it('returns properly while binding a context and supplies bound arguments', function() { |
||||||
|
var ret; |
||||||
|
testSubject.func = func.bind(actual, 1,2,3); |
||||||
|
ret = testSubject.func(4,5,6); |
||||||
|
expect(ret).toBe(actual); |
||||||
|
expect(ret).not.toBe(testSubject); |
||||||
|
}); |
||||||
|
it('passes the correct arguments as a constructor', function() { |
||||||
|
var ret, expected = { name: "Correct" }; |
||||||
|
testSubject.func = function(arg) { |
||||||
|
return arg; |
||||||
|
}.bind({ name: "Incorrect" }); |
||||||
|
ret = new testSubject.func(expected); |
||||||
|
expect(ret).toBe(expected); |
||||||
|
}); |
||||||
|
it('returns the return value of the bound function when called as a constructor', function () { |
||||||
|
var oracle = [1, 2, 3]; |
||||||
|
var subject = function () { |
||||||
|
return oracle; |
||||||
|
}.bind(null); |
||||||
|
var result = new subject; |
||||||
|
expect(result).toBe(oracle); |
||||||
|
}); |
||||||
|
it('returns the correct value if constructor returns primitive', function() { |
||||||
|
var oracle = [1, 2, 3]; |
||||||
|
var subject = function () { |
||||||
|
return oracle; |
||||||
|
}.bind(null); |
||||||
|
var result = new subject; |
||||||
|
expect(result).toBe(oracle); |
||||||
|
|
||||||
|
oracle = {}; |
||||||
|
result = new subject; |
||||||
|
expect(result).toBe(oracle); |
||||||
|
|
||||||
|
oracle = function(){}; |
||||||
|
result = new subject; |
||||||
|
expect(result).toBe(oracle); |
||||||
|
|
||||||
|
oracle = "asdf"; |
||||||
|
result = new subject; |
||||||
|
expect(result).not.toBe(oracle); |
||||||
|
|
||||||
|
oracle = null; |
||||||
|
result = new subject; |
||||||
|
expect(result).not.toBe(oracle); |
||||||
|
|
||||||
|
oracle = true; |
||||||
|
result = new subject; |
||||||
|
expect(result).not.toBe(oracle); |
||||||
|
|
||||||
|
oracle = 1; |
||||||
|
result = new subject; |
||||||
|
expect(result).not.toBe(oracle); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
@ -0,0 +1,84 @@ |
|||||||
|
describe('Object', function () { |
||||||
|
"use strict"; |
||||||
|
|
||||||
|
describe("Object.keys", function () { |
||||||
|
var obj = { |
||||||
|
"str": "boz", |
||||||
|
"obj": { }, |
||||||
|
"arr": [], |
||||||
|
"bool": true, |
||||||
|
"num": 42, |
||||||
|
"null": null, |
||||||
|
"undefined": undefined |
||||||
|
}; |
||||||
|
|
||||||
|
var loopedValues = []; |
||||||
|
for (var k in obj) { |
||||||
|
loopedValues.push(k); |
||||||
|
} |
||||||
|
|
||||||
|
var keys = Object.keys(obj); |
||||||
|
it('should have correct length', function () { |
||||||
|
expect(keys.length).toBe(7);
|
||||||
|
}); |
||||||
|
|
||||||
|
it('should return an Array', function () { |
||||||
|
expect(Array.isArray(keys)).toBe(true);
|
||||||
|
}); |
||||||
|
|
||||||
|
it('should return names which are own properties', function () { |
||||||
|
keys.forEach(function (name) { |
||||||
|
expect(obj.hasOwnProperty(name)).toBe(true); |
||||||
|
});
|
||||||
|
}); |
||||||
|
|
||||||
|
it('should return names which are enumerable', function () { |
||||||
|
keys.forEach(function (name) { |
||||||
|
expect(loopedValues.indexOf(name)).toNotBe(-1); |
||||||
|
})
|
||||||
|
}); |
||||||
|
|
||||||
|
it('should throw error for non object', function () { |
||||||
|
var e = {}; |
||||||
|
expect(function () { |
||||||
|
try { |
||||||
|
Object.keys(42) |
||||||
|
} catch (err) { |
||||||
|
throw e; |
||||||
|
} |
||||||
|
}).toThrow(e); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe("Object.isExtensible", function () { |
||||||
|
var obj = { }; |
||||||
|
|
||||||
|
it('should return true if object is extensible', function () { |
||||||
|
expect(Object.isExtensible(obj)).toBe(true);
|
||||||
|
}); |
||||||
|
|
||||||
|
it('should return false if object is not extensible', function () { |
||||||
|
expect(Object.isExtensible(Object.preventExtensions(obj))).toBe(false);
|
||||||
|
}); |
||||||
|
|
||||||
|
it('should return false if object is seal', function () { |
||||||
|
expect(Object.isExtensible(Object.seal(obj))).toBe(false);
|
||||||
|
}); |
||||||
|
|
||||||
|
it('should return false if object is freeze', function () { |
||||||
|
expect(Object.isExtensible(Object.freeze(obj))).toBe(false);
|
||||||
|
}); |
||||||
|
|
||||||
|
it('should throw error for non object', function () { |
||||||
|
var e1 = {}; |
||||||
|
expect(function () { |
||||||
|
try { |
||||||
|
Object.isExtensible(42) |
||||||
|
} catch (err) { |
||||||
|
throw e1; |
||||||
|
} |
||||||
|
}).toThrow(e1); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
describe('String', function() { |
||||||
|
"use strict"; |
||||||
|
describe("trim", function() { |
||||||
|
var test = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFFHello, World!\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF"; |
||||||
|
|
||||||
|
it('trims all ES5 whitespace', function() { |
||||||
|
expect(test.trim()).toEqual("Hello, World!"); |
||||||
|
expect(test.trim().length).toEqual(13); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
@ -0,0 +1,33 @@ |
|||||||
|
{ |
||||||
|
"name": "flight", |
||||||
|
"description": "Clientside component infrastructure", |
||||||
|
"main": "lib/index.js", |
||||||
|
"version": "1.1.4", |
||||||
|
"ignore": [ |
||||||
|
"doc", |
||||||
|
"tools", |
||||||
|
"test", |
||||||
|
".gitignore", |
||||||
|
".travis.yml", |
||||||
|
"CHANGELOG.md", |
||||||
|
"CONTRIBUTING.md", |
||||||
|
"Makefile", |
||||||
|
"karma.conf.js", |
||||||
|
"package.json" |
||||||
|
], |
||||||
|
"dependencies": { |
||||||
|
"es5-shim": "2.0.0", |
||||||
|
"jquery": ">=1.8.0" |
||||||
|
}, |
||||||
|
"homepage": "https://github.com/flightjs/flight", |
||||||
|
"_release": "1.1.4", |
||||||
|
"_resolution": { |
||||||
|
"type": "version", |
||||||
|
"tag": "v1.1.4", |
||||||
|
"commit": "5da831ba9026330b692ecf5668cad8832f1f19cd" |
||||||
|
}, |
||||||
|
"_source": "git://github.com/flightjs/flight.git", |
||||||
|
"_target": "~1.1.4", |
||||||
|
"_originalSource": "flight", |
||||||
|
"_direct": true |
||||||
|
} |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
Copyright (c) 2013-2014 Twitter, Inc and others |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
of this software and associated documentation files (the "Software"), to deal |
||||||
|
in the Software without restriction, including without limitation the rights |
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
copies of the Software, and to permit persons to whom the Software is |
||||||
|
furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in |
||||||
|
all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
THE SOFTWARE. |
||||||
@ -0,0 +1,242 @@ |
|||||||
|
# Flight |
||||||
|
|
||||||
|
[](http://travis-ci.org/flightjs/flight) |
||||||
|
|
||||||
|
[Flight](http://flightjs.github.io/) is a lightweight, component-based, |
||||||
|
event-driven JavaScript framework that maps behavior to DOM nodes. It was |
||||||
|
created at Twitter, and is used by the [twitter.com](https://twitter.com/) and |
||||||
|
[TweetDeck](https://web.tweetdeck.com/) web applications. |
||||||
|
|
||||||
|
* [Website](http://flightjs.github.io/) |
||||||
|
* [API documentation](doc/README.md) |
||||||
|
* [Flight example app](http://flightjs.github.io/example-app/) ([Source](https://github.com/flightjs/example-app)) |
||||||
|
* [Flight's Google Group](https://groups.google.com/forum/?fromgroups#!forum/twitter-flight) |
||||||
|
* [Flight on Twitter](https://twitter.com/flight) |
||||||
|
|
||||||
|
|
||||||
|
## Why Flight? |
||||||
|
|
||||||
|
Flight is only ~5K minified and gzipped. It's built upon jQuery, and has |
||||||
|
first-class support for Asynchronous Module Definition (AMD) and [Bower](http://bower.io/). |
||||||
|
|
||||||
|
Flight components are highly portable and easily testable. This is because a |
||||||
|
Flight component (and its API) is entirely decoupled from other components. |
||||||
|
Flight components communicate only by triggering and subscribing to events. |
||||||
|
|
||||||
|
Flight also includes a simple and safe |
||||||
|
[mixin](https://javascriptweblog.wordpress.com/2011/05/31/a-fresh-look-at-javascript-mixins/) |
||||||
|
infrastructure, allowing components to be easily extended with minimal |
||||||
|
boilerplate. |
||||||
|
|
||||||
|
|
||||||
|
## Development tools |
||||||
|
|
||||||
|
Flight has supporting projects that provide everything you need to setup, |
||||||
|
write, and test your application. |
||||||
|
|
||||||
|
* [Flight generator](https://github.com/flightjs/generator-flight/) |
||||||
|
Recommended. One-step to setup everything you need to work with Flight. |
||||||
|
|
||||||
|
* [Flight package generator](https://github.com/flightjs/generator-flight-package/) |
||||||
|
Recommended. One-step to setup everything you need to write and test a |
||||||
|
standalone Flight component. |
||||||
|
|
||||||
|
* [Jasmine Flight](https://github.com/flightjs/jasmine-flight/) |
||||||
|
Extensions for the Jasmine test framework. |
||||||
|
|
||||||
|
* [Mocha Flight](https://github.com/flightjs/mocha-flight/) |
||||||
|
Extensions for the Mocha test framework. |
||||||
|
|
||||||
|
|
||||||
|
## Finding and writing standalone components |
||||||
|
|
||||||
|
You can browse all the [Flight components](http://flight-components.jit.su) |
||||||
|
available at this time. They can also be found by searching the Bower registry: |
||||||
|
|
||||||
|
``` |
||||||
|
bower search flight |
||||||
|
``` |
||||||
|
|
||||||
|
The easiest way to write a standalone Flight component is to use the [Flight |
||||||
|
package generator](https://github.com/flightjs/generator-flight-package/): |
||||||
|
|
||||||
|
``` |
||||||
|
yo flight-package foo |
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
## Installation |
||||||
|
|
||||||
|
If you prefer not to use the Flight generators, it's highly recommended that |
||||||
|
you install Flight as an AMD package (including all the correct dependencies). |
||||||
|
This is best done with [Bower](http://bower.io/), a package manager for the web. |
||||||
|
|
||||||
|
``` |
||||||
|
npm install -g bower |
||||||
|
bower install --save flight |
||||||
|
``` |
||||||
|
|
||||||
|
You will have to reference Flight's installed dependencies – |
||||||
|
[ES5-shim](https://github.com/kriskowal/es5-shim) and |
||||||
|
[jQuery](http://jquery.com) – and use an AMD module loader like |
||||||
|
[Require.js](http://requirejs.org/) or |
||||||
|
[Loadrunner](https://github.com/danwrong/loadrunner). |
||||||
|
|
||||||
|
```html |
||||||
|
<script src="bower_components/es5-shim/es5-shim.js"></script> |
||||||
|
<script src="bower_components/es5-shim/es5-sham.js"></script> |
||||||
|
<script src="bower_components/jquery/dist/jquery.js"></script> |
||||||
|
<script data-main="main.js" src="bower_components/requirejs/require.js"></script> |
||||||
|
... |
||||||
|
``` |
||||||
|
|
||||||
|
## Standalone version |
||||||
|
|
||||||
|
Alternatively, you can manually install the [standalone |
||||||
|
version](http://flightjs.github.io/release/latest/flight.js) of Flight, also |
||||||
|
available on [cdnjs](http://cdnjs.com/). It exposes all of its modules as |
||||||
|
properties of a global variable, `flight`: |
||||||
|
|
||||||
|
```html |
||||||
|
... |
||||||
|
<script src="flight.js"></script> |
||||||
|
<script> |
||||||
|
var MyComponent = flight.component(function() { |
||||||
|
//... |
||||||
|
}); |
||||||
|
</script> |
||||||
|
``` |
||||||
|
|
||||||
|
N.B. You will also need to manually install the correct versions of Flight's |
||||||
|
dependencies: ES5 Shim and jQuery. |
||||||
|
|
||||||
|
## Browser Support |
||||||
|
|
||||||
|
Chrome, Firefox, Safari, Opera, IE 7+. |
||||||
|
|
||||||
|
## Quick Overview |
||||||
|
|
||||||
|
Here's a brief introduction to Flight's key concepts and syntax. Read the [API |
||||||
|
documentation](doc) for a comprehensive overview. |
||||||
|
|
||||||
|
### Example |
||||||
|
|
||||||
|
A simple example of how to write and use a Flight component. |
||||||
|
|
||||||
|
```js |
||||||
|
define(function (require) { |
||||||
|
var defineComponent = require('flight/lib/component'); |
||||||
|
|
||||||
|
// define the component |
||||||
|
return defineComponent(inbox); |
||||||
|
|
||||||
|
function inbox() { |
||||||
|
// define custom functions here |
||||||
|
this.doSomething = function() { |
||||||
|
//... |
||||||
|
} |
||||||
|
|
||||||
|
this.doSomethingElse = function() { |
||||||
|
//... |
||||||
|
} |
||||||
|
|
||||||
|
// now initialize the component |
||||||
|
this.after('initialize', function() { |
||||||
|
this.on('click', this.doSomething); |
||||||
|
this.on('mouseover', this.doSomethingElse); |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
``` |
||||||
|
|
||||||
|
```js |
||||||
|
/* attach an inbox component to a node with id 'inbox' */ |
||||||
|
|
||||||
|
define(function (require) { |
||||||
|
var Inbox = require('inbox'); |
||||||
|
|
||||||
|
Inbox.attachTo('#inbox', { |
||||||
|
'nextPageSelector': '#nextPage', |
||||||
|
'previousPageSelector': '#previousPage', |
||||||
|
}); |
||||||
|
}); |
||||||
|
``` |
||||||
|
|
||||||
|
### Components ([API](doc/component_api.md)) |
||||||
|
|
||||||
|
- A Component is nothing more than a constructor with properties mixed into its prototype. |
||||||
|
- Every Component comes with a set of basic functionality such as event handling and component registration. |
||||||
|
(see [Base API](doc/base_api.md)) |
||||||
|
- Additionally, each Component definition mixes in a set of custom properties which describe its behavior. |
||||||
|
- When a component is attached to a DOM node, a new instance of that component is created. Each component |
||||||
|
instance references the DOM node via its `node` property. |
||||||
|
- Component instances cannot be referenced directly; they communicate with other components via events. |
||||||
|
|
||||||
|
### Interacting with the DOM |
||||||
|
|
||||||
|
Once attached, component instances have direct access to their node object via the `node` property. (There's |
||||||
|
also a jQuery version of the node available via the `$node` property.) |
||||||
|
|
||||||
|
### Events in Flight |
||||||
|
|
||||||
|
Events are how Flight components interact. The Component prototype supplies methods for triggering events as |
||||||
|
well as for subscribing to and unsubscribing from events. These Component event methods are actually just convenient |
||||||
|
wrappers around regular event methods on DOM nodes. |
||||||
|
|
||||||
|
### Mixins ([API](doc/mixin_api.md)) |
||||||
|
|
||||||
|
- In Flight, a mixin is a function which assigns properties to a target object (represented by the `this` |
||||||
|
keyword). |
||||||
|
- A typical mixin defines a set of functionality that will be useful to more than one component. |
||||||
|
- One mixin can be applied to any number of [Component](#components) definitions. |
||||||
|
- One Component definition can have any number of mixins applied to it. |
||||||
|
- Each Component defines a [*core*](#core_mixin) mixin within its own module. |
||||||
|
- A mixin can itself have mixins applied to it. |
||||||
|
|
||||||
|
### Advice ([API](doc/advice_api.md)) |
||||||
|
|
||||||
|
In Flight, advice is a mixin (`'lib/advice.js'`) that defines `before`, `after` and `around` methods. |
||||||
|
|
||||||
|
These can be used to modify existing functions by adding custom code. All Components have advice mixed in to |
||||||
|
their prototype so that mixins can augment existing functions without requiring knowledge |
||||||
|
of the original implementation. Moreover, since Component's are seeded with an empty `initialize` method, |
||||||
|
Component definitions will typically use `after` to define custom `initialize` behavior. |
||||||
|
|
||||||
|
### Debugging ([API](doc/debug_api.md)) |
||||||
|
|
||||||
|
Flight ships with a debug module which can help you trace the sequence of event triggering and binding. By default |
||||||
|
console logging is turned off, but you can you can log `trigger`, `on` and `off` events by means of the following console |
||||||
|
commands. |
||||||
|
|
||||||
|
## Authors |
||||||
|
|
||||||
|
+ [@angus-c](http://github.com/angus-c) |
||||||
|
+ [@danwrong](http://github.com/danwrong) |
||||||
|
+ [@kpk](http://github.com/kennethkufluk) |
||||||
|
|
||||||
|
Thanks for assistance and contributions: |
||||||
|
[@sayrer](https://github.com/sayrer), |
||||||
|
[@shinypb](https://github.com/shinypb), |
||||||
|
[@kloots](https://github.com/kloots), |
||||||
|
[@marcelduran](https://github.com/marcelduran), |
||||||
|
[@tbrd](https://github.com/tbrd), |
||||||
|
[@necolas](https://github.com/necolas), |
||||||
|
[@fat](https://github.com/fat), |
||||||
|
[@mkuklis](https://github.com/mkuklis), |
||||||
|
[@jrburke](https://github.com/jrburke), |
||||||
|
[@garann](https://github.com/garann), |
||||||
|
[@WebReflection](https://github.com/WebReflection), |
||||||
|
[@coldhead](https://github.com/coldhead), |
||||||
|
[@paulirish](https://github.com/paulirish), |
||||||
|
[@nimbupani](https://github.com/nimbupani), |
||||||
|
[@mootcycle](https://github.com/mootcycle). |
||||||
|
|
||||||
|
Special thanks to the rest of the Twitter web team for their abundant |
||||||
|
contributions and feedback. |
||||||
|
|
||||||
|
|
||||||
|
## License |
||||||
|
|
||||||
|
Copyright 2013 Twitter, Inc and other contributors. |
||||||
|
|
||||||
|
Licensed under the MIT License |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
{ |
||||||
|
"name": "flight", |
||||||
|
"description": "Clientside component infrastructure", |
||||||
|
"main": "lib/index.js", |
||||||
|
"version": "1.1.4", |
||||||
|
"ignore": [ |
||||||
|
"doc", |
||||||
|
"tools", |
||||||
|
"test", |
||||||
|
".gitignore", |
||||||
|
".travis.yml", |
||||||
|
"CHANGELOG.md", |
||||||
|
"CONTRIBUTING.md", |
||||||
|
"Makefile", |
||||||
|
"karma.conf.js", |
||||||
|
"package.json" |
||||||
|
], |
||||||
|
"dependencies": { |
||||||
|
"es5-shim": "2.0.0", |
||||||
|
"jquery": ">=1.8.0" |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,69 @@ |
|||||||
|
// ==========================================
|
||||||
|
// Copyright 2013 Twitter, Inc
|
||||||
|
// Licensed under The MIT License
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
define( |
||||||
|
|
||||||
|
[ |
||||||
|
'./compose' |
||||||
|
], |
||||||
|
|
||||||
|
function(compose) { |
||||||
|
'use strict'; |
||||||
|
|
||||||
|
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') { |
||||||
|
this[method] = advice[m](this[method], fn); |
||||||
|
} else { |
||||||
|
this[method] = fn; |
||||||
|
} |
||||||
|
|
||||||
|
return this[method]; |
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
}, this); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
return advice; |
||||||
|
} |
||||||
|
); |
||||||
@ -0,0 +1,237 @@ |
|||||||
|
// ==========================================
|
||||||
|
// Copyright 2013 Twitter, Inc
|
||||||
|
// Licensed under The MIT License
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
define( |
||||||
|
|
||||||
|
[ |
||||||
|
'./utils', |
||||||
|
'./registry', |
||||||
|
'./debug' |
||||||
|
], |
||||||
|
|
||||||
|
function(utils, registry, debug) { |
||||||
|
'use strict'; |
||||||
|
|
||||||
|
// common mixin allocates basic functionality - used by all component prototypes
|
||||||
|
// callback context is bound to component
|
||||||
|
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 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(' ') |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function proxyEventTo(targetEvent) { |
||||||
|
return function(e, data) { |
||||||
|
$(e.target).trigger(targetEvent, data); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
function withBase() { |
||||||
|
|
||||||
|
// 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 (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 if (typeof origin == 'string') { |
||||||
|
originalCb = proxyEventTo(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; |
||||||
|
callback.context = this; |
||||||
|
|
||||||
|
$element.on(type, callback); |
||||||
|
|
||||||
|
// store every bound version of the callback
|
||||||
|
originalCb.bound || (originalCb.bound = []); |
||||||
|
originalCb.bound.push(callback); |
||||||
|
|
||||||
|
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]; |
||||||
|
} |
||||||
|
|
||||||
|
if (callback) { |
||||||
|
//this callback may be the original function or a bound version
|
||||||
|
var boundFunctions = callback.target ? callback.target.bound : callback.bound || []; |
||||||
|
//set callback to version bound against this instance
|
||||||
|
boundFunctions && boundFunctions.some(function(fn, i, arr) { |
||||||
|
if (fn.context && (this.identity == fn.context.identity)) { |
||||||
|
arr.splice(i, 1); |
||||||
|
callback = fn; |
||||||
|
return true; |
||||||
|
} |
||||||
|
}, this); |
||||||
|
} |
||||||
|
|
||||||
|
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]] = (typeof ruleInfo[r] == 'string') ? proxyEventTo(ruleInfo[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 = function(node, attrs) { |
||||||
|
attrs || (attrs = {}); |
||||||
|
//only assign identity if there isn't one (initialize can be called multiple times)
|
||||||
|
this.identity || (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); |
||||||
|
} |
||||||
|
|
||||||
|
// merge defaults with supplied options
|
||||||
|
// put options in attr.__proto__ to avoid merge overhead
|
||||||
|
var attr = Object.create(attrs); |
||||||
|
for (var key in this.defaults) { |
||||||
|
if (!attrs.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); |
||||||
|
|
||||||
|
return this; |
||||||
|
}; |
||||||
|
|
||||||
|
this.teardown = function() { |
||||||
|
teardownInstance(registry.findInstanceInfo(this)); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
return withBase; |
||||||
|
} |
||||||
|
); |
||||||
@ -0,0 +1,136 @@ |
|||||||
|
// ==========================================
|
||||||
|
// 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 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(' ') |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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); |
||||||
|
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; |
||||||
|
} |
||||||
|
); |
||||||
@ -0,0 +1,85 @@ |
|||||||
|
// ==========================================
|
||||||
|
// Copyright 2013 Twitter, Inc
|
||||||
|
// Licensed under The MIT License
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
define( |
||||||
|
|
||||||
|
[ |
||||||
|
'./utils', |
||||||
|
'./debug' |
||||||
|
], |
||||||
|
|
||||||
|
function(utils, debug) { |
||||||
|
'use strict'; |
||||||
|
|
||||||
|
//enumerables are shims - getOwnPropertyDescriptor shim doesn't work
|
||||||
|
var canWriteProtect = debug.enabled && !utils.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 : []; |
||||||
|
|
||||||
|
for (var i=0; i<mixins.length; i++) { |
||||||
|
if (base.mixedIn.indexOf(mixins[i]) == -1) { |
||||||
|
setPropertyWritability(base, false); |
||||||
|
mixins[i].call(base); |
||||||
|
base.mixedIn.push(mixins[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
setPropertyWritability(base, true); |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
mixin: mixin, |
||||||
|
unlockProperty: unlockProperty |
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
); |
||||||
@ -0,0 +1,165 @@ |
|||||||
|
// ==========================================
|
||||||
|
// Copyright 2013 Twitter, Inc
|
||||||
|
// Licensed under The MIT License
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
define( |
||||||
|
|
||||||
|
[], |
||||||
|
|
||||||
|
function() { |
||||||
|
'use strict'; |
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Search object model
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
function traverse(util, searchTerm, options) { |
||||||
|
options = options || {}; |
||||||
|
var obj = options.obj || window; |
||||||
|
var path = options.path || ((obj==window) ? 'window' : ''); |
||||||
|
var props = Object.keys(obj); |
||||||
|
props.forEach(function(prop) { |
||||||
|
if ((tests[util] || util)(searchTerm, obj, prop)){ |
||||||
|
console.log([path, '.', prop].join(''), '->', ['(', typeof obj[prop], ')'].join(''), obj[prop]); |
||||||
|
} |
||||||
|
if (Object.prototype.toString.call(obj[prop]) == '[object Object]' && (obj[prop] != obj) && path.split('.').indexOf(prop) == -1) { |
||||||
|
traverse(util, searchTerm, {obj: obj[prop], path: [path,prop].join('.')}); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function search(util, expected, searchTerm, options) { |
||||||
|
if (!expected || typeof searchTerm == expected) { |
||||||
|
traverse(util, searchTerm, options); |
||||||
|
} else { |
||||||
|
console.error([searchTerm, 'must be', expected].join(' ')); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var tests = { |
||||||
|
'name': function(searchTerm, obj, prop) {return searchTerm == prop;}, |
||||||
|
'nameContains': function(searchTerm, obj, prop) {return prop.indexOf(searchTerm) > -1;}, |
||||||
|
'type': function(searchTerm, obj, prop) {return obj[prop] instanceof searchTerm;}, |
||||||
|
'value': function(searchTerm, obj, prop) {return obj[prop] === searchTerm;}, |
||||||
|
'valueCoerced': function(searchTerm, obj, prop) {return obj[prop] == searchTerm;} |
||||||
|
}; |
||||||
|
|
||||||
|
function byName(searchTerm, options) {search('name', 'string', searchTerm, options);} |
||||||
|
function byNameContains(searchTerm, options) {search('nameContains', 'string', searchTerm, options);} |
||||||
|
function byType(searchTerm, options) {search('type', 'function', searchTerm, options);} |
||||||
|
function byValue(searchTerm, options) {search('value', null, searchTerm, options);} |
||||||
|
function byValueCoerced(searchTerm, options) {search('valueCoerced', null, searchTerm, options);} |
||||||
|
function custom(fn, options) {traverse(fn, null, options);} |
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Event logging
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
var ALL = 'all'; //no filter
|
||||||
|
|
||||||
|
//log nothing by default
|
||||||
|
var logFilter = { |
||||||
|
eventNames: [], |
||||||
|
actions: [] |
||||||
|
} |
||||||
|
|
||||||
|
function filterEventLogsByAction(/*actions*/) { |
||||||
|
var actions = [].slice.call(arguments); |
||||||
|
|
||||||
|
logFilter.eventNames.length || (logFilter.eventNames = ALL); |
||||||
|
logFilter.actions = actions.length ? actions : ALL; |
||||||
|
saveLogFilter(); |
||||||
|
} |
||||||
|
|
||||||
|
function filterEventLogsByName(/*eventNames*/) { |
||||||
|
var eventNames = [].slice.call(arguments); |
||||||
|
|
||||||
|
logFilter.actions.length || (logFilter.actions = ALL); |
||||||
|
logFilter.eventNames = eventNames.length ? eventNames : ALL; |
||||||
|
saveLogFilter(); |
||||||
|
} |
||||||
|
|
||||||
|
function hideAllEventLogs() { |
||||||
|
logFilter.actions = []; |
||||||
|
logFilter.eventNames = []; |
||||||
|
saveLogFilter(); |
||||||
|
} |
||||||
|
|
||||||
|
function showAllEventLogs() { |
||||||
|
logFilter.actions = ALL; |
||||||
|
logFilter.eventNames = ALL; |
||||||
|
saveLogFilter(); |
||||||
|
} |
||||||
|
|
||||||
|
function saveLogFilter() { |
||||||
|
try { |
||||||
|
if (window.localStorage) { |
||||||
|
localStorage.setItem('logFilter_eventNames', logFilter.eventNames); |
||||||
|
localStorage.setItem('logFilter_actions', logFilter.actions); |
||||||
|
} |
||||||
|
} catch (ignored) {}; |
||||||
|
} |
||||||
|
|
||||||
|
function retrieveLogFilter() { |
||||||
|
var eventNames, actions; |
||||||
|
try { |
||||||
|
eventNames = (window.localStorage && localStorage.getItem('logFilter_eventNames')); |
||||||
|
actions = (window.localStorage && localStorage.getItem('logFilter_actions')); |
||||||
|
} catch(ignored) { |
||||||
|
return; |
||||||
|
} |
||||||
|
eventNames && (logFilter.eventNames = eventNames); |
||||||
|
actions && (logFilter.actions = actions); |
||||||
|
|
||||||
|
// reconstitute arrays in place
|
||||||
|
Object.keys(logFilter).forEach(function(k) { |
||||||
|
var thisProp = logFilter[k]; |
||||||
|
if (typeof thisProp == 'string' && thisProp !== ALL) { |
||||||
|
logFilter[k] = thisProp ? thisProp.split(',') : []; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
|
||||||
|
enable: function(enable) { |
||||||
|
this.enabled = !!enable; |
||||||
|
|
||||||
|
if (enable && window.console) { |
||||||
|
console.info('Booting in DEBUG mode'); |
||||||
|
console.info('You can configure event logging with DEBUG.events.logAll()/logNone()/logByName()/logByAction()'); |
||||||
|
} |
||||||
|
|
||||||
|
retrieveLogFilter(); |
||||||
|
|
||||||
|
window.DEBUG = this; |
||||||
|
}, |
||||||
|
|
||||||
|
find: { |
||||||
|
byName: byName, |
||||||
|
byNameContains: byNameContains, |
||||||
|
byType: byType, |
||||||
|
byValue: byValue, |
||||||
|
byValueCoerced: byValueCoerced, |
||||||
|
custom: custom |
||||||
|
}, |
||||||
|
|
||||||
|
events: { |
||||||
|
logFilter: logFilter, |
||||||
|
|
||||||
|
// Accepts any number of action args
|
||||||
|
// e.g. DEBUG.events.logByAction("on", "off")
|
||||||
|
logByAction: filterEventLogsByAction, |
||||||
|
|
||||||
|
// Accepts any number of event name args (inc. regex or wildcards)
|
||||||
|
// e.g. DEBUG.events.logByName(/ui.*/, "*Thread*");
|
||||||
|
logByName: filterEventLogsByName, |
||||||
|
|
||||||
|
logAll: showAllEventLogs, |
||||||
|
logNone: hideAllEventLogs |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
); |
||||||
@ -0,0 +1,31 @@ |
|||||||
|
// ==========================================
|
||||||
|
// 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) { |
||||||
|
'use strict'; |
||||||
|
|
||||||
|
return { |
||||||
|
advice: advice, |
||||||
|
component: component, |
||||||
|
compose: compose, |
||||||
|
logger: logger, |
||||||
|
registry: registry, |
||||||
|
utils: utils |
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
); |
||||||
@ -0,0 +1,100 @@ |
|||||||
|
// ==========================================
|
||||||
|
// Copyright 2013 Twitter, Inc
|
||||||
|
// Licensed under The MIT License
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
define( |
||||||
|
|
||||||
|
[ |
||||||
|
'./utils' |
||||||
|
], |
||||||
|
|
||||||
|
function(utils) { |
||||||
|
'use strict'; |
||||||
|
|
||||||
|
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) { |
||||||
|
if (!window.DEBUG || !window.DEBUG.enabled) return; |
||||||
|
var name, eventType, elem, fn, payload, logFilter, toRegExp, actionLoggable, nameLoggable, info; |
||||||
|
|
||||||
|
if (typeof eventArgs[eventArgs.length-1] == 'function') { |
||||||
|
fn = eventArgs.pop(); |
||||||
|
fn = fn.unbound || fn; // use unbound version if any (better info)
|
||||||
|
} |
||||||
|
|
||||||
|
if (eventArgs.length == 1) { |
||||||
|
elem = component.$node[0]; |
||||||
|
eventType = eventArgs[0]; |
||||||
|
} else if ((eventArgs.length == 2) && typeof eventArgs[1] == 'object' && !eventArgs[1].type) { |
||||||
|
//2 args, first arg is not elem
|
||||||
|
elem = component.$node[0]; |
||||||
|
eventType = eventArgs[0]; |
||||||
|
if (action == "trigger") { |
||||||
|
payload = eventArgs[1]; |
||||||
|
} |
||||||
|
} else { |
||||||
|
//2+ args, first arg is elem
|
||||||
|
elem = eventArgs[0]; |
||||||
|
eventType = eventArgs[1]; |
||||||
|
if (action == "trigger") { |
||||||
|
payload = eventArgs[2]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
name = typeof eventType == 'object' ? eventType.type : eventType; |
||||||
|
|
||||||
|
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) { |
||||||
|
info = [actionSymbols[action], action, '[' + name + ']']; |
||||||
|
payload && info.push(payload); |
||||||
|
info.push(elemToString(elem)); |
||||||
|
info.push(component.constructor.describe.split(' ').slice(0,3).join(' ')); |
||||||
|
console.groupCollapsed && action == 'trigger' && console.groupCollapsed(action, name); |
||||||
|
console.info.apply(console, info); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function withLogging() { |
||||||
|
this.before('trigger', function() { |
||||||
|
log('trigger', this, utils.toArray(arguments)); |
||||||
|
}); |
||||||
|
if (console.groupCollapsed) { |
||||||
|
this.after('trigger', function() { |
||||||
|
console.groupEnd(); |
||||||
|
}); |
||||||
|
} |
||||||
|
this.before('on', function() { |
||||||
|
log('on', this, utils.toArray(arguments)); |
||||||
|
}); |
||||||
|
this.before('off', function() { |
||||||
|
log('off', this, utils.toArray(arguments)); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
return withLogging; |
||||||
|
} |
||||||
|
); |
||||||
@ -0,0 +1,228 @@ |
|||||||
|
// ==========================================
|
||||||
|
// Copyright 2013 Twitter, Inc
|
||||||
|
// Licensed under The MIT License
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
define( |
||||||
|
|
||||||
|
[], |
||||||
|
|
||||||
|
function() { |
||||||
|
'use strict'; |
||||||
|
|
||||||
|
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 (!Object.keys(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.getBoundEventNames = function(instance) { |
||||||
|
return this.findInstanceInfo(instance).events.map(function(ev) { |
||||||
|
return ev.type; |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
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); |
||||||
|
} |
||||||
|
|
||||||
|
//remove from global event registry
|
||||||
|
for (var i = 0, e; e = registry.events[i]; i++) { |
||||||
|
if (matchEvent(e, event)) { |
||||||
|
registry.events.splice(i, 1); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// debug tools may want to add advice to trigger
|
||||||
|
registry.trigger = function() {}; |
||||||
|
|
||||||
|
this.teardown = function() { |
||||||
|
registry.removeInstance(this); |
||||||
|
}; |
||||||
|
|
||||||
|
this.withRegistration = function() { |
||||||
|
this.after('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; |
||||||
|
} |
||||||
|
); |
||||||
@ -0,0 +1,263 @@ |
|||||||
|
// ==========================================
|
||||||
|
// Copyright 2013 Twitter, Inc
|
||||||
|
// Licensed under The MIT License
|
||||||
|
// http://opensource.org/licenses/MIT
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
define( |
||||||
|
|
||||||
|
[], |
||||||
|
|
||||||
|
function() { |
||||||
|
'use strict'; |
||||||
|
|
||||||
|
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 new 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)
|
||||||
|
// utils.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 (!e.isPropagationStopped() && (parent = target.closest(selector)).length) { |
||||||
|
data = data || {}; |
||||||
|
data.el = parent[0]; |
||||||
|
return rules[selector].apply(this, [e, data]); |
||||||
|
} |
||||||
|
}, this); |
||||||
|
}; |
||||||
|
}, |
||||||
|
|
||||||
|
// ensures that a function will only be called once.
|
||||||
|
// usage:
|
||||||
|
// will only create the application once
|
||||||
|
// var initialize = utils.once(createApplication)
|
||||||
|
// initialize();
|
||||||
|
// initialize();
|
||||||
|
//
|
||||||
|
// will only delete a record once
|
||||||
|
// var myHanlder = function () {
|
||||||
|
// $.ajax({type: 'DELETE', url: 'someurl.com', data: {id: 1}});
|
||||||
|
// };
|
||||||
|
// this.on('click', utils.once(myHandler));
|
||||||
|
//
|
||||||
|
once: function(func) { |
||||||
|
var ran, result; |
||||||
|
|
||||||
|
return function() { |
||||||
|
if (ran) { |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
ran = true; |
||||||
|
result = func.apply(this, arguments); |
||||||
|
|
||||||
|
return result; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
return utils; |
||||||
|
} |
||||||
|
); |
||||||
Loading…
Reference in new issue