32 changed files with 7595 additions and 0 deletions
@ -0,0 +1,326 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1,3 @@
|
||||
function implement() { |
||||
throw 'Not implemented'; |
||||
} |
||||
@ -0,0 +1,62 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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