UNPKG

jstest

Version:

The cross-platform JavaScript test framework

1,924 lines (1,535 loc) 222 kB
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) {