it
Version:
A testing framework for node
1,566 lines (1,436 loc) • 294 kB
JavaScript
(function(){
var require = function (file, cwd) {
var resolved = require.resolve(file, cwd || '/');
var mod = require.modules[resolved];
if (!mod) throw new Error(
'Failed to resolve module ' + file + ', tried ' + resolved
);
var cached = require.cache[resolved];
var res = cached? cached.exports : mod();
return res;
};
require.paths = [];
require.modules = {};
require.cache = {};
require.extensions = [".js",".coffee",".json"];
require._core = {
'assert': true,
'events': true,
'fs': true,
'path': true,
'vm': true
};
require.resolve = (function () {
return function (x, cwd) {
if (!cwd) cwd = '/';
if (require._core[x]) return x;
var path = require.modules.path();
cwd = path.resolve('/', cwd);
var y = cwd || '/';
if (x.match(/^(?:\.\.?\/|\/)/)) {
var m = loadAsFileSync(path.resolve(y, x))
|| loadAsDirectorySync(path.resolve(y, x));
if (m) return m;
}
var n = loadNodeModulesSync(x, y);
if (n) return n;
throw new Error("Cannot find module '" + x + "'");
function loadAsFileSync (x) {
x = path.normalize(x);
if (require.modules[x]) {
return x;
}
for (var i = 0; i < require.extensions.length; i++) {
var ext = require.extensions[i];
if (require.modules[x + ext]) return x + ext;
}
}
function loadAsDirectorySync (x) {
x = x.replace(/\/+$/, '');
var pkgfile = path.normalize(x + '/package.json');
if (require.modules[pkgfile]) {
var pkg = require.modules[pkgfile]();
var b = pkg.browserify;
if (typeof b === 'object' && b.main) {
var m = loadAsFileSync(path.resolve(x, b.main));
if (m) return m;
}
else if (typeof b === 'string') {
var m = loadAsFileSync(path.resolve(x, b));
if (m) return m;
}
else if (pkg.main) {
var m = loadAsFileSync(path.resolve(x, pkg.main));
if (m) return m;
}
}
return loadAsFileSync(x + '/index');
}
function loadNodeModulesSync (x, start) {
var dirs = nodeModulesPathsSync(start);
for (var i = 0; i < dirs.length; i++) {
var dir = dirs[i];
var m = loadAsFileSync(dir + '/' + x);
if (m) return m;
var n = loadAsDirectorySync(dir + '/' + x);
if (n) return n;
}
var m = loadAsFileSync(x);
if (m) return m;
}
function nodeModulesPathsSync (start) {
var parts;
if (start === '/') parts = [ '' ];
else parts = path.normalize(start).split('/');
var dirs = [];
for (var i = parts.length - 1; i >= 0; i--) {
if (parts[i] === 'node_modules') continue;
var dir = parts.slice(0, i + 1).join('/') + '/node_modules';
dirs.push(dir);
}
return dirs;
}
};
})();
require.alias = function (from, to) {
var path = require.modules.path();
var res = null;
try {
res = require.resolve(from + '/package.json', '/');
}
catch (err) {
res = require.resolve(from, '/');
}
var basedir = path.dirname(res);
var keys = (Object.keys || function (obj) {
var res = [];
for (var key in obj) res.push(key);
return res;
})(require.modules);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (key.slice(0, basedir.length + 1) === basedir + '/') {
var f = key.slice(basedir.length);
require.modules[to + f] = require.modules[basedir + f];
}
else if (key === basedir) {
require.modules[to] = require.modules[basedir];
}
}
};
(function () {
var process = {};
var global = typeof window !== 'undefined' ? window : {};
var definedProcess = false;
require.define = function (filename, fn) {
if (!definedProcess && require.modules.__browserify_process) {
process = require.modules.__browserify_process();
definedProcess = true;
}
var dirname = require._core[filename]
? ''
: require.modules.path().dirname(filename)
;
var require_ = function (file) {
var requiredModule = require(file, dirname);
var cached = require.cache[require.resolve(file, dirname)];
if (cached && cached.parent === null) {
cached.parent = module_;
}
return requiredModule;
};
require_.resolve = function (name) {
return require.resolve(name, dirname);
};
require_.modules = require.modules;
require_.define = require.define;
require_.cache = require.cache;
var module_ = {
id : filename,
filename: filename,
exports : {},
loaded : false,
parent: null
};
require.modules[filename] = function () {
require.cache[filename] = module_;
fn.call(
module_.exports,
require_,
module_,
module_.exports,
dirname,
filename,
process,
global
);
module_.loaded = true;
return module_.exports;
};
};
})();
require.define("path",function(require,module,exports,__dirname,__filename,process,global){function filter (xs, fn) {
var res = [];
for (var i = 0; i < xs.length; i++) {
if (fn(xs[i], i, xs)) res.push(xs[i]);
}
return res;
}
// resolves . and .. elements in a path array with directory names there
// must be no slashes, empty elements, or device names (c:\) in the array
// (so also no leading and trailing slashes - it does not distinguish
// relative and absolute paths)
function normalizeArray(parts, allowAboveRoot) {
// if the path tries to go above the root, `up` ends up > 0
var up = 0;
for (var i = parts.length; i >= 0; i--) {
var last = parts[i];
if (last == '.') {
parts.splice(i, 1);
} else if (last === '..') {
parts.splice(i, 1);
up++;
} else if (up) {
parts.splice(i, 1);
up--;
}
}
// if the path is allowed to go above the root, restore leading ..s
if (allowAboveRoot) {
for (; up--; up) {
parts.unshift('..');
}
}
return parts;
}
// Regex to split a filename into [*, dir, basename, ext]
// posix version
var splitPathRe = /^(.+\/(?!$)|\/)?((?:.+?)?(\.[^.]*)?)$/;
// path.resolve([from ...], to)
// posix version
exports.resolve = function() {
var resolvedPath = '',
resolvedAbsolute = false;
for (var i = arguments.length; i >= -1 && !resolvedAbsolute; i--) {
var path = (i >= 0)
? arguments[i]
: process.cwd();
// Skip empty and invalid entries
if (typeof path !== 'string' || !path) {
continue;
}
resolvedPath = path + '/' + resolvedPath;
resolvedAbsolute = path.charAt(0) === '/';
}
// At this point the path should be resolved to a full absolute path, but
// handle relative paths to be safe (might happen when process.cwd() fails)
// Normalize the path
resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
return !!p;
}), !resolvedAbsolute).join('/');
return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
};
// path.normalize(path)
// posix version
exports.normalize = function(path) {
var isAbsolute = path.charAt(0) === '/',
trailingSlash = path.slice(-1) === '/';
// Normalize the path
path = normalizeArray(filter(path.split('/'), function(p) {
return !!p;
}), !isAbsolute).join('/');
if (!path && !isAbsolute) {
path = '.';
}
if (path && trailingSlash) {
path += '/';
}
return (isAbsolute ? '/' : '') + path;
};
// posix version
exports.join = function() {
var paths = Array.prototype.slice.call(arguments, 0);
return exports.normalize(filter(paths, function(p, index) {
return p && typeof p === 'string';
}).join('/'));
};
exports.dirname = function(path) {
var dir = splitPathRe.exec(path)[1] || '';
var isWindows = false;
if (!dir) {
// No dirname
return '.';
} else if (dir.length === 1 ||
(isWindows && dir.length <= 3 && dir.charAt(1) === ':')) {
// It is just a slash or a drive letter with a slash
return dir;
} else {
// It is a full dirname, strip trailing slash
return dir.substring(0, dir.length - 1);
}
};
exports.basename = function(path, ext) {
var f = splitPathRe.exec(path)[2] || '';
// TODO: make this comparison case-insensitive on windows?
if (ext && f.substr(-1 * ext.length) === ext) {
f = f.substr(0, f.length - ext.length);
}
return f;
};
exports.extname = function(path) {
return splitPathRe.exec(path)[3] || '';
};
});
require.define("__browserify_process",function(require,module,exports,__dirname,__filename,process,global){var process = module.exports = {};
process.nextTick = (function () {
var canSetImmediate = typeof window !== 'undefined'
&& window.setImmediate;
var canPost = typeof window !== 'undefined'
&& window.postMessage && window.addEventListener
;
if (canSetImmediate) {
return function (f) { return window.setImmediate(f) };
}
if (canPost) {
var queue = [];
window.addEventListener('message', function (ev) {
if (ev.source === window && ev.data === 'browserify-tick') {
ev.stopPropagation();
if (queue.length > 0) {
var fn = queue.shift();
fn();
}
}
}, true);
return function nextTick(fn) {
queue.push(fn);
window.postMessage('browserify-tick', '*');
};
}
return function nextTick(fn) {
setTimeout(fn, 0);
};
})();
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
process.binding = function (name) {
if (name === 'evals') return (require)('vm')
else throw new Error('No such module. (Possibly not yet loaded)')
};
(function () {
var cwd = '/';
var path;
process.cwd = function () { return cwd };
process.chdir = function (dir) {
if (!path) path = require('path');
cwd = path.resolve(dir, cwd);
};
})();
});
require.define("/lib/extended.js",function(require,module,exports,__dirname,__filename,process,global){module.exports = require("extended")()
.register(require("is-extended"))
.register(require("array-extended"))
.register(require("date-extended"))
.register(require("object-extended"))
.register(require("string-extended"))
.register(require("promise-extended"))
.register(require("function-extended"))
.register("declare", require("declare.js"))
.register("bus", new (require("events").EventEmitter)());
});
require.define("/node_modules/extended/package.json",function(require,module,exports,__dirname,__filename,process,global){module.exports = {"main":"index.js"}
});
require.define("/node_modules/extended/index.js",function(require,module,exports,__dirname,__filename,process,global){(function () {
"use strict";
/*global extender isa, dateExtended*/
function defineExtended(extender, require) {
var merge = (function merger() {
function _merge(target, source) {
var name, s;
for (name in source) {
if (source.hasOwnProperty(name)) {
s = source[name];
if (!(name in target) || (target[name] !== s)) {
target[name] = s;
}
}
}
return target;
}
return function merge(obj) {
if (!obj) {
obj = {};
}
for (var i = 1, l = arguments.length; i < l; i++) {
_merge(obj, arguments[i]);
}
return obj; // Object
};
}());
function getExtended() {
var loaded = {};
//getInitial instance;
var extended = extender.define();
extended.expose({
register: function register(alias, extendWith) {
if (!extendWith) {
extendWith = alias;
alias = null;
}
var type = typeof extendWith;
if (alias) {
extended[alias] = extendWith;
} else if (extendWith && type === "function") {
extended.extend(extendWith);
} else if (type === "object") {
extended.expose(extendWith);
} else {
throw new TypeError("extended.register must be called with an extender function");
}
return extended;
},
define: function () {
return extender.define.apply(extender, arguments);
}
});
return extended;
}
function extended() {
return getExtended();
}
extended.define = function define() {
return extender.define.apply(extender, arguments);
};
return extended;
}
if ("undefined" !== typeof exports) {
if ("undefined" !== typeof module && module.exports) {
module.exports = defineExtended(require("extender"), require);
}
} else if ("function" === typeof define) {
define(["require"], function (require) {
return defineExtended(require("extender"), require);
});
} else {
this.extended = defineExtended(this.extender);
}
}).call(this);
});
require.define("/node_modules/extended/node_modules/extender/package.json",function(require,module,exports,__dirname,__filename,process,global){module.exports = {"main":"index.js"}
});
require.define("/node_modules/extended/node_modules/extender/index.js",function(require,module,exports,__dirname,__filename,process,global){module.exports = require("./extender.js");
});
require.define("/node_modules/extended/node_modules/extender/extender.js",function(require,module,exports,__dirname,__filename,process,global){(function () {
/*jshint strict:false*/
/**
*
* @projectName extender
* @github http://github.com/doug-martin/extender
* @header
* [](http://travis-ci.org/doug-martin/extender)
* # Extender
*
* `extender` is a library that helps in making chainable APIs, by creating a function that accepts different values and returns an object decorated with functions based on the type.
*
* ## Why Is Extender Different?
*
* Extender is different than normal chaining because is does more than return `this`. It decorates your values in a type safe manner.
*
* For example if you return an array from a string based method then the returned value will be decorated with array methods and not the string methods. This allow you as the developer to focus on your API and not worrying about how to properly build and connect your API.
*
*
* ## Installation
*
* ```
* npm install extender
* ```
*
* Or [download the source](https://raw.github.com/doug-martin/extender/master/extender.js) ([minified](https://raw.github.com/doug-martin/extender/master/extender-min.js))
*
* **Note** `extender` depends on [`declare.js`](http://doug-martin.github.com/declare.js/).
*
* ### Requirejs
*
* To use with requirejs place the `extend` source in the root scripts directory
*
* ```javascript
*
* define(["extender"], function(extender){
* });
*
* ```
*
*
* ## Usage
*
* **`extender.define(tester, decorations)`**
*
* To create your own extender call the `extender.define` function.
*
* This function accepts an optional tester which is used to determine a value should be decorated with the specified `decorations`
*
* ```javascript
* function isString(obj) {
* return !isUndefinedOrNull(obj) && (typeof obj === "string" || obj instanceof String);
* }
*
*
* var myExtender = extender.define(isString, {
* multiply: function (str, times) {
* var ret = str;
* for (var i = 1; i < times; i++) {
* ret += str;
* }
* return ret;
* },
* toArray: function (str, delim) {
* delim = delim || "";
* return str.split(delim);
* }
* });
*
* myExtender("hello").multiply(2).value(); //hellohello
*
* ```
*
* If you do not specify a tester function and just pass in an object of `functions` then all values passed in will be decorated with methods.
*
* ```javascript
*
* function isUndefined(obj) {
* var undef;
* return obj === undef;
* }
*
* function isUndefinedOrNull(obj) {
* var undef;
* return obj === undef || obj === null;
* }
*
* function isArray(obj) {
* return Object.prototype.toString.call(obj) === "[object Array]";
* }
*
* function isBoolean(obj) {
* var undef, type = typeof obj;
* return !isUndefinedOrNull(obj) && type === "boolean" || type === "Boolean";
* }
*
* function isString(obj) {
* return !isUndefinedOrNull(obj) && (typeof obj === "string" || obj instanceof String);
* }
*
* var myExtender = extender.define({
* isUndefined : isUndefined,
* isUndefinedOrNull : isUndefinedOrNull,
* isArray : isArray,
* isBoolean : isBoolean,
* isString : isString
* });
*
* ```
*
* To use
*
* ```
* var undef;
* myExtender("hello").isUndefined().value(); //false
* myExtender(undef).isUndefined().value(); //true
* ```
*
* You can also chain extenders so that they accept multiple types and decorates accordingly.
*
* ```javascript
* myExtender
* .define(isArray, {
* pluck: function (arr, m) {
* var ret = [];
* for (var i = 0, l = arr.length; i < l; i++) {
* ret.push(arr[i][m]);
* }
* return ret;
* }
* })
* .define(isBoolean, {
* invert: function (val) {
* return !val;
* }
* });
*
* myExtender([{a: "a"},{a: "b"},{a: "c"}]).pluck("a").value(); //["a", "b", "c"]
* myExtender("I love javascript!").toArray(/\s+/).pluck("0"); //["I", "l", "j"]
*
* ```
*
* Notice that we reuse the same extender as defined above.
*
* **Return Values**
*
* When creating an extender if you return a value from one of the decoration functions then that value will also be decorated. If you do not return any values then the extender will be returned.
*
* **Default decoration methods**
*
* By default every value passed into an extender is decorated with the following methods.
*
* * `value` : The value this extender represents.
* * `eq(otherValue)` : Tests strict equality of the currently represented value to the `otherValue`
* * `neq(oterValue)` : Tests strict inequality of the currently represented value.
* * `print` : logs the current value to the console.
*
* **Extender initialization**
*
* When creating an extender you can also specify a constructor which will be invoked with the current value.
*
* ```javascript
* myExtender.define(isString, {
* constructor : function(val){
* //set our value to the string trimmed
* this._value = val.trimRight().trimLeft();
* }
* });
* ```
*
* **`noWrap`**
*
* `extender` also allows you to specify methods that should not have the value wrapped providing a cleaner exit function other than `value()`.
*
* For example suppose you have an API that allows you to build a validator, rather than forcing the user to invoke the `value` method you could add a method called `validator` which makes more syntactic sense.
*
* ```
*
* var myValidator = extender.define({
* //chainable validation methods
* //...
* //end chainable validation methods
*
* noWrap : {
* validator : function(){
* //return your validator
* }
* }
* });
*
* myValidator().isNotNull().isEmailAddress().validator(); //now you dont need to call .value()
*
*
* ```
* **`extender.extend(extendr)`**
*
* You may also compose extenders through the use of `extender.extend(extender)`, which will return an entirely new extender that is the composition of extenders.
*
* Suppose you have the following two extenders.
*
* ```javascript
* var myExtender = extender
* .define({
* isFunction: is.function,
* isNumber: is.number,
* isString: is.string,
* isDate: is.date,
* isArray: is.array,
* isBoolean: is.boolean,
* isUndefined: is.undefined,
* isDefined: is.defined,
* isUndefinedOrNull: is.undefinedOrNull,
* isNull: is.null,
* isArguments: is.arguments,
* isInstanceOf: is.instanceOf,
* isRegExp: is.regExp
* });
* var myExtender2 = extender.define(is.array, {
* pluck: function (arr, m) {
* var ret = [];
* for (var i = 0, l = arr.length; i < l; i++) {
* ret.push(arr[i][m]);
* }
* return ret;
* },
*
* noWrap: {
* pluckPlain: function (arr, m) {
* var ret = [];
* for (var i = 0, l = arr.length; i < l; i++) {
* ret.push(arr[i][m]);
* }
* return ret;
* }
* }
* });
*
*
* ```
*
* And you do not want to alter either of them but instead what to create a third that is the union of the two.
*
*
* ```javascript
* var composed = extender.extend(myExtender).extend(myExtender2);
* ```
* So now you can use the new extender with the joined functionality if `myExtender` and `myExtender2`.
*
* ```javascript
* var extended = composed([
* {a: "a"},
* {a: "b"},
* {a: "c"}
* ]);
* extended.isArray().value(); //true
* extended.pluck("a").value(); // ["a", "b", "c"]);
*
* ```
*
* **Note** `myExtender` and `myExtender2` will **NOT** be altered.
*
* **`extender.expose(methods)`**
*
* The `expose` method allows you to add methods to your extender that are not wrapped or automatically chained by exposing them on the extender directly.
*
* ```
* var isMethods = {
* isFunction: is.function,
* isNumber: is.number,
* isString: is.string,
* isDate: is.date,
* isArray: is.array,
* isBoolean: is.boolean,
* isUndefined: is.undefined,
* isDefined: is.defined,
* isUndefinedOrNull: is.undefinedOrNull,
* isNull: is.null,
* isArguments: is.arguments,
* isInstanceOf: is.instanceOf,
* isRegExp: is.regExp
* };
*
* var myExtender = extender.define(isMethods).expose(isMethods);
*
* myExtender.isArray([]); //true
* myExtender([]).isArray([]).value(); //true
*
* ```
*
*
* **Using `instanceof`**
*
* When using extenders you can test if a value is an `instanceof` of an extender by using the instanceof operator.
*
* ```javascript
* var str = myExtender("hello");
*
* str instanceof myExtender; //true
* ```
*
* ## Examples
*
* To see more examples click [here](https://github.com/doug-martin/extender/tree/master/examples)
*/
function defineExtender(declare) {
var slice = Array.prototype.slice, undef;
function indexOf(arr, item) {
if (arr && arr.length) {
for (var i = 0, l = arr.length; i < l; i++) {
if (arr[i] === item) {
return i;
}
}
}
return -1;
}
function isArray(obj) {
return Object.prototype.toString.call(obj) === "[object Array]";
}
var merge = (function merger() {
function _merge(target, source, exclude) {
var name, s;
for (name in source) {
if (source.hasOwnProperty(name) && indexOf(exclude, name) === -1) {
s = source[name];
if (!(name in target) || (target[name] !== s)) {
target[name] = s;
}
}
}
return target;
}
return function merge(obj) {
if (!obj) {
obj = {};
}
var l = arguments.length;
var exclude = arguments[arguments.length - 1];
if (isArray(exclude)) {
l--;
} else {
exclude = [];
}
for (var i = 1; i < l; i++) {
_merge(obj, arguments[i], exclude);
}
return obj; // Object
};
}());
function extender(supers) {
supers = supers || [];
var Base = declare({
instance: {
constructor: function (value) {
this._value = value;
},
value: function () {
return this._value;
},
eq: function eq(val) {
return this["__extender__"](this._value === val);
},
neq: function neq(other) {
return this["__extender__"](this._value !== other);
},
print: function () {
console.log(this._value);
return this;
}
}
}), defined = [];
function addMethod(proto, name, func) {
if ("function" !== typeof func) {
throw new TypeError("when extending type you must provide a function");
}
var extendedMethod;
if (name === "constructor") {
extendedMethod = function () {
this._super(arguments);
func.apply(this, arguments);
};
} else {
extendedMethod = function extendedMethod() {
var args = slice.call(arguments);
args.unshift(this._value);
var ret = func.apply(this, args);
return ret !== undef ? this["__extender__"](ret) : this;
};
}
proto[name] = extendedMethod;
}
function addNoWrapMethod(proto, name, func) {
if ("function" !== typeof func) {
throw new TypeError("when extending type you must provide a function");
}
var extendedMethod;
if (name === "constructor") {
extendedMethod = function () {
this._super(arguments);
func.apply(this, arguments);
};
} else {
extendedMethod = function extendedMethod() {
var args = slice.call(arguments);
args.unshift(this._value);
return func.apply(this, args);
};
}
proto[name] = extendedMethod;
}
function decorateProto(proto, decoration, nowrap) {
for (var i in decoration) {
if (decoration.hasOwnProperty(i)) {
if (i !== "getters" && i !== "setters") {
if (i === "noWrap") {
decorateProto(proto, decoration[i], true);
} else if (nowrap) {
addNoWrapMethod(proto, i, decoration[i]);
} else {
addMethod(proto, i, decoration[i]);
}
} else {
proto[i] = decoration[i];
}
}
}
}
function _extender(obj) {
var ret = obj, i, l;
if (!(obj instanceof Base)) {
var base = {}, instance = (base.instance = {"__extender__": _extender});
for (i = 0, l = defined.length; i < l; i++) {
var definer = defined[i];
if (definer[0](obj)) {
merge(instance, definer[1]);
}
}
ret = new (Base.extend(base))(obj);
}
return ret;
}
function always() {
return true;
}
function define(tester, decorate) {
if (arguments.length) {
if (typeof tester === "object") {
decorate = tester;
tester = always;
}
decorate = decorate || {};
var proto = {};
decorateProto(proto, decorate);
defined.push([tester, proto]);
}
return _extender;
}
function extend(supr) {
if (supr && supr.hasOwnProperty("__defined__")) {
_extender["__defined__"] = defined = defined.concat(supr["__defined__"]);
}
merge(_extender, supr, ["define", "extend", "expose", "__defined__"]);
return _extender;
}
_extender.define = define;
_extender.extend = extend;
_extender.expose = function expose() {
var methods;
for (var i = 0, l = arguments.length; i < l; i++) {
methods = arguments[i];
if (typeof methods === "object") {
merge(_extender, methods, ["define", "extend", "expose", "__defined__"]);
}
}
return _extender;
};
_extender["__defined__"] = defined;
return _extender;
}
return {
define: function () {
return extender().define.apply(extender, arguments);
},
extend: function (supr) {
return extender().define().extend(supr);
}
};
}
if ("undefined" !== typeof exports) {
if ("undefined" !== typeof module && module.exports) {
module.exports = defineExtender(require("declare.js"));
}
} else if ("function" === typeof define) {
define(["require"], function (require) {
return defineExtender((require("declare.js")));
});
} else {
this.extender = defineExtender(this.declare);
}
}).call(this);
});
require.define("/node_modules/declare.js/package.json",function(require,module,exports,__dirname,__filename,process,global){module.exports = {"main":"index.js"}
});
require.define("/node_modules/declare.js/index.js",function(require,module,exports,__dirname,__filename,process,global){module.exports = require("./declare.js");
});
require.define("/node_modules/declare.js/declare.js",function(require,module,exports,__dirname,__filename,process,global){(function () {
/**
* @projectName declare
* @github http://github.com/doug-martin/declare.js
* @header
*
* Declare is a library designed to allow writing object oriented code the same way in both the browser and node.js.
*
* ##Installation
*
* `npm install declare.js`
*
* Or [download the source](https://raw.github.com/doug-martin/declare.js/master/declare.js) ([minified](https://raw.github.com/doug-martin/declare.js/master/declare-min.js))
*
* ###Requirejs
*
* To use with requirejs place the `declare` source in the root scripts directory
*
* ```
*
* define(["declare"], function(declare){
* return declare({
* instance : {
* hello : function(){
* return "world";
* }
* }
* });
* });
*
* ```
*
*
* ##Usage
*
* declare.js provides
*
* Class methods
*
* * `as(module | object, name)` : exports the object to module or the object with the name
* * `mixin(mixin)` : mixes in an object but does not inherit directly from the object. **Note** this does not return a new class but changes the original class.
* * `extend(proto)` : extend a class with the given properties. A shortcut to `declare(Super, {})`;
*
* Instance methods
*
* * `_super(arguments)`: calls the super of the current method, you can pass in either the argments object or an array with arguments you want passed to super
* * `_getSuper()`: returns a this methods direct super.
* * `_static` : use to reference class properties and methods.
* * `get(prop)` : gets a property invoking the getter if it exists otherwise it just returns the named property on the object.
* * `set(prop, val)` : sets a property invoking the setter if it exists otherwise it just sets the named property on the object.
*
*
* ###Declaring a new Class
*
* Creating a new class with declare is easy!
*
* ```
*
* var Mammal = declare({
* //define your instance methods and properties
* instance : {
*
* //will be called whenever a new instance is created
* constructor: function(options) {
* options = options || {};
* this._super(arguments);
* this._type = options.type || "mammal";
* },
*
* speak : function() {
* return "A mammal of type " + this._type + " sounds like";
* },
*
* //Define your getters
* getters : {
*
* //can be accessed by using the get method. (mammal.get("type"))
* type : function() {
* return this._type;
* }
* },
*
* //Define your setters
* setters : {
*
* //can be accessed by using the set method. (mammal.set("type", "mammalType"))
* type : function(t) {
* this._type = t;
* }
* }
* },
*
* //Define your static methods
* static : {
*
* //Mammal.soundOff(); //"Im a mammal!!"
* soundOff : function() {
* return "Im a mammal!!";
* }
* }
* });
*
*
* ```
*
* You can use Mammal just like you would any other class.
*
* ```
* Mammal.soundOff("Im a mammal!!");
*
* var myMammal = new Mammal({type : "mymammal"});
* myMammal.speak(); // "A mammal of type mymammal sounds like"
* myMammal.get("type"); //"mymammal"
* myMammal.set("type", "mammal");
* myMammal.get("type"); //"mammal"
*
*
* ```
*
* ###Extending a class
*
* If you want to just extend a single class use the .extend method.
*
* ```
*
* var Wolf = Mammal.extend({
*
* //define your instance method
* instance: {
*
* //You can override super constructors just be sure to call `_super`
* constructor: function(options) {
* options = options || {};
* this._super(arguments); //call our super constructor.
* this._sound = "growl";
* this._color = options.color || "grey";
* },
*
* //override Mammals `speak` method by appending our own data to it.
* speak : function() {
* return this._super(arguments) + " a " + this._sound;
* },
*
* //add new getters for sound and color
* getters : {
*
* //new Wolf().get("type")
* //notice color is read only as we did not define a setter
* color : function() {
* return this._color;
* },
*
* //new Wolf().get("sound")
* sound : function() {
* return this._sound;
* }
* },
*
* setters : {
*
* //new Wolf().set("sound", "howl")
* sound : function(s) {
* this._sound = s;
* }
* }
*
* },
*
* static : {
*
* //You can override super static methods also! And you can still use _super
* soundOff : function() {
* //You can even call super in your statics!!!
* //should return "I'm a mammal!! that growls"
* return this._super(arguments) + " that growls";
* }
* }
* });
*
* Wolf.soundOff(); //Im a mammal!! that growls
*
* var myWolf = new Wolf();
* myWolf instanceof Mammal //true
* myWolf instanceof Wolf //true
*
* ```
*
* You can also extend a class by using the declare method and just pass in the super class.
*
* ```
* //Typical hierarchical inheritance
* // Mammal->Wolf->Dog
* var Dog = declare(Wolf, {
* instance: {
* constructor: function(options) {
* options = options || {};
* this._super(arguments);
* //override Wolfs initialization of sound to woof.
* this._sound = "woof";
*
* },
*
* speak : function() {
* //Should return "A mammal of type mammal sounds like a growl thats domesticated"
* return this._super(arguments) + " thats domesticated";
* }
* },
*
* static : {
* soundOff : function() {
* //should return "I'm a mammal!! that growls but now barks"
* return this._super(arguments) + " but now barks";
* }
* }
* });
*
* Dog.soundOff(); //Im a mammal!! that growls but now barks
*
* var myDog = new Dog();
* myDog instanceof Mammal //true
* myDog instanceof Wolf //true
* myDog instanceof Dog //true
*
*
* //Notice you still get the extend method.
*
* // Mammal->Wolf->Dog->Breed
* var Breed = Dog.extend({
* instance: {
*
* //initialize outside of constructor
* _pitch : "high",
*
* constructor: function(options) {
* options = options || {};
* this._super(arguments);
* this.breed = options.breed || "lab";
* },
*
* speak : function() {
* //Should return "A mammal of type mammal sounds like a
* //growl thats domesticated with a high pitch!"
* return this._super(arguments) + " with a " + this._pitch + " pitch!";
* },
*
* getters : {
* pitch : function() {
* return this._pitch;
* }
* }
* },
*
* static : {
* soundOff : function() {
* //should return "I'M A MAMMAL!! THAT GROWLS BUT NOW BARKS!"
* return this._super(arguments).toUpperCase() + "!";
* }
* }
* });
*
*
* Breed.soundOff()//"IM A MAMMAL!! THAT GROWLS BUT NOW BARKS!"
*
* var myBreed = new Breed({color : "gold", type : "lab"}),
* myBreed instanceof Dog //true
* myBreed instanceof Wolf //true
* myBreed instanceof Mammal //true
* myBreed.speak() //"A mammal of type lab sounds like a woof thats domesticated with a high pitch!"
* myBreed.get("type") //"lab"
* myBreed.get("color") //"gold"
* myBreed.get("sound")" //"woof"
* ```
*
* ###Multiple Inheritance / Mixins
*
* declare also allows the use of multiple super classes.
* This is useful if you have generic classes that provide functionality but shouldnt be used on their own.
*
* Lets declare a mixin that allows us to watch for property changes.
*
* ```
* //Notice that we set up the functions outside of declare because we can reuse them
*
* function _set(prop, val) {
* //get the old value
* var oldVal = this.get(prop);
* //call super to actually set the property
* var ret = this._super(arguments);
* //call our handlers
* this.__callHandlers(prop, oldVal, val);
* return ret;
* }
*
* function _callHandlers(prop, oldVal, newVal) {
* //get our handlers for the property
* var handlers = this.__watchers[prop], l;
* //if the handlers exist and their length does not equal 0 then we call loop through them
* if (handlers && (l = handlers.length) !== 0) {
* for (var i = 0; i < l; i++) {
* //call the handler
* handlers[i].call(null, prop, oldVal, newVal);
* }
* }
* }
*
*
* //the watch function
* function _watch(prop, handler) {
* if ("function" !== typeof handler) {
* //if its not a function then its an invalid handler
* throw new TypeError("Invalid handler.");
* }
* if (!this.__watchers[prop]) {
* //create the watchers if it doesnt exist
* this.__watchers[prop] = [handler];
* } else {
* //otherwise just add it to the handlers array
* this.__watchers[prop].push(handler);
* }
* }
*
* function _unwatch(prop, handler) {
* if ("function" !== typeof handler) {
* throw new TypeError("Invalid handler.");
* }
* var handlers = this.__watchers[prop], index;
* if (handlers && (index = handlers.indexOf(handler)) !== -1) {
* //remove the handler if it is found
* handlers.splice(index, 1);
* }
* }
*
* declare({
* instance:{
* constructor:function () {
* this._super(arguments);
* //set up our watchers
* this.__watchers = {};
* },
*
* //override the default set function so we can watch values
* "set":_set,
* //set up our callhandlers function
* __callHandlers:_callHandlers,
* //add the watch function
* watch:_watch,
* //add the unwatch function
* unwatch:_unwatch
* },
*
* "static":{
*
* init:function () {
* this._super(arguments);
* this.__watchers = {};
* },
* //override the default set function so we can watch values
* "set":_set,
* //set our callHandlers function
* __callHandlers:_callHandlers,
* //add the watch
* watch:_watch,
* //add the unwatch function
* unwatch:_unwatch
* }
* })
*
* ```
*
* Now lets use the mixin
*
* ```
* var WatchDog = declare([Dog, WatchMixin]);
*
* var watchDog = new WatchDog();
* //create our handler
* function watch(id, oldVal, newVal) {
* console.log("watchdog's %s was %s, now %s", id, oldVal, newVal);
* }
*
* //watch for property changes
* watchDog.watch("type", watch);
* watchDog.watch("color", watch);
* watchDog.watch("sound", watch);
*
* //now set the properties each handler will be called
* watchDog.set("type", "newDog");
* watchDog.set("color", "newColor");
* watchDog.set("sound", "newSound");
*
*
* //unwatch the property changes
* watchDog.unwatch("type", watch);
* watchDog.unwatch("color", watch);
* watchDog.unwatch("sound", watch);
*
* //no handlers will be called this time
* watchDog.set("type", "newDog");
* watchDog.set("color", "newColor");
* watchDog.set("sound", "newSound");
*
*
* ```
*
* ###Accessing static methods and properties witin an instance.
*
* To access static properties on an instance use the `_static` property which is a reference to your constructor.
*
* For example if your in your constructor and you want to have configurable default values.
*
* ```
* consturctor : function constructor(opts){
* this.opts = opts || {};
* this._type = opts.type || this._static.DEFAULT_TYPE;
* }
* ```
*
*
*
* ###Creating a new instance of within an instance.
*
* Often times you want to create a new instance of an object within an instance. If your subclassed however you cannot return a new instance of the parent class as it will not be the right sub class. `declare` provides a way around this by setting the `_static` property on each isntance of the class.
*
* Lets add a reproduce method `Mammal`
*
* ```
* reproduce : function(options){
* return new this._static(options);
* }
* ```
*
* Now in each subclass you can call reproduce and get the proper type.
*
* ```
* var myDog = new Dog();
* var myDogsChild = myDog.reproduce();
*
* myDogsChild instanceof Dog; //true
* ```
*
* ###Using the `as`
*
* `declare` also provides an `as` method which allows you to add your class to an object or if your using node.js you can pass in `module` and the class will be exported as the module.
*
* ```
* var animals = {};
*
* Mammal.as(animals, "Dog");
* Wolf.as(animals, "Wolf");
* Dog.as(animals, "Dog");
* Breed.as(animals, "Breed");
*
* var myDog = new animals.Dog();
*
* ```
*
* Or in node
*
* ```
* Mammal.as(exports, "Dog");
* Wolf.as(exports, "Wolf");
* Dog.as(exports, "Dog");
* Breed.as(exports, "Breed");
*
* ```
*
* To export a class as the `module` in node
*
* ```
* Mammal.as(module);
* ```
*
*
*/
function createDeclared() {
var arraySlice = Array.prototype.slice, classCounter = 0, Base, forceNew = new Function();
function argsToArray(args, slice) {
slice = slice || 0;
return arraySlice.call(args, slice);
}
function isArray(obj) {
return Object.prototype.toString.call(obj) === "[object Array]";
}
function isObject(obj) {
var undef;
return obj !== null && obj !== undef && typeof obj === "object";
}
function isHash(obj) {
var ret = isObject(obj);
return ret && obj.constructor === Object;
}
function indexOf(arr, item) {
if (arr && arr.length) {
for (var i = 0, l = arr.length; i < l; i++) {
if (arr[i] === item) {
return i;
}
}
}
return -1;
}
function merge(target, source, exclude) {
var name, s;
for (name in source) {
if (source.hasOwnProperty(name) && indexOf(exclude, name) === -1) {
s = source[name];
if (!(name in targ