jstest
Version:
The cross-platform JavaScript test framework
1,924 lines (1,535 loc) • 222 kB
JavaScript
var JS = (typeof this.JS === 'undefined') ? {} : this.JS;
JS.Date = Date;
(function(factory) {
var $ = (typeof this.global === 'object') ? this.global : this,
E = (typeof exports === 'object');
if (E) {
exports.JS = exports;
JS = exports;
} else {
$.JS = JS;
}
factory($, JS);
})(function(global, exports) {
'use strict';
var Package = function(loader) {
Package._index(this);
this._loader = loader;
this._names = new OrderedSet();
this._deps = new OrderedSet();
this._uses = new OrderedSet();
this._styles = new OrderedSet();
this._observers = {};
this._events = {};
};
Package.displayName = 'Package';
Package.toString = function() { return Package.displayName };
Package.log = function(message) {
if (!exports.debug) return;
if (typeof window === 'undefined') return;
if (typeof global.runtime === 'object') runtime.trace(message);
if (global.console && console.info) console.info(message);
};
var resolve = function(filename) {
if (/^https?:/.test(filename)) return filename;
var root = exports.ROOT;
if (root) filename = (root + '/' + filename).replace(/\/+/g, '/');
return filename;
};
//================================================================
// Ordered list of unique elements, for storing dependencies
var OrderedSet = function(list) {
this._members = this.list = [];
this._index = {};
if (!list) return;
for (var i = 0, n = list.length; i < n; i++)
this.push(list[i]);
};
OrderedSet.prototype.push = function(item) {
var key = (item.id !== undefined) ? item.id : item,
index = this._index;
if (index.hasOwnProperty(key)) return;
index[key] = this._members.length;
this._members.push(item);
};
//================================================================
// Wrapper for deferred values
var Deferred = Package.Deferred = function() {
this._status = 'deferred';
this._value = null;
this._callbacks = [];
};
Deferred.prototype.callback = function(callback, context) {
if (this._status === 'succeeded') callback.call(context, this._value);
else this._callbacks.push([callback, context]);
};
Deferred.prototype.succeed = function(value) {
this._status = 'succeeded';
this._value = value;
var callback;
while (callback = this._callbacks.shift())
callback[0].call(callback[1], value);
};
//================================================================
// Environment settings
Package.ENV = exports.ENV = global;
Package.onerror = function(e) { throw e };
Package._throw = function(message) {
Package.onerror(new Error(message));
};
//================================================================
// Configuration methods, called by the DSL
var instance = Package.prototype,
methods = [['requires', '_deps'],
['uses', '_uses']],
i = methods.length;
while (i--)
(function(pair) {
var method = pair[0], list = pair[1];
instance[method] = function() {
var n = arguments.length, i;
for (i = 0; i < n; i++) this[list].push(arguments[i]);
return this;
};
})(methods[i]);
instance.provides = function() {
var n = arguments.length, i;
for (i = 0; i < n; i++) {
this._names.push(arguments[i]);
Package._getFromCache(arguments[i]).pkg = this;
}
return this;
};
instance.styling = function() {
for (var i = 0, n = arguments.length; i < n; i++)
this._styles.push(resolve(arguments[i]));
};
instance.setup = function(block) {
this._onload = block;
return this;
};
//================================================================
// Event dispatchers, for communication between packages
instance._on = function(eventType, block, context) {
if (this._events[eventType]) return block.call(context);
var list = this._observers[eventType] = this._observers[eventType] || [];
list.push([block, context]);
this._load();
};
instance._fire = function(eventType) {
if (this._events[eventType]) return false;
this._events[eventType] = true;
var list = this._observers[eventType];
if (!list) return true;
delete this._observers[eventType];
for (var i = 0, n = list.length; i < n; i++)
list[i][0].call(list[i][1]);
return true;
};
//================================================================
// Loading frontend and other miscellany
instance._isLoaded = function(withExceptions) {
if (!withExceptions && this.__isLoaded !== undefined) return this.__isLoaded;
var names = this._names.list,
i = names.length,
name, object;
while (i--) { name = names[i];
object = Package._getObject(name, this._exports);
if (object !== undefined) continue;
if (withExceptions)
return Package._throw('Expected package at ' + this._loader + ' to define ' + name);
else
return this.__isLoaded = false;
}
return this.__isLoaded = true;
};
instance._load = function() {
if (!this._fire('request')) return;
if (!this._isLoaded()) this._prefetch();
var allDeps = this._deps.list.concat(this._uses.list),
source = this._source || [],
n = (this._loader || {}).length,
self = this;
Package.when({load: allDeps});
Package.when({complete: this._deps.list}, function() {
Package.when({complete: allDeps, load: [this]}, function() {
this._fire('complete');
}, this);
var loadNext = function(exports) {
if (n === 0) return fireOnLoad(exports);
n -= 1;
var index = self._loader.length - n - 1;
Package.loader.loadFile(self._loader[index], loadNext, source[index]);
};
var fireOnLoad = function(exports) {
self._exports = exports;
if (self._onload) self._onload();
self._isLoaded(true);
self._fire('load');
};
if (this._isLoaded()) {
this._fire('download');
return this._fire('load');
}
if (this._loader === undefined)
return Package._throw('No load path found for ' + this._names.list[0]);
if (typeof this._loader === 'function')
this._loader(fireOnLoad);
else
loadNext();
if (!Package.loader.loadStyle) return;
var styles = this._styles.list,
i = styles.length;
while (i--) Package.loader.loadStyle(styles[i]);
this._fire('download');
}, this);
};
instance._prefetch = function() {
if (this._source || !(this._loader instanceof Array) || !Package.loader.fetch)
return;
this._source = [];
for (var i = 0, n = this._loader.length; i < n; i++)
this._source[i] = Package.loader.fetch(this._loader[i]);
};
instance.toString = function() {
return 'Package:' + this._names.list.join(',');
};
//================================================================
// Class-level event API, handles group listeners
Package.when = function(eventTable, block, context) {
var eventList = [], objects = {}, event, packages, i;
for (event in eventTable) {
if (!eventTable.hasOwnProperty(event)) continue;
objects[event] = [];
packages = new OrderedSet(eventTable[event]);
i = packages.list.length;
while (i--) eventList.push([event, packages.list[i], i]);
}
var waiting = i = eventList.length;
if (waiting === 0) return block && block.call(context, objects);
while (i--)
(function(event) {
var pkg = Package._getByName(event[1]);
pkg._on(event[0], function() {
objects[event[0]][event[2]] = Package._getObject(event[1], pkg._exports);
waiting -= 1;
if (waiting === 0 && block) block.call(context, objects);
});
})(eventList[i]);
};
//================================================================
// Indexes for fast lookup by path and name, and assigning IDs
var globalPackage = (global.JS || {}).Package || {};
Package._autoIncrement = globalPackage._autoIncrement || 1;
Package._indexByPath = globalPackage._indexByPath || {};
Package._indexByName = globalPackage._indexByName || {};
Package._autoloaders = globalPackage._autoloaders || [];
Package._index = function(pkg) {
pkg.id = this._autoIncrement;
this._autoIncrement += 1;
};
Package._getByPath = function(loader) {
var path = loader.toString(),
pkg = this._indexByPath[path];
if (pkg) return pkg;
if (typeof loader === 'string')
loader = [].slice.call(arguments);
pkg = this._indexByPath[path] = new this(loader);
return pkg;
};
Package._getByName = function(name) {
if (typeof name !== 'string') return name;
var cached = this._getFromCache(name);
if (cached.pkg) return cached.pkg;
var autoloaded = this._manufacture(name);
if (autoloaded) return autoloaded;
var placeholder = new this();
placeholder.provides(name);
return placeholder;
};
Package.remove = function(name) {
var pkg = this._getByName(name);
delete this._indexByName[name];
delete this._indexByPath[pkg._loader];
};
//================================================================
// Auotloading API, generates packages from naming patterns
Package._autoload = function(pattern, options) {
this._autoloaders.push([pattern, options]);
};
Package._manufacture = function(name) {
var autoloaders = this._autoloaders,
n = autoloaders.length,
i, j, autoloader, path;
for (i = 0; i < n; i++) {
autoloader = autoloaders[i];
if (!autoloader[0].test(name)) continue;
path = autoloader[1].from;
if (typeof path === 'string') path = this._convertNameToPath(path);
var pkg = new this([path(name)]);
pkg.provides(name);
if (path = autoloader[1].require) {
path = [].concat(path);
j = path.length;
while (j--) pkg.requires(name.replace(autoloader[0], path[j]));
}
return pkg;
}
return null;
};
Package._convertNameToPath = function(from) {
return function(name) {
return from.replace(/\/?$/, '/') +
name.replace(/([a-z])([A-Z])/g, function(m,a,b) { return a + '_' + b })
.replace(/\./g, '/')
.toLowerCase() + '.js';
};
};
//================================================================
// Cache for named packages and runtime objects
Package._getFromCache = function(name) {
return this._indexByName[name] = this._indexByName[name] || {};
};
Package._getObject = function(name, rootObject) {
if (typeof name !== 'string') return undefined;
var cached = rootObject ? {} : this._getFromCache(name);
if (cached.obj !== undefined) return cached.obj;
var object = rootObject || this.ENV,
parts = name.split('.'), part;
while (part = parts.shift()) object = object && object[part];
if (rootObject && object === undefined)
return this._getObject(name);
return cached.obj = object;
};
Package.CommonJSLoader = {
usable: function() {
return typeof require === 'function' &&
typeof exports === 'object';
},
__FILE__: function() {
return this._currentPath;
},
loadFile: function(path, fireCallbacks) {
var file, module;
if (typeof process !== 'undefined') {
module = path.replace(/\.[^\.]+$/g, '');
file = require('path').resolve(module);
}
else if (typeof phantom !== 'undefined') {
file = phantom.libraryPath.replace(/\/$/, '') + '/' +
path.replace(/^\//, '');
}
this._currentPath = file + '.js';
var module = require(file);
fireCallbacks(module);
return module;
}
};
Package.BrowserLoader = {
HOST_REGEX: /^(https?\:)?\/\/[^\/]+/i,
usable: function() {
return !!Package._getObject('window.document.getElementsByTagName') &&
typeof phantom === 'undefined';
},
__FILE__: function() {
var scripts = document.getElementsByTagName('script'),
src = scripts[scripts.length - 1].src,
url = window.location.href;
if (/^\w+\:\/+/.test(src)) return src;
if (/^\//.test(src)) return window.location.origin + src;
return url.replace(/[^\/]*$/g, '') + src;
},
cacheBust: function(path) {
if (exports.cache !== false) return path;
var token = new JS.Date().getTime();
return path + (/\?/.test(path) ? '&' : '?') + token;
},
fetch: function(path) {
var originalPath = path;
path = this.cacheBust(path);
this.HOST = this.HOST || this.HOST_REGEX.exec(window.location.href);
var host = this.HOST_REGEX.exec(path);
if (!this.HOST || (host && host[0] !== this.HOST[0])) return null;
Package.log('[FETCH] ' + path);
var source = new Package.Deferred(),
self = this,
xhr = window.ActiveXObject
? new ActiveXObject('Microsoft.XMLHTTP')
: new XMLHttpRequest();
xhr.open('GET', path, true);
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4) return;
xhr.onreadystatechange = self._K;
source.succeed(xhr.responseText + '\n//@ sourceURL=' + originalPath);
xhr = null;
};
xhr.send(null);
return source;
},
loadFile: function(path, fireCallbacks, source) {
if (!source) path = this.cacheBust(path);
var self = this,
head = document.getElementsByTagName('head')[0],
script = document.createElement('script');
script.type = 'text/javascript';
if (source)
return source.callback(function(code) {
Package.log('[EXEC] ' + path);
var execute = new Function('code', 'eval(code)');
execute(code);
fireCallbacks();
});
Package.log('[LOAD] ' + path);
script.src = path;
script.onload = script.onreadystatechange = function() {
var state = script.readyState, status = script.status;
if ( !state || state === 'loaded' || state === 'complete' ||
(state === 4 && status === 200) ) {
fireCallbacks();
script.onload = script.onreadystatechange = self._K;
head = null;
script = null;
}
};
head.appendChild(script);
},
loadStyle: function(path) {
var link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = this.cacheBust(path);
document.getElementsByTagName('head')[0].appendChild(link);
},
_K: function() {}
};
Package.RhinoLoader = {
usable: function() {
return typeof java === 'object' &&
typeof require === 'function';
},
__FILE__: function() {
return this._currentPath;
},
loadFile: function(path, fireCallbacks) {
var cwd = java.lang.System.getProperty('user.dir'),
module = path.replace(/\.[^\.]+$/g, '');
var requirePath = new java.io.File(cwd, module).toString();
this._currentPath = requirePath + '.js';
var module = require(requirePath);
fireCallbacks(module);
return module;
}
};
Package.ServerLoader = {
usable: function() {
return typeof Package._getObject('load') === 'function' &&
typeof Package._getObject('version') === 'function';
},
__FILE__: function() {
return this._currentPath;
},
loadFile: function(path, fireCallbacks) {
this._currentPath = path;
load(path);
fireCallbacks();
}
};
Package.WshLoader = {
usable: function() {
return !!Package._getObject('ActiveXObject') &&
!!Package._getObject('WScript');
},
__FILE__: function() {
return this._currentPath;
},
loadFile: function(path, fireCallbacks) {
this._currentPath = path;
var fso = new ActiveXObject('Scripting.FileSystemObject'), file, runner;
try {
file = fso.OpenTextFile(path);
runner = function() { eval(file.ReadAll()) };
runner();
fireCallbacks();
} finally {
try { if (file) file.Close() } catch (e) {}
}
}
};
Package.XULRunnerLoader = {
jsloader: '@mozilla.org/moz/jssubscript-loader;1',
cssservice: '@mozilla.org/content/style-sheet-service;1',
ioservice: '@mozilla.org/network/io-service;1',
usable: function() {
try {
var CC = (Components || {}).classes;
return !!(CC && CC[this.jsloader] && CC[this.jsloader].getService);
} catch(e) {
return false;
}
},
setup: function() {
var Cc = Components.classes, Ci = Components.interfaces;
this.ssl = Cc[this.jsloader].getService(Ci.mozIJSSubScriptLoader);
this.sss = Cc[this.cssservice].getService(Ci.nsIStyleSheetService);
this.ios = Cc[this.ioservice].getService(Ci.nsIIOService);
},
loadFile: function(path, fireCallbacks) {
Package.log('[LOAD] ' + path);
this.ssl.loadSubScript(path);
fireCallbacks();
},
loadStyle: function(path) {
var uri = this.ios.newURI(path, null, null);
this.sss.loadAndRegisterSheet(uri, this.sss.USER_SHEET);
}
};
var candidates = [ Package.XULRunnerLoader,
Package.RhinoLoader,
Package.BrowserLoader,
Package.CommonJSLoader,
Package.ServerLoader,
Package.WshLoader ],
n = candidates.length,
i, candidate;
for (i = 0; i < n; i++) {
candidate = candidates[i];
if (candidate.usable()) {
Package.loader = candidate;
if (candidate.setup) candidate.setup();
break;
}
}
var DSL = {
__FILE__: function() {
return Package.loader.__FILE__();
},
pkg: function(name, path) {
var pkg = path
? Package._getByPath(path)
: Package._getByName(name);
pkg.provides(name);
return pkg;
},
file: function(filename) {
var files = [], i = arguments.length;
while (i--) files[i] = resolve(arguments[i]);
return Package._getByPath.apply(Package, files);
},
load: function(path, fireCallbacks) {
Package.loader.loadFile(path, fireCallbacks);
},
autoload: function(pattern, options) {
Package._autoload(pattern, options);
}
};
DSL.files = DSL.file;
DSL.loader = DSL.file;
var packages = function(declaration) {
declaration.call(DSL);
};
var parseLoadArgs = function(args) {
var files = [], i = 0;
while (typeof args[i] === 'string'){
files.push(args[i]);
i += 1;
}
return {files: files, callback: args[i], context: args[i+1]};
};
exports.load = function(path, callback) {
var args = parseLoadArgs(arguments),
n = args.files.length;
var loadNext = function(index) {
if (index === n) return args.callback.call(args.context);
Package.loader.loadFile(args.files[index], function() {
loadNext(index + 1);
});
};
loadNext(0);
};
exports.require = function() {
var args = parseLoadArgs(arguments);
Package.when({complete: args.files}, function(objects) {
if (!args.callback) return;
args.callback.apply(args.context, objects && objects.complete);
});
return this;
};
exports.Package = Package;
exports.Packages = exports.packages = packages;
exports.DSL = DSL;
});
var JS = (typeof this.JS === 'undefined') ? {} : this.JS;
(function(factory) {
var $ = (typeof this.global === 'object') ? this.global : this,
E = (typeof exports === 'object');
if (E) {
exports.JS = exports;
JS = exports;
} else {
$.JS = JS;
}
factory($, JS);
})(function(global, exports) {
'use strict';
var JS = {ENV: global};
JS.END_WITHOUT_DOT = /([^\.])$/;
JS.array = function(enumerable) {
var array = [], i = enumerable.length;
while (i--) array[i] = enumerable[i];
return array;
};
JS.bind = function(method, object) {
return function() {
return method.apply(object, arguments);
};
};
JS.Date = JS.ENV.Date;
JS.extend = function(destination, source, overwrite) {
if (!destination || !source) return destination;
for (var field in source) {
if (destination[field] === source[field]) continue;
if (overwrite === false && destination.hasOwnProperty(field)) continue;
destination[field] = source[field];
}
return destination;
};
JS.indexOf = function(list, item) {
if (list.indexOf) return list.indexOf(item);
var i = list.length;
while (i--) {
if (list[i] === item) return i;
}
return -1;
};
JS.isType = function(object, type) {
if (typeof type === 'string')
return typeof object === type;
if (object === null || object === undefined)
return false;
return (typeof type === 'function' && object instanceof type) ||
(object.isA && object.isA(type)) ||
object.constructor === type;
};
JS.makeBridge = function(parent) {
var bridge = function() {};
bridge.prototype = parent.prototype;
return new bridge();
};
JS.makeClass = function(parent) {
parent = parent || Object;
var constructor = function() {
return this.initialize
? this.initialize.apply(this, arguments) || this
: this;
};
constructor.prototype = JS.makeBridge(parent);
constructor.superclass = parent;
constructor.subclasses = [];
if (parent.subclasses) parent.subclasses.push(constructor);
return constructor;
};
JS.match = function(category, object) {
if (object === undefined) return false;
return typeof category.test === 'function'
? category.test(object)
: category.match(object);
};
JS.Method = JS.makeClass();
JS.extend(JS.Method.prototype, {
initialize: function(module, name, callable) {
this.module = module;
this.name = name;
this.callable = callable;
this._words = {};
if (typeof callable !== 'function') return;
this.arity = callable.length;
var matches = callable.toString().match(/\b[a-z\_\$][a-z0-9\_\$]*\b/ig),
i = matches.length;
while (i--) this._words[matches[i]] = true;
},
setName: function(name) {
this.callable.displayName =
this.displayName = name;
},
contains: function(word) {
return this._words.hasOwnProperty(word);
},
call: function() {
return this.callable.call.apply(this.callable, arguments);
},
apply: function(receiver, args) {
return this.callable.apply(receiver, args);
},
compile: function(environment) {
var method = this,
trace = method.module.__trace__ || environment.__trace__,
callable = method.callable,
words = method._words,
allWords = JS.Method._keywords,
i = allWords.length,
keywords = [],
keyword;
while (i--) {
keyword = allWords[i];
if (words[keyword.name]) keywords.push(keyword);
}
if (keywords.length === 0 && !trace) return callable;
var compiled = function() {
var N = keywords.length, j = N, previous = {}, keyword, existing, kwd;
while (j--) {
keyword = keywords[j];
existing = this[keyword.name];
if (existing && !existing.__kwd__) continue;
previous[keyword.name] = {
_value: existing,
_own: this.hasOwnProperty(keyword.name)
};
kwd = keyword.filter(method, environment, this, arguments);
if (kwd) kwd.__kwd__ = true;
this[keyword.name] = kwd;
}
var returnValue = callable.apply(this, arguments),
j = N;
while (j--) {
keyword = keywords[j];
if (!previous[keyword.name]) continue;
if (previous[keyword.name]._own)
this[keyword.name] = previous[keyword.name]._value;
else
delete this[keyword.name];
}
return returnValue;
};
var StackTrace = trace && (exports.StackTrace || require('./stack_trace').StackTrace);
if (trace) return StackTrace.wrap(compiled, method, environment);
return compiled;
},
toString: function() {
var name = this.displayName || (this.module.toString() + '#' + this.name);
return '#<Method:' + name + '>';
}
});
JS.Method.create = function(module, name, callable) {
if (callable && callable.__inc__ && callable.__fns__)
return callable;
var method = (typeof callable !== 'function')
? callable
: new this(module, name, callable);
this.notify(method);
return method;
};
JS.Method.compile = function(method, environment) {
return (method instanceof this)
? method.compile(environment)
: method;
};
JS.Method.__listeners__ = [];
JS.Method.added = function(block, context) {
this.__listeners__.push([block, context]);
};
JS.Method.notify = function(method) {
var listeners = this.__listeners__,
i = listeners.length,
listener;
while (i--) {
listener = listeners[i];
listener[0].call(listener[1], method);
}
};
JS.Method._keywords = [];
JS.Method.keyword = function(name, filter) {
this._keywords.push({name: name, filter: filter});
};
JS.Method.tracing = function(classes, block, context) {
var pkg = exports.require ? exports : require('./loader');
pkg.require('JS.StackTrace', function(StackTrace) {
var logger = StackTrace.logger,
active = logger.active;
classes = [].concat(classes);
this.trace(classes);
logger.active = true;
block.call(context);
this.untrace(classes);
logger.active = active;
}, this);
};
JS.Method.trace = function(classes) {
var i = classes.length;
while (i--) {
classes[i].__trace__ = true;
classes[i].resolve();
}
};
JS.Method.untrace = function(classes) {
var i = classes.length;
while (i--) {
classes[i].__trace__ = false;
classes[i].resolve();
}
};
JS.Module = JS.makeClass();
JS.Module.__queue__ = [];
JS.extend(JS.Module.prototype, {
initialize: function(name, methods, options) {
if (typeof name !== 'string') {
options = arguments[1];
methods = arguments[0];
name = undefined;
}
options = options || {};
this.__inc__ = [];
this.__dep__ = [];
this.__fns__ = {};
this.__tgt__ = options._target;
this.__anc__ = null;
this.__mct__ = {};
this.setName(name);
this.include(methods, {_resolve: false});
if (JS.Module.__queue__)
JS.Module.__queue__.push(this);
},
setName: function(name) {
this.displayName = name || '';
for (var field in this.__fns__)
this.__name__(field);
if (name && this.__meta__)
this.__meta__.setName(name + '.');
},
__name__: function(name) {
if (!this.displayName) return;
var object = this.__fns__[name];
if (!object) return;
name = this.displayName.replace(JS.END_WITHOUT_DOT, '$1#') + name;
if (typeof object.setName === 'function') return object.setName(name);
if (typeof object === 'function') object.displayName = name;
},
define: function(name, callable, options) {
var method = JS.Method.create(this, name, callable),
resolve = (options || {})._resolve;
this.__fns__[name] = method;
this.__name__(name);
if (resolve !== false) this.resolve();
},
include: function(module, options) {
if (!module) return this;
var options = options || {},
resolve = options._resolve !== false,
extend = module.extend,
include = module.include,
extended, field, value, mixins, i, n;
if (module.__fns__ && module.__inc__) {
this.__inc__.push(module);
if ((module.__dep__ || {}).push) module.__dep__.push(this);
if (extended = options._extended) {
if (typeof module.extended === 'function')
module.extended(extended);
}
else {
if (typeof module.included === 'function')
module.included(this);
}
}
else {
if (this.shouldIgnore('extend', extend)) {
mixins = [].concat(extend);
for (i = 0, n = mixins.length; i < n; i++)
this.extend(mixins[i]);
}
if (this.shouldIgnore('include', include)) {
mixins = [].concat(include);
for (i = 0, n = mixins.length; i < n; i++)
this.include(mixins[i], {_resolve: false});
}
for (field in module) {
if (!module.hasOwnProperty(field)) continue;
value = module[field];
if (this.shouldIgnore(field, value)) continue;
this.define(field, value, {_resolve: false});
}
if (module.hasOwnProperty('toString'))
this.define('toString', module.toString, {_resolve: false});
}
if (resolve) this.resolve();
return this;
},
alias: function(aliases) {
for (var method in aliases) {
if (!aliases.hasOwnProperty(method)) continue;
this.define(method, this.instanceMethod(aliases[method]), {_resolve: false});
}
this.resolve();
},
resolve: function(host) {
var host = host || this,
target = host.__tgt__,
inc = this.__inc__,
fns = this.__fns__,
i, n, key, compiled;
if (host === this) {
this.__anc__ = null;
this.__mct__ = {};
i = this.__dep__.length;
while (i--) this.__dep__[i].resolve();
}
if (!target) return;
for (i = 0, n = inc.length; i < n; i++)
inc[i].resolve(host);
for (key in fns) {
compiled = JS.Method.compile(fns[key], host);
if (target[key] !== compiled) target[key] = compiled;
}
if (fns.hasOwnProperty('toString'))
target.toString = JS.Method.compile(fns.toString, host);
},
shouldIgnore: function(field, value) {
return (field === 'extend' || field === 'include') &&
(typeof value !== 'function' ||
(value.__fns__ && value.__inc__));
},
ancestors: function(list) {
var cachable = !list,
list = list || [],
inc = this.__inc__;
if (cachable && this.__anc__) return this.__anc__.slice();
for (var i = 0, n = inc.length; i < n; i++)
inc[i].ancestors(list);
if (JS.indexOf(list, this) < 0)
list.push(this);
if (cachable) this.__anc__ = list.slice();
return list;
},
lookup: function(name) {
var cached = this.__mct__[name];
if (cached && cached.slice) return cached.slice();
var ancestors = this.ancestors(),
methods = [],
fns;
for (var i = 0, n = ancestors.length; i < n; i++) {
fns = ancestors[i].__fns__;
if (fns.hasOwnProperty(name)) methods.push(fns[name]);
}
this.__mct__[name] = methods.slice();
return methods;
},
includes: function(module) {
if (module === this) return true;
var inc = this.__inc__;
for (var i = 0, n = inc.length; i < n; i++) {
if (inc[i].includes(module))
return true;
}
return false;
},
instanceMethod: function(name) {
return this.lookup(name).pop();
},
instanceMethods: function(recursive, list) {
var methods = list || [],
fns = this.__fns__,
field;
for (field in fns) {
if (!JS.isType(this.__fns__[field], JS.Method)) continue;
if (JS.indexOf(methods, field) >= 0) continue;
methods.push(field);
}
if (recursive !== false) {
var ancestors = this.ancestors(), i = ancestors.length;
while (i--) ancestors[i].instanceMethods(false, methods);
}
return methods;
},
match: function(object) {
return object && object.isA && object.isA(this);
},
toString: function() {
return this.displayName;
}
});
JS.Kernel = new JS.Module('Kernel', {
__eigen__: function() {
if (this.__meta__) return this.__meta__;
var name = this.toString() + '.';
this.__meta__ = new JS.Module(name, null, {_target: this});
return this.__meta__.include(this.klass, {_resolve: false});
},
equals: function(other) {
return this === other;
},
extend: function(module, options) {
var resolve = (options || {})._resolve;
this.__eigen__().include(module, {_extended: this, _resolve: resolve});
return this;
},
hash: function() {
return JS.Kernel.hashFor(this);
},
isA: function(module) {
return (typeof module === 'function' && this instanceof module) ||
this.__eigen__().includes(module);
},
method: function(name) {
var cache = this.__mct__ = this.__mct__ || {},
value = cache[name],
field = this[name];
if (typeof field !== 'function') return field;
if (value && field === value._value) return value._bound;
var bound = JS.bind(field, this);
cache[name] = {_value: field, _bound: bound};
return bound;
},
methods: function() {
return this.__eigen__().instanceMethods();
},
tap: function(block, context) {
block.call(context, this);
return this;
},
toString: function() {
if (this.displayName) return this.displayName;
var name = this.klass.displayName || this.klass.toString();
return '#<' + name + ':' + this.hash() + '>';
}
});
(function() {
var id = 1;
JS.Kernel.hashFor = function(object) {
if (object.__hash__ !== undefined) return object.__hash__;
object.__hash__ = (new JS.Date().getTime() + id).toString(16);
id += 1;
return object.__hash__;
};
})();
JS.Class = JS.makeClass(JS.Module);
JS.extend(JS.Class.prototype, {
initialize: function(name, parent, methods, options) {
if (typeof name !== 'string') {
options = arguments[2];
methods = arguments[1];
parent = arguments[0];
name = undefined;
}
if (typeof parent !== 'function') {
options = methods;
methods = parent;
parent = Object;
}
JS.Module.prototype.initialize.call(this, name);
options = options || {};
var klass = JS.makeClass(parent);
JS.extend(klass, this);
klass.prototype.constructor =
klass.prototype.klass = klass;
klass.__eigen__().include(parent.__meta__, {_resolve: options._resolve});
klass.setName(name);
klass.__tgt__ = klass.prototype;
var parentModule = (parent === Object)
? {}
: (parent.__fns__ ? parent : new JS.Module(parent.prototype, {_resolve: false}));
klass.include(JS.Kernel, {_resolve: false})
.include(parentModule, {_resolve: false})
.include(methods, {_resolve: false});
if (options._resolve !== false) klass.resolve();
if (typeof parent.inherited === 'function')
parent.inherited(klass);
return klass;
}
});
(function() {
var methodsFromPrototype = function(klass) {
var methods = {},
proto = klass.prototype;
for (var field in proto) {
if (!proto.hasOwnProperty(field)) continue;
methods[field] = JS.Method.create(klass, field, proto[field]);
}
return methods;
};
var classify = function(name, parentName) {
var klass = JS[name],
parent = JS[parentName];
klass.__inc__ = [];
klass.__dep__ = [];
klass.__fns__ = methodsFromPrototype(klass);
klass.__tgt__ = klass.prototype;
klass.prototype.constructor =
klass.prototype.klass = klass;
JS.extend(klass, JS.Class.prototype);
klass.include(parent || JS.Kernel);
klass.setName(name);
klass.constructor = klass.klass = JS.Class;
};
classify('Method');
classify('Module');
classify('Class', 'Module');
var eigen = JS.Kernel.instanceMethod('__eigen__');
eigen.call(JS.Method).resolve();
eigen.call(JS.Module).resolve();
eigen.call(JS.Class).include(JS.Module.__meta__);
})();
JS.NotImplementedError = new JS.Class('NotImplementedError', Error);
JS.Method.keyword('callSuper', function(method, env, receiver, args) {
var methods = env.lookup(method.name),
stackIndex = methods.length - 1,
params = JS.array(args);
if (stackIndex === 0) return undefined;
var _super = function() {
var i = arguments.length;
while (i--) params[i] = arguments[i];
stackIndex -= 1;
if (stackIndex === 0) delete receiver.callSuper;
var returnValue = methods[stackIndex].apply(receiver, params);
receiver.callSuper = _super;
stackIndex += 1;
return returnValue;
};
return _super;
});
JS.Method.keyword('blockGiven', function(method, env, receiver, args) {
var block = Array.prototype.slice.call(args, method.arity),
hasBlock = (typeof block[0] === 'function');
return function() { return hasBlock };
});
JS.Method.keyword('yieldWith', function(method, env, receiver, args) {
var block = Array.prototype.slice.call(args, method.arity);
return function() {
if (typeof block[0] !== 'function') return;
return block[0].apply(block[1] || null, arguments);
};
});
JS.Interface = new JS.Class('Interface', {
initialize: function(methods) {
this.test = function(object, returnName) {
var n = methods.length;
while (n--) {
if (typeof object[methods[n]] !== 'function')
return returnName ? methods[n] : false;
}
return true;
};
},
extend: {
ensure: function() {
var args = JS.array(arguments), object = args.shift(), face, result;
while (face = args.shift()) {
result = face.test(object, true);
if (result !== true) throw new Error('object does not implement ' + result + '()');
}
}
}
});
JS.Singleton = new JS.Class('Singleton', {
initialize: function(name, parent, methods) {
return new (new JS.Class(name, parent, methods));
}
});
JS.extend(exports, JS);
if (global.JS) JS.extend(global.JS, JS);
});
(function(factory) {
var E = (typeof exports === 'object'),
js = (typeof JS === 'undefined') ? require('./core') : JS;
if (E) exports.JS = exports;
factory(js, E ? exports : js);
})(function(JS, exports) {
'use strict';
var Enumerable = new JS.Module('Enumerable', {
extend: {
ALL_EQUAL: {},
forEach: function(block, context) {
if (!block) return new Enumerator(this, 'forEach');
for (var i = 0; i < this.length; i++)
block.call(context, this[i]);
return this;
},
isComparable: function(list) {
return list.all(function(item) { return typeof item.compareTo === 'function' });
},
areEqual: function(expected, actual) {
var result;
if (expected === actual)
return true;
if (expected && typeof expected.equals === 'function')
return expected.equals(actual);
if (expected instanceof Function)
return expected === actual;
if (expected instanceof Array) {
if (!(actual instanceof Array)) return false;
for (var i = 0, n = expected.length; i < n; i++) {
result = this.areEqual(expected[i], actual[i]);
if (result === this.ALL_EQUAL) return true;
if (!result) return false;
}
if (expected.length !== actual.length) return false;
return true;
}
if (expected instanceof Date) {
if (!(actual instanceof Date)) return false;
if (expected.getTime() !== actual.getTime()) return false;
return true;
}
if (expected instanceof Object) {
if (!(actual instanceof Object)) return false;
if (this.objectSize(expected) !== this.objectSize(actual)) return false;
for (var key in expected) {
if (!this.areEqual(expected[key], actual[key]))
return false;
}
return true;
}
return false;
},
objectKeys: function(object, includeProto) {
var keys = [];
for (var key in object) {
if (object.hasOwnProperty(key) || includeProto !== false)
keys.push(key);
}
return keys;
},
objectSize: function(object) {
return this.objectKeys(object).length;
},
Collection: new JS.Class({
initialize: function(array) {
this.length = 0;
if (array) Enumerable.forEach.call(array, this.push, this);
},
push: function(item) {
Array.prototype.push.call(this, item);
},
clear: function() {
var i = this.length;
while (i--) delete this[i];
this.length = 0;
}
})
},
all: function(block, context) {
block = Enumerable.toFn(block);
var truth = true;
this.forEach(function(item) {
truth = truth && (block ? block.apply(context, arguments) : item);
});
return !!truth;
},
any: function(block, context) {
block = Enumerable.toFn(block);
var truth = false;
this.forEach(function(item) {
truth = truth || (block ? block.apply(context, arguments) : item);
});
return !!truth;
},
chunk: function(block, context) {
if (!block) return this.enumFor('chunk');
var result = [],
value = null,
started = false;
this.forEach(function(item) {
var v = block.apply(context, arguments);
if (started) {
if (Enumerable.areEqual(value, v))
result[result.length - 1][1].push(item);
else
result.push([v, [item]]);
} else {
result.push([v, [item]]);
started = true;
}
value = v;
});
return result;
},
count: function(block, context) {
if (typeof this.size === 'function') return this.size();
var count = 0, object = block;
if (block && typeof block !== 'function')
block = function(x) { return Enumerable.areEqual(x, object) };
this.forEach(function() {
if (!block || block.apply(context, arguments))
count += 1;
});
return count;
},
cycle: function(n, block, context) {
if (!block) return this.enumFor('cycle', n);
block = Enumerable.toFn(block);
while (n--) this.forEach(block, context);
},
drop: function(n) {
var entries = [];
this.forEachWithIndex(function(item, i) {
if (i >= n) entries.push(item);
});
return entries;
},
dropWhile: function(block, context) {
if (!block) return this.enumFor('dropWhile');
block = Enumerable.toFn(block);
var entries = [],
drop = true;
this.forEach(function(item) {
if (drop) drop = drop && block.apply(context, arguments);
if (!drop) entries.push(item);
});
return entries;
},
forEachCons: function(n, block, context) {
if (!block) return this.enumFor('forEachCons', n);
block = Enumerable.toFn(block);
var entries = this.toArray(),
size = entries.length,
limit = size - n,
i;
for (i = 0; i <= limit; i++)
block.call(context, entries.slice(i, i+n));
return this;
},
forEachSlice: function(n, block, context) {
if (!block) return this.enumFor('forEachSlice', n);
block = Enumerable.toFn(block);
var entries = this.toArray(),
size = entries.length,
m = Math.ceil(size/n),
i;
for (i = 0; i < m; i++)
block.call(context, entries.slice(i*n, (i+1)*n));
return this;
},
forEachWithIndex: function(offset, block, context) {
if (typeof offset === 'function') {
context = block;
block = offset;
offset = 0;
}
offset = offset || 0;
if (!block) return this.enumFor('forEachWithIndex', offset);
block = Enumerable.toFn(block);
return this.forEach(function(item) {
var result = block.call(context, item, offset);
offset += 1;
return result;
});
},
forEachWithObject: function(object, block, context) {
if (!block) return this.enumFor('forEachWithObject', object);
block = Enumerable.toFn(block);
this.forEach(function() {
var args = [object].concat(JS.array(arguments));
block.apply(context, args);
});
return object;
},
find: function(block, context) {
if (!block) return this.enumFor('find');
block = Enumerable.toFn(block);
var needle = {}, K = needle;
this.forEach(function(item) {
if (needle !== K) return;
needle = block.apply(context, arguments) ? item : needle;
});
return needle === K ? null : needle;
},
findIndex: function(needle, context) {
if (needle === undefined) return this.enumFor('findIndex');
var index = null,
block = (typeof needle === 'function');
this.forEachWithIndex(function(item, i) {
if (index !== null) return;
if (Enumerable.areEqual(needle, item) || (block && needle.apply(context, arguments)))
index = i;
});
return index;
},
first: function(n) {
var entries = this.toArray();
return (n === undefined) ? entries[0] : entries.slice(0,n);
},
grep: function(pattern, block, context) {
block = Enumerable.toFn(block);
var results = [];
this.forEach(function(item) {
var match = (typeof pattern.match === 'function') ? pattern.match(item)
: (typeof pattern.test === 'function') ? pattern.test(item)
: JS.isType(item, pattern);
if (!match) return;
if (block) item = block.apply(context, arguments);
results.push(item);
});
return results;
},
groupBy: function(block, context) {
if (!block) return this.enumFor('groupBy');
block = Enumerable.toFn(block);
var Hash = ((typeof require === 'function') ? require('./hash') : JS).Hash,
hash = new Hash();
this.forEach(function(item) {
var value = block.apply(context, arguments);
if (!hash.hasKey(value)) hash.store(value, []);
hash.get(value).push(item);
});
return hash;
},
inject: function(memo, block, context) {
var args = JS.array(arguments),
counter = 0,
K = {};
switch (args.length) {
case 1: memo = K;
block = args[0];
break;
case 2: if (typeof memo === 'function') {
memo = K;
block = args[0];
context = args[1];
}
}
block = Enumerable.toFn(block);
this.forEach(function(item) {
if (!counter++ && memo === K) return memo = item;
var args = [memo].concat(JS.array(arguments));
memo = block.apply(context, args);
});
return memo;
},
map: function(block, context) {
if (!block) return this.enumFor('map');
block = Enumerable.toFn(block);
var map = [];
this.forEach(function() {
map.push(block.apply(context, arguments));
});
return map;
},
max: function(block, context) {
return this.minmax(block, context)[1];
},
maxBy: function(block, context) {
if (!block) return this.enumFor('maxBy');
return this.minmaxBy(block, context)[1];
},
member: function(needle) {
return this.any(function(item) { return Enumerable.areEqual(item, needle) });
},
min: function(block, context) {
return this.minmax(block, context)[0];
},
minBy: function(block, context) {
if (!block) return this.enumFor('minBy');
return this.minmaxBy(block, context)[0];
},
minmax: function(block, context) {
var list = this.sort(block, context);
return [list[0], list[list.length - 1]];
},
minmaxBy: function(block, context) {
if (!block) return this.enumFor('minmaxBy');
var list = this.sortBy(block, context);
return [list[0], list[list.length - 1]];
},
none: function(block, context) {
return !this.any(block, context);
},
one: function(block, context) {
block = Enumerable.toFn(block);
var count = 0;
this.forEach(function(item) {
if (block ? block.apply(context, arguments) : item) count += 1;
});
return count === 1;
},
partition: function(block, context) {
if (!block) return this.enumFor('partition');
block = Enumerable.toFn(block);
var ayes = [], noes = [];
this.forEach(function(item) {
(block.apply(context, arguments) ? ayes : noes).push(item);
});
return [ayes, noes];
},
reject: function(block, context) {
if (!block) return this.enumFor('reject');
block = Enumerable.toFn(block);
var map = [];
this.forEach(function(item) {
if (!block.apply(context, arguments)) map.push(item);
});
return map;
},
reverseForEach: function(block, context) {
if (!block) return this.enumFor('reverseForEach');
block = Enumerable.toFn(block);
var entries = this.toArray(),
n = entries.length;
while (n--) block.call(context, entries[n]);
return this;
},
select: function(block, context) {
if (!block) return this.enumFor('select');
block = Enumerable.toFn(block);
var map = [];
this.forEach(function(item) {
if (block.apply(context, arguments)) map.push(item);
});
return map;
},
sort: function(block, context) {
var comparable = Enumerable.isComparable(this),
entries = this.toArray();
block = block || (comparable
? function(a,b) { return a.compareTo(b); }
: null);
return block
? entries.sort(function(a,b) { return block.call(context, a, b); })
: entries.sort();
},
sortBy: function(block, context) {
if (!block) return this.enumFor('sortBy');
block = Enumerable.toFn(block);
var util = Enumerable,
map = new util.Collection(this.map(block, context)),
comparable = util.isComparable(map);
return new util.Collection(map.zip(this).sort(function(a, b) {
a = a[0]; b = b[0];
return comparable ? a.compareTo(b) : (a < b ? -1 : (a > b ? 1 : 0));
})).map(function(item) { return item[1]; });
},
take: function(n) {
var entries = [];
this.forEachWithIndex(function(item, i) {
if (i < n) entries.push(item);
});
return entries;
},
takeWhile: function(block, context) {
if (!block) return this.enumFor('takeWhile');
block = Enumerable.toFn(block);
var entries = [],
take = true;
this.forEach(function(item) {
if (take) take = take && block.apply(context, arguments);
if (take) entries.push(item);
});
return entries;
},
toArray: function() {
return this.drop(0);
},
zip: function() {
var util = Enumerable,
args = [],
counter = 0,
n = arguments.length,
block, context;
if (typeof arguments[n-1] === 'function') {
block = arguments[n-1]; context = {};
}
if (typeof arguments[n-2] === 'function') {
block = arguments[n-2]; context = arguments[n-1];
}
util.forEach.call(arguments, function(arg) {
if (arg === block || arg === context) return;
if (arg.toArray) arg = arg.toArray();
if (JS.isType(arg, Array)) args.push(arg);
});
var results = this.map(function(item) {