Commit 57e70d6d authored by Brian Cavalier's avatar Brian Cavalier

Update to latest cujoJS releases: wire 0.10.0, curl 0.7.4, when 2.1.1

parent db9cb67d
...@@ -174,13 +174,8 @@ define({ ...@@ -174,13 +174,8 @@ define({
} }
}, },
plugins: [ plugins: [ //'wire/debug',
// { module: 'wire/debug', trace: true }, 'wire/dom', 'wire/dom/render', 'wire/on',
{ module: 'wire/dom' }, 'wire/aop', 'wire/connect', 'cola'
{ module: 'wire/dom/render' },
{ module: 'wire/on' },
{ module: 'wire/aop' },
{ module: 'wire/connect' },
{ module: 'cola' }
] ]
}); });
/*global curl */ // Bootstrap the app. Notice that curl is not a global, only define.
(function (curl) { /*global define*/
define(['curl'], function (curl) {
'use strict'; 'use strict';
curl({ curl({
...@@ -12,10 +13,9 @@ ...@@ -12,10 +13,9 @@
{ name: 'cola', location: 'bower_components/cola', main: 'cola' }, { name: 'cola', location: 'bower_components/cola', main: 'cola' },
{ name: 'poly', location: 'bower_components/poly', main: 'poly' } { name: 'poly', location: 'bower_components/poly', main: 'poly' }
], ],
preloads: ['poly/string', 'poly/array'], preloads: ['poly/es5'],
// Turn off i18n locale sniffing. Change or remove this line if you want to // Turn off i18n locale sniffing. Change or remove this line if you want to
// test specific locales or try automatic locale-sniffing. // test specific locales or try automatic locale-sniffing.
locale: false locale: false
}); });
});
})(curl);
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
"name": "todomvc-cujoJS", "name": "todomvc-cujoJS",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"todomvc-common": "~0.1.4", "todomvc-common": "~0.1.7",
"curl": "~0.7.3", "curl": "~0.7.4",
"cola": "latest", "cola": "latest",
"poly": "~0.5.1", "poly": "~0.5.1",
"when": "~2.1.0", "when": "~2.1.1",
"wire": "~0.9.4", "wire": "~0.10.0",
"meld": "~1.3.0" "meld": "~1.3.0"
} }
} }
[submodule "test/curl"]
path = test/curl
url = https://unscriptable@github.com/cujojs/curl.git
[submodule "test/util"]
path = test/util
url = https://unscriptable@github.com/cujojs/util.git
[submodule "support/when"]
path = support/when
url = https://github.com/cujojs/when.git
{
"name": "cola",
"version": "0.0.0",
"commit": "27b8c7e8fe88feef62a746d29d30af945dcb244d",
"repository": {
"type": "git",
"url": "git://github.com/cujojs/cola"
}
}
\ No newline at end of file
{
"name": "curl",
"version": "0.7.3",
"repository": {
"type": "git",
"url": "git://github.com/cujojs/curl"
}
}
\ No newline at end of file
{
"name": "curl",
"version": "0.7.3",
"description": "A small, fast module and resource loader with dependency management. (AMD, CommonJS Modules/1.1, CSS, HTML, etc.)",
"keywords": ["curl", "cujo", "amd", "loader", "module"],
"licenses": [
{
"type": "MIT",
"url": "http://www.opensource.org/licenses/mit-license.php"
}
],
"repositories": [
{
"type": "git",
"url": "https://github.com/cujojs/curl"
}
],
"bugs": "https://github.com/cujojs/curl/issues",
"maintainers": [
{
"name": "John Hann",
"web": "http://unscriptable.com"
}
],
"contributors": [
{
"name": "John Hann",
"web": "http://unscriptable.com"
},
{
"name": "Brian Cavalier",
"web": "http://hovercraftstudios.com"
}
],
"main": "./src/curl",
"directories": {
"test": "test"
}
}
/** @license MIT License (c) copyright B Cavalier & J Hann */ /** @license MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
/** /**
* curl (cujo resource loader) * curl (cujo resource loader)
...@@ -13,9 +13,10 @@ ...@@ -13,9 +13,10 @@
(function (global) { (function (global) {
//"use strict"; don't restore this until the config routine is refactored //"use strict"; don't restore this until the config routine is refactored
var var
version = '0.7.3', version = '0.7.4',
curlName = 'curl', curlName = 'curl',
defineName = 'define', defineName = 'define',
runModuleAttr = 'data-curl-run',
userCfg, userCfg,
prevCurl, prevCurl,
prevDefine, prevDefine,
...@@ -49,8 +50,9 @@ ...@@ -49,8 +50,9 @@
dontAddExtRx = /\?|\.js\b/, dontAddExtRx = /\?|\.js\b/,
absUrlRx = /^\/|^[^:]+:\/\//, absUrlRx = /^\/|^[^:]+:\/\//,
findDotsRx = /(\.)(\.?)(?:$|\/([^\.\/]+.*)?)/g, findDotsRx = /(\.)(\.?)(?:$|\/([^\.\/]+.*)?)/g,
removeCommentsRx = /\/\*[\s\S]*?\*\/|(?:[^\\])\/\/.*?[\n\r]/g, removeCommentsRx = /\/\*[\s\S]*?\*\/|\/\/.*?[\n\r]/g,
findRValueRequiresRx = /require\s*\(\s*["']([^"']+)["']\s*\)|(?:[^\\]?)(["'])/g, findRValueRequiresRx = /require\s*\(\s*(["'])(.*?[^\\])\1\s*\)|[^\\]?(["'])/g,
splitMainDirectives = /\s*,\s*/,
cjsGetters, cjsGetters,
core; core;
...@@ -202,7 +204,7 @@ ...@@ -202,7 +204,7 @@
} }
function isPromise (o) { function isPromise (o) {
return o instanceof Promise; return o instanceof Promise || o instanceof CurlApi;
} }
function when (promiseOrValue, callback, errback, progback) { function when (promiseOrValue, callback, errback, progback) {
...@@ -637,18 +639,6 @@ ...@@ -637,18 +639,6 @@
}, },
checkPreloads: function (cfg) {
var preloads;
preloads = cfg && cfg['preloads'];
if (preloads && preloads.length > 0) {
// chain from previous preload, if any.
when(preload, function () {
preload = core.getDeps(core.createContext(userCfg, undef, preloads, true));
});
}
},
resolvePathInfo: function (absId, cfg) { resolvePathInfo: function (absId, cfg) {
// searches through the configured path mappings and packages // searches through the configured path mappings and packages
var pathMap, pathInfo, path, pkgCfg; var pathMap, pathInfo, path, pkgCfg;
...@@ -744,8 +734,8 @@ ...@@ -744,8 +734,8 @@
defFunc : defFunc :
defFunc.toSource ? defFunc.toSource() : defFunc.toString(); defFunc.toSource ? defFunc.toSource() : defFunc.toString();
// remove comments, then look for require() or quotes // remove comments, then look for require() or quotes
source.replace(removeCommentsRx, '').replace(findRValueRequiresRx, function (m, id, qq) { source.replace(removeCommentsRx, '').replace(findRValueRequiresRx, function (m, rq, id, qq) {
// if we encounter a quote // if we encounter a string in the source, don't look for require()
if (qq) { if (qq) {
currQuote = currQuote == qq ? undef : currQuote; currQuote = currQuote == qq ? undef : currQuote;
} }
...@@ -971,35 +961,44 @@ ...@@ -971,35 +961,44 @@
}, },
fetchDep: function (depName, parentDef) { fetchDep: function (depName, parentDef) {
var toAbsId, isPreload, cfg, parts, mainId, loaderId, pluginId, var toAbsId, isPreload, cfg, parts, absId, mainId, loaderId, pluginId,
resId, pathInfo, def, tempDef, resCfg; resId, pathInfo, def, tempDef, resCfg;
toAbsId = parentDef.toAbsId; toAbsId = parentDef.toAbsId;
isPreload = parentDef.isPreload; isPreload = parentDef.isPreload;
cfg = parentDef.config || userCfg; // is this fallback necessary? cfg = parentDef.config || userCfg; // is this fallback necessary?
// check for plugin loaderId absId = toAbsId(depName);
// TODO: this runs pluginParts() twice. how to run it just once?
parts = pluginParts(toAbsId(depName));
resId = parts.resourceId;
// get id of first resource to load (which could be a plugin)
mainId = parts.pluginId || resId;
pathInfo = core.resolvePathInfo(mainId, cfg);
// get custom module loader from package config if not a plugin if (absId in cache) {
if (parts.pluginId) { // module already exists in cache
loaderId = mainId; mainId = absId;
} }
else { else {
// TODO: move config.moduleLoader to config.transform // check for plugin loaderId
loaderId = pathInfo.config['moduleLoader'] || pathInfo.config.moduleLoader; parts = pluginParts(absId);
if (loaderId) { resId = parts.resourceId;
// TODO: allow transforms to have relative module ids? // get id of first resource to load (which could be a plugin)
// (we could do this by returning package location from mainId = parts.pluginId || resId;
// resolvePathInfo. why not return all package info?) pathInfo = core.resolvePathInfo(mainId, cfg);
resId = mainId; }
mainId = loaderId;
pathInfo = core.resolvePathInfo(loaderId, cfg); // get custom module loader from package config if not a plugin
if (parts) {
if (parts.pluginId) {
loaderId = mainId;
}
else {
// TODO: move config.moduleLoader to config.transform
loaderId = pathInfo.config['moduleLoader'] || pathInfo.config.moduleLoader;
if (loaderId) {
// TODO: allow transforms to have relative module ids?
// (we could do this by returning package location from
// resolvePathInfo. why not return all package info?)
resId = mainId;
mainId = loaderId;
pathInfo = core.resolvePathInfo(loaderId, cfg);
}
} }
} }
...@@ -1070,8 +1069,8 @@ ...@@ -1070,8 +1069,8 @@
// but to be compatible with AMD spec, we have to // but to be compatible with AMD spec, we have to
// piggy-back on the callback function parameter: // piggy-back on the callback function parameter:
var loaded = function (res) { var loaded = function (res) {
normalizedDef.resolve(res);
if (!dynamic) cache[fullId] = res; if (!dynamic) cache[fullId] = res;
normalizedDef.resolve(res);
}; };
loaded['resolve'] = loaded; loaded['resolve'] = loaded;
loaded['reject'] = loaded['error'] = normalizedDef.reject; loaded['reject'] = loaded['error'] = normalizedDef.reject;
...@@ -1110,6 +1109,34 @@ ...@@ -1110,6 +1109,34 @@
} }
} }
return def; return def;
},
findScript: function (predicate) {
var i = 0, script;
while (doc && (script = doc.scripts[i++])) {
if (predicate(script)) return script;
}
},
extractDataAttrConfig: function (cfg) {
var script;
script = core.findScript(function (script) {
var main;
// find main module(s) in data-curl-run attr on script element
// TODO: extract baseUrl, too?
main = script.getAttribute(runModuleAttr);
if (main) cfg.main = main;
return main;
});
// removeAttribute is wonky (in IE6?) but this works
if (script) {
script.setAttribute(runModuleAttr, '');
}
return cfg;
},
nextTurn: function (task) {
setTimeout(task, 0);
} }
}; };
...@@ -1118,42 +1145,57 @@ ...@@ -1118,42 +1145,57 @@
cjsGetters = {'require': core.getCjsRequire, 'exports': core.getCjsExports, 'module': core.getCjsModule}; cjsGetters = {'require': core.getCjsRequire, 'exports': core.getCjsExports, 'module': core.getCjsModule};
function _curl (/* various */) { function _curl (/* various */) {
var args, promise, cfg;
var args = [].slice.call(arguments), cfg; args = [].slice.call(arguments);
// extract config, if it's specified // extract config, if it's specified
if (isType(args[0], 'Object')) { if (isType(args[0], 'Object')) {
cfg = args.shift(); cfg = args.shift();
_config(cfg); promise = _config(cfg);
} }
return new CurlApi(args[0], args[1], args[2]); return new CurlApi(args[0], args[1], args[2], promise);
} }
function _config (cfg) { function _config (cfg, callback, errback) {
var pPromise, mPromise, main, devmain, fallback;
if (cfg) { if (cfg) {
core.setApi(cfg); core.setApi(cfg);
userCfg = core.config(cfg); userCfg = core.config(cfg);
// check for preloads // check for preloads
core.checkPreloads(cfg); if ('preloads' in cfg) {
// check for main module(s) pPromise = new CurlApi(cfg['preloads'], undef, errback, preload, true);
if ('main' in cfg) { // yes, this is hacky and embarrassing. now that we've got that
// start in next turn to wait for other modules in current file // settled... until curl has deferred factory execution, this
setTimeout(function () { // is the only way to stop preloads from dead-locking when
var ctx; // they have dependencies inside a bundle.
ctx = core.createContext(userCfg, undef, [].concat(cfg['main'])); core.nextTurn(function () { preload = pPromise; });
core.getDeps(ctx); }
}, 0); // check for main module(s). all modules wait for preloads implicitly.
main = cfg['main'];
main = main && String(main).split(splitMainDirectives);
if (main) {
mPromise = new Promise();
mPromise.then(callback, errback);
// figure out if we are using a dev-time fallback
fallback = main[1]
? function () { new CurlApi(main[1], mPromise.resolve, mPromise.reject); }
: mPromise.reject;
new CurlApi(main[0], mPromise.resolve, fallback);
return mPromise;
} }
} }
} }
// thanks to Joop Ringelberg for helping troubleshoot the API // thanks to Joop Ringelberg for helping troubleshoot the API
function CurlApi (ids, callback, errback, waitFor) { function CurlApi (ids, callback, errback, waitFor, isPreload) {
var then, ctx; var then, ctx;
ctx = core.createContext(userCfg, undef, [].concat(ids));
this['then'] = then = function (resolved, rejected) { ctx = core.createContext(userCfg, undef, [].concat(ids), isPreload);
this['then'] = this.then = then = function (resolved, rejected) {
when(ctx, when(ctx,
// return the dependencies as arguments, not an array // return the dependencies as arguments, not an array
function (deps) { function (deps) {
...@@ -1166,13 +1208,22 @@ ...@@ -1166,13 +1208,22 @@
); );
return this; return this;
}; };
this['next'] = function (ids, cb, eb) { this['next'] = function (ids, cb, eb) {
// chain api // chain api
return new CurlApi(ids, cb, eb, ctx); return new CurlApi(ids, cb, eb, ctx);
}; };
this['config'] = _config; this['config'] = _config;
if (callback || errback) then(callback, errback); if (callback || errback) then(callback, errback);
when(waitFor, function () { core.getDeps(ctx); });
// ensure next-turn so inline code can execute first
core.nextTurn(function () {
when(isPreload || preload, function () {
when(waitFor, function () { core.getDeps(ctx); }, errback);
});
});
} }
_curl['version'] = version; _curl['version'] = version;
...@@ -1232,18 +1283,23 @@ ...@@ -1232,18 +1283,23 @@
pathRx: /$^/ pathRx: /$^/
}; };
// look for "data-curl-run" directive, and override config
userCfg = core.extractDataAttrConfig(userCfg);
// handle pre-existing global // handle pre-existing global
prevCurl = global[curlName]; prevCurl = global[curlName];
prevDefine = global[defineName]; prevDefine = global[defineName];
if (!prevCurl || isType(prevCurl, 'Function')) {
// set default api // only run config if there is something to config (perf saver?)
core.setApi(); if (prevCurl && isType(prevCurl, 'Object') || userCfg.main) {
}
else {
// remove global curl object // remove global curl object
global[curlName] = undef; // can't use delete in IE 6-8 global[curlName] = undef; // can't use delete in IE 6-8
// configure curl // configure curl
_config(prevCurl); _config(prevCurl || userCfg);
}
else {
// set default api
core.setApi();
} }
// allow curl to be a dependency // allow curl to be a dependency
......
/** MIT License (c) copyright B Cavalier & J Hann */ /** MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
/** /**
* curl debug plugin * curl debug plugin
......
/** MIT License (c) copyright B Cavalier & J Hann */ /** MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
/** /**
* curl domReady * curl domReady
......
/** MIT License (c) copyright B Cavalier & J Hann */ /** MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
/** /**
* curl CommonJS Modules/1.1 loader * curl CommonJS Modules/1.1 loader
...@@ -16,52 +16,13 @@ define(/*=='curl/loader/cjsm11',==*/ function () { ...@@ -16,52 +16,13 @@ define(/*=='curl/loader/cjsm11',==*/ function () {
var head, insertBeforeEl /*, findRequiresRx, myId*/; var head, insertBeforeEl /*, findRequiresRx, myId*/;
// findRequiresRx = /require\s*\(\s*['"](\w+)['"]\s*\)/,
// function nextId (index) {
// var varname = '', part;
// do {
// part = index % 26;
// varname += String.fromCharCode(part + 65);
// index -= part;
// }
// while (index > 0);
// return 'curl$' + varname;
// }
// /**
// * @description Finds the require() instances in the source text of a cjs
// * module and collects them. If removeRequires is true, it also replaces
// * them with a unique variable name. All unique require()'d module ids
// * are assigned a unique variable name to be used in the define(deps)
// * that will be constructed to wrap the cjs module.
// * @param source - source code of cjs module
// * @param moduleIds - hashMap (object) to receive pairs of moduleId /
// * unique variable name
// * @param removeRequires - if truthy, replaces all require() instances with
// * a unique variable
// * @return - source code of cjs module, possibly with require()s replaced
// */
// function parseDepModuleIds (source, moduleIds, removeRequires) {
// var index = 0;
// // fast parse
// source = source.replace(findRequiresRx, function (match, id) {
// if (!moduleIds[id]) {
// moduleIds[id] = nextId(index++);
// moduleIds.push(id);
// }
// return removeRequires ? moduleIds[id] : match;
// });
// return source;
// }
head = document && (document['head'] || document.getElementsByTagName('head')[0]); head = document && (document['head'] || document.getElementsByTagName('head')[0]);
// to keep IE from crying, we need to put scripts before any // to keep IE from crying, we need to put scripts before any
// <base> elements, but after any <meta>. this should do it: // <base> elements, but after any <meta>. this should do it:
insertBeforeEl = head && head.getElementsByTagName('base')[0] || null; insertBeforeEl = head && head.getElementsByTagName('base')[0] || null;
function wrapSource (source, resourceId, fullUrl) { function wrapSource (source, resourceId, fullUrl) {
var sourceUrl = fullUrl ? '////@ sourceURL=' + fullUrl.replace(/\s/g, '%20') + '.js' : ''; var sourceUrl = fullUrl ? '/*\n////@ sourceURL=' + fullUrl.replace(/\s/g, '%20') + '.js\n*/' : '';
return "define('" + resourceId + "'," + return "define('" + resourceId + "'," +
"['require','exports','module'],function(require,exports,module){" + "['require','exports','module'],function(require,exports,module){" +
source + "\n});\n" + sourceUrl + "\n"; source + "\n});\n" + sourceUrl + "\n";
...@@ -82,39 +43,41 @@ define(/*=='curl/loader/cjsm11',==*/ function () { ...@@ -82,39 +43,41 @@ define(/*=='curl/loader/cjsm11',==*/ function () {
head.insertBefore(el, insertBeforeEl); head.insertBefore(el, insertBeforeEl);
} }
return { wrapSource['load'] = function (resourceId, require, callback, config) {
'load': function (resourceId, require, callback, config) { // TODO: extract xhr from text! plugin and use that instead (after we upgrade to cram.js)
// TODO: extract xhr from text! plugin and use that instead (after we upgrade to cram.js) require(['text!' + resourceId + '.js', 'curl/_privileged'], function (source, priv) {
require(['text!' + resourceId + '.js', 'curl/_privileged'], function (source, priv) { var moduleMap;
var moduleMap;
// find (and replace?) dependencies // find (and replace?) dependencies
moduleMap = priv['core'].extractCjsDeps(source); moduleMap = priv['core'].extractCjsDeps(source);
//source = parseDepModuleIds(source, moduleMap, config.replaceRequires); //source = parseDepModuleIds(source, moduleMap, config.replaceRequires);
// get deps // get deps
require(moduleMap, function () { require(moduleMap, function () {
// wrap source in a define // wrap source in a define
source = wrapSource(source, resourceId, config['injectSourceUrl'] !== false && require.toUrl(resourceId)); source = wrapSource(source, resourceId, config['injectSourceUrl'] !== false && require.toUrl(resourceId));
if (config['injectScript']) { if (config['injectScript']) {
injectScript(source); injectScript(source);
} }
else { else {
//eval(source); //eval(source);
globalEval(source); globalEval(source);
} }
// call callback now that the module is defined // call callback now that the module is defined
callback(require(resourceId)); callback(require(resourceId));
}, callback['error'] || function (ex) { throw ex; }); }, callback['error'] || function (ex) { throw ex; });
}); });
}
}; };
wrapSource['cramPlugin'] = '../cram/cjsm11';
return wrapSource;
}); });
}(this, this.document, function () { /* FB needs direct eval here */ eval(arguments[0]); })); }(this, this.document, function () { /* FB needs direct eval here */ eval(arguments[0]); }));
define([], function () {
var xhr, progIds;
progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
xhr = function () {
if (typeof XMLHttpRequest !== "undefined") {
// rewrite the getXhr method to always return the native implementation
xhr = function () {
return new XMLHttpRequest();
};
}
else {
// keep trying progIds until we find the correct one, then rewrite the getXhr method
// to always return that one.
var noXhr = xhr = function () {
throw new Error("getXhr(): XMLHttpRequest not available");
};
while (progIds.length > 0 && xhr === noXhr) (function (id) {
try {
new ActiveXObject(id);
xhr = function () {
return new ActiveXObject(id);
};
}
catch (ex) {
}
}(progIds.shift()));
}
return xhr();
};
function fetchText (url, callback, errback) {
var x = xhr();
x.open('GET', url, true);
x.onreadystatechange = function (e) {
if (x.readyState === 4) {
if (x.status < 400) {
callback(x.responseText);
}
else {
errback(new Error('fetchText() failed. status: ' + x.statusText));
}
}
};
x.send(null);
}
return fetchText;
});
/** MIT License (c) copyright B Cavalier & J Hann */ /** MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
/** /**
* curl async! plugin * curl async! plugin
......
/** MIT License (c) copyright B Cavalier & J Hann */
/**
* curl css! plugin build-time module
*
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*/
define(function () {
"use strict";
// collection of modules that have been written to the built file
var built = {};
function nameWithExt (name, defaultExt) {
return name.lastIndexOf('.') <= name.lastIndexOf('/') ?
name + '.' + defaultExt : name;
}
function jsEncode (text) {
// TODO: hoist the map and regex to the enclosing scope for better performance
var map = { 34: '\\"', 13: '\\r', 12: '\\f', 10: '\\n', 9: '\\t', 8: '\\b' };
return text.replace(/(["\n\f\t\r\b])/g, function (c) {
return map[c.charCodeAt(0)];
});
}
function parseSuffixes (name) {
// creates a dual-structure: both an array and a hashmap
// suffixes[0] is the actual name
var parts = name.split('!'),
suf, i = 1, pair;
while ((suf = parts[i++])) { // double-parens to avoid jslint griping
pair = suf.split('=', 2);
parts[pair[0]] = pair.length == 2 ? pair[1] : true;
}
return parts;
}
var
// this actually tests for absolute urls and root-relative urls
// they're both non-relative
nonRelUrlRe = /^\/|^[^:]*:\/\//,
// Note: this will fail if there are parentheses in the url
findUrlRx = /url\s*\(['"]?([^'"\)]*)['"]?\)/g;
function translateUrls (cssText, baseUrl) {
return cssText.replace(findUrlRx, function (all, url) {
return 'url("' + translateUrl(url, baseUrl) + '")';
});
}
function translateUrl (url, parentPath) {
// if this is a relative url
if (!nonRelUrlRe.test(url)) {
// append path onto it
url = parentPath + url;
}
return url;
}
function createSheetProxy (sheet) {
return {
cssRules: function () {
return sheet.cssRules || sheet.rules;
},
insertRule: sheet.insertRule || function (text, index) {
var parts = text.split(/\{|\}/g);
sheet.addRule(parts[0], parts[1], index);
return index;
},
deleteRule: sheet.deleteRule || function (index) {
sheet.removeRule(index);
return index;
},
sheet: function () {
return sheet;
}
};
}
/***** style element functions *****/
var currentStyle;
function createStyle (cssText) {
clearTimeout(createStyle.debouncer);
if (createStyle.accum) {
createStyle.accum.push(cssText);
}
else {
createStyle.accum = [cssText];
currentStyle = doc.createStyleSheet ? doc.createStyleSheet() :
head.appendChild(doc.createElement('style'));
}
createStyle.debouncer = setTimeout(function () {
// Note: IE 6-8 won't accept the W3C method for inserting css text
var style, allCssText;
style = currentStyle;
currentStyle = undef;
allCssText = createStyle.accum.join('\n');
createStyle.accum = undef;
// for safari which chokes on @charset "UTF-8";
allCssText = allCssText.replace(/.+charset[^;]+;/g, '');
// TODO: hoist all @imports to the top of the file to follow w3c spec
'cssText' in style ? style.cssText = allCssText :
style.appendChild(doc.createTextNode(allCssText));
}, 0);
return currentStyle;
}
/*
// return the run-time API
callback({
'translateUrls': function (cssText, baseId) {
var baseUrl;
baseUrl = require['toUrl'](baseId);
baseUrl = baseUrl.substr(0, baseUrl.lastIndexOf('/') + 1);
return translateUrls(cssText, baseUrl);
},
'injectStyle': function (cssText) {
return createStyle(cssText);
},
'proxySheet': function (sheet) {
// for W3C, `sheet` is a reference to a <style> node so we need to
// return the sheet property.
if (sheet.sheet) sheet = sheet.sheet;
return createSheetProxy(sheet);
}
});
*/
return {
'build': function (writer, fetcher, config) {
// writer is a function used to output to the built file
// fetcher is a function used to fetch a text file
// config is the global config
// returns a function that the build tool can use to tell this
// plugin to write-out a resource
return function write (pluginId, resource, resolver) {
var opts, name, url, absId, text, output;
opts = parseSuffixes(resource);
name = opts.shift();
absId = resolver['toAbsMid'](name);
if (!(absId in built)) {
built[absId] = true;
url = resolver['toUrl'](nameWithExt(absId, 'css'));
// fetch text
text = jsEncode(fetcher(url));
// write out a define
// TODO: wait until sheet's rules are active before returning (use an amd promise)
// TODO: fix parser so that it doesn't choke on the word define( in a string
// TODO: write out api calls from this plugin-builder
output = 'def' + 'ine("' + pluginId + '!' + absId + '", ["' + pluginId + '!"], function (api) {\n' +
// translate urls
'\tvar cssText = "' + text + '";\n' +
'\tcssText = api.translateUrls(cssText, "' + absId + '");\n' +
// call the injectStyle function
'\treturn api.proxySheet(api.injectStyle(cssText));\n' +
'});\n';
writer(output);
}
};
}
};
});
define(function () {
return {
normalize: function (id, toAbsId) {
},
compile: function (absId, req, io, config) {
}
};
});
/** MIT License (c) copyright B Cavalier & J Hann */
/**
* curl text! loader builder plugin
*
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*/
define(function () {
"use strict";
// collection of modules that have been written to the built file
var built = {};
function nameWithExt (name, defaultExt) {
return name.lastIndexOf('.') <= name.lastIndexOf('/') ?
name + '.' + defaultExt : name;
}
function jsEncode (text) {
// TODO: hoist the map and regex to the enclosing scope for better performance
var map = { 34: '\\"', 13: '\\r', 12: '\\f', 10: '\\n', 9: '\\t', 8: '\\b' };
return text.replace(/(["\n\f\t\r\b])/g, function (c) {
return map[c.charCodeAt(0)];
});
}
return {
build: function (writer, fetcher, config) {
// writer is a function used to output to the built file
// fetcher is a function used to fetch a text file
// config is the global config
// returns a function that the build tool can use to tell this
// plugin to write-out a resource
return function write (pluginId, resource, resolver) {
var url, absId, text, output;
url = resolver['toUrl'](nameWithExt(resource, 'html'));
absId = resolver['toAbsMid'](resource);
if (!(absId in built)) {
built[absId] = true;
// fetch text
text = jsEncode(fetcher(url));
// write out a define
output = 'define("' + pluginId + '!' + absId + '", function () {\n' +
'\treturn "' + text + '";\n' +
'});\n';
writer(output);
}
};
}
};
});
/** MIT License (c) copyright B Cavalier & J Hann */ /** MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
/** /**
* curl css! plugin * curl css! plugin
...@@ -573,8 +573,7 @@ ...@@ -573,8 +573,7 @@
}, },
'plugin-builder': './builder/css', 'cramPlugin': '../cram/css'
'pluginBuilder': './builder/css'
}); });
......
/** MIT License (c) copyright B Cavalier & J Hann */ /** MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
/** /**
* curl domReady loader plugin * curl domReady loader plugin
......
/** MIT License (c) copyright B Cavalier & J Hann */ /** MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
/** /**
* curl i18n plugin * curl i18n plugin
...@@ -85,7 +85,7 @@ ...@@ -85,7 +85,7 @@
* *
*/ */
(function (global) { (function (window) {
define(/*=='curl/plugin/i18n',==*/ function () { define(/*=='curl/plugin/i18n',==*/ function () {
...@@ -96,6 +96,7 @@ define(/*=='curl/plugin/i18n',==*/ function () { ...@@ -96,6 +96,7 @@ define(/*=='curl/plugin/i18n',==*/ function () {
appendLocaleRx = /(\.js)?$/; appendLocaleRx = /(\.js)?$/;
return { return {
load: function (absId, require, loaded, config) { load: function (absId, require, loaded, config) {
var eb, toFile, locale, bundles, fetched, id, ids, specifiers, i; var eb, toFile, locale, bundles, fetched, id, ids, specifiers, i;
...@@ -160,7 +161,10 @@ define(/*=='curl/plugin/i18n',==*/ function () { ...@@ -160,7 +161,10 @@ define(/*=='curl/plugin/i18n',==*/ function () {
} }
} }
} },
'cramPlugin': '../cram/i18n'
}; };
function fetch (require, id, i, cb, eb) { function fetch (require, id, i, cb, eb) {
...@@ -177,7 +181,9 @@ define(/*=='curl/plugin/i18n',==*/ function () { ...@@ -177,7 +181,9 @@ define(/*=='curl/plugin/i18n',==*/ function () {
} }
function getLocale () { function getLocale () {
var ci = global['clientInformation'] || global.navigator; var ci;
if (!window) return false;
ci = window['clientInformation'] || window.navigator;
return ci.language || ci['userLanguage']; return ci.language || ci['userLanguage'];
} }
...@@ -187,4 +193,4 @@ define(/*=='curl/plugin/i18n',==*/ function () { ...@@ -187,4 +193,4 @@ define(/*=='curl/plugin/i18n',==*/ function () {
}); });
}(this)); }(typeof window != 'undefined' && window));
/** MIT License (c) copyright B Cavalier & J Hann */ /** MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
/** /**
* curl js! plugin * curl js! plugin
...@@ -191,7 +191,9 @@ define(/*=='curl/plugin/js',==*/ ['curl/_privileged'], function (priv) { ...@@ -191,7 +191,9 @@ define(/*=='curl/plugin/js',==*/ ['curl/_privileged'], function (priv) {
} }
} }
} },
'cramPlugin': '../cram/js'
}; };
}); });
......
/** MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
/**
* curl json! plugin
*
* Like the text! plugin, will only load same-domain resources.
*/
(function (globalEval) {
define(/*=='curl/plugin/json',==*/ ['./_fetchText'], function (fetchText) {
var hasJsonParse, missingJsonMsg;
hasJsonParse = typeof JSON != 'undefined' && JSON.parse;
missingJsonMsg = 'Cannot use strictJSONParse without JSON.parse';
return {
load: function (absId, require, loaded, config) {
var evaluator, errback;
errback = loaded['error'] || error;
// create a json evaluator function
if (config.strictJSONParse) {
if (!hasJsonParse) error(new Error(missingJsonMsg));
evaluator = guard(parseSource, loaded, errback);
}
else {
evaluator = guard(evalSource, loaded, errback);
}
// get the text, then eval it
fetchText(require['toUrl'](absId), evaluator, errback);
function evalSource (source) {
loaded(globalEval('(' + source + ')'));
}
function parseSource (source) {
return JSON.parse(source);
}
},
'cramPlugin': '../cram/json'
};
function error (ex) {
throw ex;
}
function guard (evaluator, success, fail) {
return function (source) {
try {
success(evaluator(source));
}
catch (ex) {
fail(ex);
}
}
}
});
}(
function () {/*jshint evil:true*/ return (1,eval)(arguments[0]); }
));
/** MIT License (c) copyright B Cavalier & J Hann */ /** MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
/** /**
* curl link! plugin * curl link! plugin
......
/** MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
/**
* curl style! plugin
*/
define([], function () {
var nonRelUrlRe, findUrlRx, undef, doc, head;
if (typeof window != 'undefined') {
doc = window.document;
head = doc.head || doc.getElementsByTagName('head')[0];
}
// tests for absolute urls and root-relative urls
nonRelUrlRe = /^\/|^[^:]*:\/\//;
// Note: this will fail if there are parentheses in the url
findUrlRx = /url\s*\(['"]?([^'"\)]*)['"]?\)/g;
function translateUrls (cssText, baseUrl) {
return cssText.replace(findUrlRx, function (all, url) {
return 'url("' + translateUrl(url, baseUrl) + '")';
});
}
function translateUrl (url, parentPath) {
// if this is a relative url
if (!nonRelUrlRe.test(url)) {
// append path onto it
url = parentPath + url;
}
return url;
}
/***** style element functions *****/
var currentStyle, callbacks = [];
function createStyle (cssText, callback, errback) {
try {
clearTimeout(createStyle.debouncer);
if (createStyle.accum) {
createStyle.accum.push(cssText);
}
else {
createStyle.accum = [cssText];
currentStyle = doc.createStyleSheet ? doc.createStyleSheet() :
head.appendChild(doc.createElement('style'));
}
callbacks.push({
callback: callback,
errback: errback,
sheet: currentStyle
});
createStyle.debouncer = setTimeout(function () {
var style, allCssText;
try {
style = currentStyle;
currentStyle = undef;
allCssText = createStyle.accum.join('\n');
createStyle.accum = undef;
// for safari which chokes on @charset "UTF-8";
// TODO: see if Safari 5.x and up still complain
allCssText = allCssText.replace(/.+charset[^;]+;/g, '');
// IE 6-8 won't accept the W3C method for inserting css text
'cssText' in style ? style.cssText = allCssText :
style.appendChild(doc.createTextNode(allCssText));
waitForDocumentComplete(notify);
}
catch (ex) {
// just notify most recent errback. no need to spam
errback(ex);
}
}, 0);
}
catch (ex) {
errback(ex);
}
}
function notify () {
var list = callbacks;
callbacks = [];
for (var i = 0, len = list.length; i < len; i++) {
list[i].callback(list[i].sheet);
}
}
/**
* Keep checking for the document readyState to be "complete" since
* Chrome doesn't apply the styles to the document until that time.
* If we return before readyState == 'complete', Chrome may not have
* applied the styles, yet.
* Chrome only.
* @private
* @param cb
*/
function waitForDocumentComplete (cb) {
// this isn't exactly the same as domReady (when dom can be
// manipulated). it's later (when styles are applied).
// chrome needs this (and opera?)
function complete () {
if (isDocumentComplete()) {
cb();
}
else {
setTimeout(complete, 10);
}
}
complete();
}
/**
* Returns true if the documents' readyState == 'complete' or the
* document doesn't implement readyState.
* Chrome only.
* @private
* @return {Boolean}
*/
function isDocumentComplete () {
return !doc.readyState || doc.readyState == 'complete';
}
createStyle.load = function (absId, req, loaded, config) {
// get css text
req([absId], function (cssText) {
// TODO: translate urls?
createStyle(cssText, loaded, loaded.error);
});
};
createStyle.translateUrls = translateUrls;
return createStyle;
});
/** MIT License (c) copyright B Cavalier & J Hann */ /** MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
/** /**
* curl text! loader plugin * curl text! loader plugin
...@@ -8,62 +8,18 @@ ...@@ -8,62 +8,18 @@
*/ */
/** /**
* TODO: load xdomain text, too * TODO: load xdomain text, too, somehow
* *
*/ */
define(/*=='curl/plugin/text',==*/ function () { define(/*=='curl/plugin/text',==*/ ['./_fetchText'], function (fetchText) {
var progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
function xhr () {
if (typeof XMLHttpRequest !== "undefined") {
// rewrite the getXhr method to always return the native implementation
xhr = function () { return new XMLHttpRequest(); };
}
else {
// keep trying progIds until we find the correct one, then rewrite the getXhr method
// to always return that one.
var noXhr = xhr = function () {
throw new Error("getXhr(): XMLHttpRequest not available");
};
while (progIds.length > 0 && xhr === noXhr) (function (id) {
try {
new ActiveXObject(id);
xhr = function () { return new ActiveXObject(id); };
}
catch (ex) {}
}(progIds.shift()));
}
return xhr();
}
function fetchText (url, callback, errback) {
var x = xhr();
x.open('GET', url, true);
x.onreadystatechange = function (e) {
if (x.readyState === 4) {
if (x.status < 400) {
callback(x.responseText);
}
else {
errback(new Error('fetchText() failed. status: ' + x.statusText));
}
}
};
x.send(null);
}
function error (ex) {
throw ex;
}
return { return {
// 'normalize': function (resourceId, toAbsId) { 'normalize': function (resourceId, toAbsId) {
// // remove options // remove options
// return resourceId ? toAbsId(resourceId.split("!")[0]) : resourceId; return resourceId ? toAbsId(resourceId.split("!")[0]) : resourceId;
// }, },
load: function (resourceName, req, callback, config) { load: function (resourceName, req, callback, config) {
// remove suffixes (future) // remove suffixes (future)
...@@ -71,8 +27,12 @@ define(/*=='curl/plugin/text',==*/ function () { ...@@ -71,8 +27,12 @@ define(/*=='curl/plugin/text',==*/ function () {
fetchText(req['toUrl'](resourceName), callback, callback['error'] || error); fetchText(req['toUrl'](resourceName), callback, callback['error'] || error);
}, },
'plugin-builder': './builder/text' 'cramPlugin': '../cram/text'
}; };
function error (ex) {
throw ex;
}
}); });
/** MIT License (c) copyright B Cavalier & J Hann */ /** MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
/** /**
* curl dojo 1.6 shim * curl dojo 1.6 shim
......
/** MIT License (c) copyright B Cavalier & J Hann */ /** MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
/** /**
* curl ssjs shim * curl ssjs shim
...@@ -59,6 +59,10 @@ define(/*=='curl/shim/ssjs',==*/ function (require, exports) { ...@@ -59,6 +59,10 @@ define(/*=='curl/shim/ssjs',==*/ function (require, exports) {
localLoadFunc = remoteLoadFunc = failIfInvoked; localLoadFunc = remoteLoadFunc = failIfInvoked;
} }
if (typeof process === 'object' && process.nextTick) {
priv.core.nextTurn = process.nextTick;
}
function stripExtension (url) { function stripExtension (url) {
return url.replace(/\.js$/, ''); return url.replace(/\.js$/, '');
} }
...@@ -136,5 +140,9 @@ define(/*=='curl/shim/ssjs',==*/ function (require, exports) { ...@@ -136,5 +140,9 @@ define(/*=='curl/shim/ssjs',==*/ function (require, exports) {
: protocol; : protocol;
} }
function _nextTick (func) {
nextTick(func);
}
}); });
}(require, load)); }(require, load));
/** MIT License (c) copyright B Cavalier & J Hann */ /** MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
/** /**
* curl createContext module * curl createContext module
...@@ -266,6 +266,7 @@ define(['curl', 'curl/_privileged', './undefine'], function (curl, priv, undefin ...@@ -266,6 +266,7 @@ define(['curl', 'curl/_privileged', './undefine'], function (curl, priv, undefin
function createTrackedRequire (require, modulesAllFetched) { function createTrackedRequire (require, modulesAllFetched) {
var callCount = 0; var callCount = 0;
function trackedRequire (idOrArray, callback) { function trackedRequire (idOrArray, callback) {
var cb; var cb;
...@@ -277,12 +278,14 @@ define(['curl', 'curl/_privileged', './undefine'], function (curl, priv, undefin ...@@ -277,12 +278,14 @@ define(['curl', 'curl/_privileged', './undefine'], function (curl, priv, undefin
if (--callCount == 0) modulesAllFetched(); if (--callCount == 0) modulesAllFetched();
}; };
// preserve AMD API
trackedRequire.toUrl = require.toUrl;
return require(idOrArray, cb); return require(idOrArray, cb);
} }
// preserve AMD API
trackedRequire.toUrl = require.toUrl;
// helpful
trackedRequire.notAsync = function () { return callCount == 0; }; trackedRequire.notAsync = function () { return callCount == 0; };
return trackedRequire; return trackedRequire;
} }
......
/** MIT License (c) copyright B Cavalier & J Hann */ /** MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
/** /**
* curl createContext module * curl createContext module
......
root = true
[*]
indent_style = tab
end_of_line = LF
\ No newline at end of file
.idea/
.npmignore
node_modules/
projectFilesBackup/
{
"browser": true,
"node": true,
"es5": true,
"predef": [
"define",
"module"
],
"boss": true,
"curly": true,
"eqnull": true,
"expr": true,
"globalstrict": false,
"laxbreak": true,
"newcap": true,
"noarg": true,
"noempty": true,
"nonew": true,
"quotmark": "single",
"strict": false,
"trailing": true,
"undef": true,
"unused": true,
"maxdepth": 4,
"maxcomplexity": 6
}
\ No newline at end of file
### 1.3.0
* [`meld()`](docs/api.md#adding-aspects) is now a function that adds aspects.
* **DEPRECATED:** `meld.add()`. Use `meld()` instead.
### 1.2.2
* Remove stray `console.log`.
### 1.2.1
* Fix for IE8-specific issue with meld's internal use of `Object.defineProperty`.
* Internally shim Object.create if not available to so that meld no longer requires an Object.create shim to advise constructors in pre-ES5 environments.
### 1.2.0
* `meld.joinpoint()` - [Access the current joinpoint](docs/api.md#meldjoinpoint) from any advice type.
* [Bundled aspects](docs/aspects.md):
* trace: trace method call entry/return/throw
* memoize: simple memoization for methods and functions
* cache: configurable caching aspect to do more than simple memoization
### 1.1.0
* Advice can be applied directly to methods on a function.
* Removed undocumented behavior that implicitly adds constructor prototype advice: to advise a prototype, pass the prototype as the advice target.
### 1.0.0
* **Removed browser global** - `window.meld` is no longer supported. See [this post on the cujo.js Google Group](https://groups.google.com/d/topic/cujojs/K0VGuvpYQ34/discussion) for an explanation.
* No functional change beyond browser global removal.
### 0.8.0
* 1.0.0 Release Candidate 1
* Documentation! Check out the new [reference](docs/reference.md) and [api](docs/api.md) docs.
* **Deprecated browser global** - meld.js will drop support for browser global for 1.0.0 and will support modular environments only.
### 0.7.2
* Fix for context when advising constructors: `this` is now the constructed instance in all advice functions.
### 0.7.1
* Fix for global name when using meld as a browser global. Thanks [@scothis](https://github.com/scothis)
* Update unit tests to run in browser using `buster server`, in addition to node. Thanks again, [@scothis](https://github.com/scothis) :)
### 0.7.0
* Advice can be applied directly to functions without a context.
* Advice can be applied to constructors.
* `joinpoint.proceed()` can be called multiple times. This makes it possible to implement "retry" types of advice.
### 0.6.0
* aop.js is now meld.js
* Use [Travis CI](http://travis-ci.org/cujojs/meld)
### 0.5.4
* Optimizations to run time advice invocation, especially around advice
* Fix for passing new args to `joinpoint.proceed()` in around advice
* Added `joinpoint.proceedApply(array)` for proceeding and supplying new arguments as an array
* Ported unit tests to [BusterJS](http://busterjs.org)
### 0.5.3
* First official release as part of [cujojs](http://github.com/cujojs)
* Minor doc and package.json tweaks
### 0.5.2
* Revert to larger, more builder-friendly module boilerplate. No functional change.
### 0.5.1
* Minor corrections and updates to `package.json`
### 0.5.0
* Rewritten Advisor that allows entire aspects to be unwoven (removed) easily.
\ No newline at end of file
[![Build Status](https://secure.travis-ci.org/cujojs/meld.png)](http://travis-ci.org/cujojs/meld)
[Aspect Oriented Programming](http://en.wikipedia.org/wiki/Aspect-oriented_programming "Aspect-oriented programming - Wikipedia, the free encyclopedia") for Javascript. It allows you to change the behavior of, or add behavior to methods and functions (including constructors) *non-invasively*.
As a simple example, instead of changing code, you can use meld to log the result of `myObject.doSomething`:
```js
var myObject = {
doSomething: function(a, b) {
return a + b;
}
};
// Call a function after myObject.doSomething returns
var remover = meld.after(myObject, 'doSomething', function(result) {
console.log('myObject.doSomething returned: ' + result);
});
myObject.doSomething(1, 2); // Logs: "myObject.doSomething returned: 3"
remover.remove();
myObject.doSomething(1, 2); // Nothing logged
```
# Docs
* [API](docs/api.md)
* [Reference](docs/reference.md)
* [Aspects](docs/aspects.md)
# Quick Start
### AMD
1. Get it using one of the following
1. `yeoman install meld`, or
1. `bower install meld`, or
1. `git clone https://github.com/cujojs/meld`, or
1. `git submodule add https://github.com/cujojs/meld`
1. Configure your loader with a package:
```js
packages: [
{ name: 'meld', location: 'path/to/meld', main: 'meld' },
// ... other packages ...
]
```
1. `define(['meld', ...], function(meld, ...) { ... });` or `require(['meld', ...], function(meld, ...) { ... });`
### Node
1. `npm install meld`
1. `var meld = require('meld');`
### RingoJS
1. `ringo-admin install cujojs/meld`
1. `var meld = require('meld');`
Running the Unit Tests
======================
Install [buster.js](http://busterjs.org/)
`npm install -g buster`
Run unit tests in Node:
`buster test`
# What's New
### 1.3.0
* [`meld()`](docs/api.md#adding-aspects) is now a function that adds aspects.
* **DEPRECATED:** `meld.add()`. Use `meld()` instead.
### 1.2.2
* Remove stray `console.log`.
### 1.2.1
* Fix for IE8-specific issue with meld's internal use of `Object.defineProperty`.
* Internally shim Object.create if not available to so that meld no longer requires an Object.create shim to advise constructors in pre-ES5 environments.
### 1.2.0
* `meld.joinpoint()` - [Access the current joinpoint](docs/api.md#meldjoinpoint) from any advice type.
* [Bundled aspects](docs/aspects.md):
* trace: trace method call entry/return/throw
* memoize: simple memoization for methods and functions
* cache: configurable caching aspect to do more than simple memoization
### 1.1.0
* Advice can be applied directly to methods on a function.
* Removed undocumented behavior that implicitly adds constructor prototype advice: to advise a prototype, pass the prototype as the advice target.
### 1.0.0
* **Removed browser global** - `window.meld` is no longer supported. See [this post on the cujo.js Google Group](https://groups.google.com/d/topic/cujojs/K0VGuvpYQ34/discussion) for an explanation.
* No functional change beyond browser global removal.
See the [full Changelog here](CHANGES.md)
# References
* [AspectJ](http://www.eclipse.org/aspectj/) and [Spring Framework AOP](http://static.springsource.org/spring/docs/3.0.x/reference/meld.html) for inspiration and great docs
* Implementation ideas from @phiggins42's [uber.js AOP](https://github.com/phiggins42/uber.js/blob/master/lib/meld.js)
* API ideas from [jquery-aop](http://code.google.com/p/jquery-aop/)
\ No newline at end of file
{
"name": "meld",
"version": "1.3.0",
"repository": {
"type": "git",
"url": "git://github.com/cujojs/meld.git"
}
}
\ No newline at end of file
{
"name": "meld",
"version": "1.3.0",
"description": "AOP for JS with before, around, on, afterReturning, afterThrowing, after advice, and pointcut support",
"keywords": ["aop", "aspect", "cujo"],
"homepage": "http://cujojs.com",
"licenses": [
{
"type": "MIT",
"url": "http://www.opensource.org/licenses/mit-license.php"
}
],
"repositories": [
{
"type": "git",
"url": "https://github.com/cujojs/meld"
}
],
"bugs": "https://github.com/cujojs/meld/issues",
"maintainers": [
{
"name": "Brian Cavalier",
"web": "http://hovercraftstudios.com"
},
{
"name": "John Hann",
"web": "http://unscriptable.com"
}
],
"contributors": [
{
"name": "Brian Cavalier",
"web": "http://hovercraftstudios.com"
},
{
"name": "John Hann",
"web": "http://unscriptable.com"
},
{
"name": "Scott Andrews"
}
],
"devDependencies": {
"buster": "~0.6",
"jshint": "~1"
},
"main": "meld",
"directories": {
"test": "test"
},
"scripts": {
"test": "jshint . && buster test -e node"
}
}
\ No newline at end of file
/** /**
* polyfill / shim plugin for AMD loaders * polyfill / shim plugin for AMD loaders
* *
* (c) copyright 2011-2012 Brian Cavalier and John Hann * (c) copyright 2011-2013 Brian Cavalier and John Hann
* *
* poly is part of the cujo.js family of libraries (http://cujojs.com/) * poly is part of the cujo.js family of libraries (http://cujojs.com/)
* *
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
*/ */
define(['./object', './string', './date', './array', './function', './json', './xhr'], function (object, string, date) { define(['./object', './string', './date', './array', './function', './json', './xhr', './setImmediate'], function (object, string, date) {
return { return {
failIfShimmed: object.failIfShimmed, failIfShimmed: object.failIfShimmed,
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
Array -- a stand-alone module for using Javascript 1.6 array features Array -- a stand-alone module for using Javascript 1.6 array features
in lame-o browsers that don't support Javascript 1.6 in lame-o browsers that don't support Javascript 1.6
(c) copyright 2011-2012 Brian Cavalier and John Hann (c) copyright 2011-2013 Brian Cavalier and John Hann
This module is part of the cujo.js family of libraries (http://cujojs.com/). This module is part of the cujo.js family of libraries (http://cujojs.com/).
......
{
"name": "poly",
"version": "0.5.1",
"repository": {
"type": "git",
"url": "git://github.com/cujojs/poly"
}
}
\ No newline at end of file
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
ES5-ish Date shims for older browsers. ES5-ish Date shims for older browsers.
(c) copyright 2011-2012 Brian Cavalier and John Hann (c) copyright 2011-2013 Brian Cavalier and John Hann
This module is part of the cujo.js family of libraries (http://cujojs.com/). This module is part of the cujo.js family of libraries (http://cujojs.com/).
...@@ -20,11 +20,14 @@ define(['./lib/_base'], function (base) { ...@@ -20,11 +20,14 @@ define(['./lib/_base'], function (base) {
invalidDate, invalidDate,
isoCompat, isoCompat,
isoParseRx, isoParseRx,
ownProp,
undef; undef;
origProto = origDate.prototype; origProto = origDate.prototype;
origParse = origDate.parse; origParse = origDate.parse;
ownProp = Object.prototype.hasOwnProperty;
maxDate = 8.64e15; maxDate = 8.64e15;
invalidDate = NaN; invalidDate = NaN;
// borrowed this from https://github.com/kriskowal/es5-shim // borrowed this from https://github.com/kriskowal/es5-shim
...@@ -119,6 +122,7 @@ define(['./lib/_base'], function (base) { ...@@ -119,6 +122,7 @@ define(['./lib/_base'], function (base) {
} }
if (!has('date-tojson')) { if (!has('date-tojson')) {
origProto.toJSON = function toJSON (key) { origProto.toJSON = function toJSON (key) {
// key arg is ignored by Date objects, but since this function // key arg is ignored by Date objects, but since this function
// is generic, other Date-like objects could use the key arg. // is generic, other Date-like objects could use the key arg.
...@@ -129,39 +133,42 @@ define(['./lib/_base'], function (base) { ...@@ -129,39 +133,42 @@ define(['./lib/_base'], function (base) {
} }
function checkIsoCompat () { function checkIsoCompat () {
if (!isoCompat()) { // fix Date constructor
// fix Date constructor
function Date_ (y, m, d, h, mn, s, ms) { var newDate = (function () {
// Replacement Date constructor
return function Date (y, m, d, h, mn, s, ms) {
var len, result; var len, result;
// Date_ called as function, not constructor // Date called as function, not constructor
if (!(this instanceof Date_)) return origDate.apply(this, arguments); if (!(this instanceof newDate)) return origDate.apply(this, arguments);
len = arguments.length; len = arguments.length;
if (len == 0) { if (len === 0) {
result = new origDate(); result = new origDate();
} }
else if (len == 1) { else if (len === 1) {
result = new origDate(base.isString(y) ? Date.parse(y) : y); result = new origDate(base.isString(y) ? newDate.parse(y) : y);
} }
else { else {
result = new origDate(y, m, d == undef ? 1 : d, h || 0, mn || 0, s || 0, ms || 0); result = new origDate(y, m, d == undef ? 1 : d, h || 0, mn || 0, s || 0, ms || 0);
} }
result.constructor = Date_; result.constructor = newDate;
return result; return result;
} };
}());
if (!isoCompat()) {
Date_.now = origDate.now; newDate.now = origDate.now;
Date_.UTC = origDate.UTC; newDate.UTC = origDate.UTC;
Date_.prototype = origProto; newDate.prototype = origProto;
Date_.prototype.constructor = Date_; newDate.prototype.constructor = newDate;
Date_.parse = function parse (str) { newDate.parse = function parse (str) {
var result; var result;
// check for iso date // check for iso date
...@@ -175,11 +182,25 @@ define(['./lib/_base'], function (base) { ...@@ -175,11 +182,25 @@ define(['./lib/_base'], function (base) {
return result; return result;
}; };
Date = Date_; // Unfortunate. See cujojs/poly#11
// Copy any owned props that may have been previously added to
// the Date constructor by 3rd party libs.
copyPropsSafely(newDate, origDate);
Date = newDate;
} }
else if (Date != origDate) { else if (Date != origDate) {
Date = origDate; Date = origDate;
} }
}
function copyPropsSafely(dst, src) {
for (var p in src) {
if (ownProp.call(src, p) && !ownProp.call(dst, p)) {
dst[p] = src[p];
}
}
} }
checkIsoCompat(); checkIsoCompat();
......
/*
poly/strict
(c) copyright 2011-2013 Brian Cavalier and John Hann
This module is part of the cujo.js family of libraries (http://cujojs.com/).
Licensed under the MIT License at:
http://www.opensource.org/licenses/mit-license.php
*/
define(['./object', './string', './date', './array', './function', './json', './xhr'], function (object, string, date) {
var failTestRx;
failTestRx = /^define|^prevent|descriptor$/i;
function regexpShouldThrow (feature) {
return failTestRx.test(feature);
}
// set unshimmable Object methods to be somewhat strict:
object.failIfShimmed(regexpShouldThrow);
// set strict whitespace
string.setWhitespaceChars('\\s');
return {
failIfShimmed: object.failIfShimmed,
setWhitespaceChars: string.setWhitespaceChars,
setIsoCompatTest: date.setIsoCompatTest
};
});
/**
* polyfill / shim plugin for AMD loaders
*
* (c) copyright 2011-2013 Brian Cavalier and John Hann
*
* poly is part of the cujo.js family of libraries (http://cujojs.com/)
*
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*/
define(['./object', './string', './date', './array', './function', './json', './xhr'], function (object, string, date) {
return {
failIfShimmed: object.failIfShimmed,
setWhitespaceChars: string.setWhitespaceChars,
setIsoCompatTest: date.setIsoCompatTest
};
});
/** /**
* Function polyfill / shims * Function polyfill / shims
* *
* (c) copyright 2011-2012 Brian Cavalier and John Hann * (c) copyright 2011-2013 Brian Cavalier and John Hann
* *
* This module is part of the cujo.js family of libraries (http://cujojs.com/). * This module is part of the cujo.js family of libraries (http://cujojs.com/).
* *
......
/** /**
* JSON polyfill / shim * JSON polyfill / shim
* *
* (c) copyright 2011-2012 Brian Cavalier and John Hann * (c) copyright 2011-2013 Brian Cavalier and John Hann
* *
* poly is part of the cujo.js family of libraries (http://cujojs.com/) * poly is part of the cujo.js family of libraries (http://cujojs.com/)
* *
* Licensed under the MIT License at: * Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
* *
* TODO: document that JSON module is always downloaded at run-time unless
* dev explicitly mentions it in build instructions
*/ */
define(['./lib/_async!./lib/_json'], function (JSON) { define(['./support/json3'], function (JSON) {
return JSON; return JSON;
}); });
/**
* async plugin for AMD loaders
*
* (c) copyright 2011-2012 Brian Cavalier and John Hann
*
* poly is part of the cujo.js family of libraries (http://cujojs.com/)
*
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*
*/
define(function () {
return {
load: function (def, require, onload, config) {
function success (module) {
// check for curl.js's promise
onload.resolve ? onload.resolve(module) : onload(module);
}
function fail (ex) {
// check for curl.js's promise
if (onload.reject) {
onload.reject(ex)
}
else {
throw ex;
}
}
// load module. wait for it if it returned a promise
require([def], function (module) {
if (module && typeof module.then == 'function') {
module.then(success, fail);
}
else {
success(module);
}
});
}
};
});
/** /**
* poly common functions * poly common functions
* *
* (c) copyright 2011-2012 Brian Cavalier and John Hann * (c) copyright 2011-2013 Brian Cavalier and John Hann
* *
* This module is part of the cujo.js family of libraries (http://cujojs.com/). * This module is part of the cujo.js family of libraries (http://cujojs.com/).
* *
......
/**
* JSON polyfill / shim
*
* (c) copyright 2011-2012 Brian Cavalier and John Hann
*
* poly is part of the cujo.js family of libraries (http://cujojs.com/)
*
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*/
(function (global) {
define(['require'], function (require) {
var cbs, ebs, promise;
function has (feature) {
return global.JSON;
}
if (!has('json')) {
cbs = [];
ebs = [];
promise = {
then: function (cb, eb) {
if (cb) cbs.push(cb);
if (eb) ebs.push(eb);
}
};
function callback (list, val) {
promise.then = list == cbs
? function (cb, eb) { cb(val); }
: function (cb, eb) { eb(val); }
for (var i = 0; i < list.length; i++) list[i](val);
}
function resolve (val) {
callback(cbs, val);
}
function reject (ex) {
callback(ebs, ex);
}
require(['../support/json2'], resolve, reject);
return promise;
}
});
}(this));
/** /**
* Object polyfill / shims * Object polyfill / shims
* *
* (c) copyright 2011-2012 Brian Cavalier and John Hann * (c) copyright 2011-2013 Brian Cavalier and John Hann
* *
* This module is part of the cujo.js family of libraries (http://cujojs.com/). * This module is part of the cujo.js family of libraries (http://cujojs.com/).
* *
...@@ -52,24 +52,45 @@ ...@@ -52,24 +52,45 @@
* IE missing enum properties fixes copied from kangax: * IE missing enum properties fixes copied from kangax:
* https://github.com/kangax/protolicious/blob/master/experimental/object.for_in.js * https://github.com/kangax/protolicious/blob/master/experimental/object.for_in.js
* *
* TODO: fix Object#propertyIsEnumerable for IE's non-enumerable props to match Object.keys()
*/ */
define(['./lib/_base'], function (base) { define(['./lib/_base'], function (base) {
"use strict"; "use strict";
var refObj, var refObj,
refProto, refProto,
has__proto__,
hasNonEnumerableProps,
getPrototypeOf, getPrototypeOf,
keys, keys,
featureMap, featureMap,
shims, shims,
secrets,
protoSecretProp,
hasOwnProp = 'hasOwnProperty',
undef; undef;
refObj = Object; refObj = Object;
refProto = refObj.prototype; refProto = refObj.prototype;
getPrototypeOf = typeof {}.__proto__ == 'object' has__proto__ = typeof {}.__proto__ == 'object';
? function (object) { return object.__proto__; }
: function (object) { return object.constructor ? object.constructor.prototype : refProto; }; hasNonEnumerableProps = (function () {
for (var p in { valueOf: 1 }) return false;
return true;
}());
// TODO: this still doesn't work for IE6-8 since object.constructor && object.constructor.prototype are clobbered/replaced when using `new` on a constructor that has a prototype. srsly.
// devs will have to do the following if they want this to work in IE6-8:
// Ctor.prototype.constructor = Ctor
getPrototypeOf = has__proto__
? function (object) { assertIsObject(object); return object.__proto__; }
: function (object) {
assertIsObject(object);
return protoSecretProp && object[protoSecretProp](secrets)
? object[protoSecretProp](secrets.proto)
: object.constructor ? object.constructor.prototype : refProto;
};
keys = !hasNonEnumerableProps keys = !hasNonEnumerableProps
? _keys ? _keys
...@@ -81,7 +102,7 @@ define(['./lib/_base'], function (base) { ...@@ -81,7 +102,7 @@ define(['./lib/_base'], function (base) {
} }
return result; return result;
} }
}([ 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toString', 'toLocaleString', 'valueOf' ])); }([ 'constructor', hasOwnProp, 'isPrototypeOf', 'propertyIsEnumerable', 'toString', 'toLocaleString', 'valueOf' ]));
featureMap = { featureMap = {
'object-create': 'create', 'object-create': 'create',
...@@ -101,10 +122,11 @@ define(['./lib/_base'], function (base) { ...@@ -101,10 +122,11 @@ define(['./lib/_base'], function (base) {
shims = {}; shims = {};
function hasNonEnumerableProps () { secrets = {
for (var p in { toString: 1 }) return false; proto: {}
return true; };
}
protoSecretProp = !has('object-getprototypeof') && !has__proto__ && hasNonEnumerableProps && hasOwnProp;
function createFlameThrower (feature) { function createFlameThrower (feature) {
return function () { return function () {
...@@ -134,6 +156,15 @@ define(['./lib/_base'], function (base) { ...@@ -134,6 +156,15 @@ define(['./lib/_base'], function (base) {
return result; return result;
} }
// we might create an owned property to hold the secrets, but make it look
// like it's not an owned property. (affects getOwnPropertyNames, too)
if (protoSecretProp) (function (_hop) {
refProto[hasOwnProp] = function (name) {
if (name == protoSecretProp) return false;
return _hop.call(this, name);
};
}(refProto[hasOwnProp]));
if (!has('object-create')) { if (!has('object-create')) {
Object.create = shims.create = function create (proto, props) { Object.create = shims.create = function create (proto, props) {
var obj; var obj;
...@@ -141,11 +172,21 @@ define(['./lib/_base'], function (base) { ...@@ -141,11 +172,21 @@ define(['./lib/_base'], function (base) {
if (typeof proto != 'object') throw new TypeError('prototype is not of type Object or Null.'); if (typeof proto != 'object') throw new TypeError('prototype is not of type Object or Null.');
PolyBase.prototype = proto; PolyBase.prototype = proto;
obj = new PolyBase(props); obj = new PolyBase();
PolyBase.prototype = null; PolyBase.prototype = null;
// provide a mechanism for retrieving the prototype in IE 6-8
if (protoSecretProp) {
var orig = obj[protoSecretProp];
obj[protoSecretProp] = function (name) {
if (name == secrets) return true; // yes, we're using secrets
if (name == secrets.proto) return proto;
return orig.call(this, name);
};
}
if (arguments.length > 1) { if (arguments.length > 1) {
// defineProperties could throw depending on `shouldThrow` // defineProperties could throw depending on `failIfShimmed`
Object.defineProperties(obj, props); Object.defineProperties(obj, props);
} }
...@@ -264,8 +305,7 @@ define(['./lib/_base'], function (base) { ...@@ -264,8 +305,7 @@ define(['./lib/_base'], function (base) {
} }
} }
// this is effectively a no-op, so why execute it? function assertIsObject (o) { if (typeof o != 'object') throw new TypeError('Object.getPrototypeOf called on non-object'); }
//failIfShimmed(false);
return { return {
failIfShimmed: failIfShimmed failIfShimmed: failIfShimmed
......
/** /**
* polyfill / shim plugin for AMD loaders * polyfill / shim plugin for AMD loaders
* *
* (c) copyright 2011-2012 Brian Cavalier and John Hann * (c) copyright 2011-2013 Brian Cavalier and John Hann
* *
* poly is part of the cujo.js family of libraries (http://cujojs.com/) * poly is part of the cujo.js family of libraries (http://cujojs.com/)
* *
* Licensed under the MIT License at: * Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
* *
* @version 0.5.1
*/ */
define(['./all'], function (all) { define(['./all'], function (all) {
...@@ -18,7 +17,7 @@ define(['./all'], function (all) { ...@@ -18,7 +17,7 @@ define(['./all'], function (all) {
// copy all // copy all
for (var p in all) poly[p] = all[p]; for (var p in all) poly[p] = all[p];
poly.version = '0.5.1'; poly.version = '0.5.2';
return poly; return poly;
......
/**
* setImmediate polyfill / shim
*
* (c) copyright 2011-2013 Brian Cavalier and John Hann
*
* poly is part of the cujo.js family of libraries (http://cujojs.com/)
*
* Based on NobleJS's setImmediate. (https://github.com/NobleJS/setImmediate)
*
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*
*/
(function (global) {
define(['./lib/_base'], function (base) {
var testCache,
tasks;
testCache = {};
tasks = (function () {
var nextHandle,
tasksByHandle,
currentlyRunningATask;
nextHandle = 1; // Spec says greater than zero
tasksByHandle = {};
currentlyRunningATask = false;
function Task (handler, args) {
this.handler = handler;
this.args = Array.prototype.slice.call(args);
}
Task.prototype.run = function () {
// See steps in section 5 of the spec.
if (base.isFunction(this.handler)) {
// Choice of `thisArg` is not in the setImmediate spec; `undefined` is in the setTimeout spec though:
// http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html
this.handler.apply(undefined, this.args);
}
else {
var scriptSource = '' + this.handler;
eval(scriptSource);
}
};
return {
addFromSetImmediateArguments: function (args) {
var handler,
argsToHandle,
task,
thisHandle;
handler = args[0];
argsToHandle = Array.prototype.slice.call(args, 1);
task = new Task(handler, argsToHandle);
thisHandle = nextHandle++;
tasksByHandle[thisHandle] = task;
return thisHandle;
},
runIfPresent: function (handle) {
// From the spec: "Wait until any invocations of this algorithm started before this one have completed."
// So if we're currently running a task, we'll need to delay this invocation.
if (!currentlyRunningATask) {
var task = tasksByHandle[handle];
if (task) {
currentlyRunningATask = true;
try {
task.run();
} finally {
delete tasksByHandle[handle];
currentlyRunningATask = false;
}
}
} else {
// Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a
// "too much recursion" error.
global.setTimeout(function () {
tasks.runIfPresent(handle);
}, 0);
}
},
remove: function (handle) {
delete tasksByHandle[handle];
}
};
}());
function has (name) {
if (base.isFunction(testCache[name])) {
testCache[name] = testCache[name](global);
}
return testCache[name];
}
function add (name, test, now) {
testCache[name] = now ? test(global, d, el) : test;
}
function aliasMicrosoftImplementation (attachTo) {
attachTo.setImmediate = global.msSetImmediate;
attachTo.clearImmediate = global.msClearImmediate;
}
function installPostMessageImplementation (attachTo) {
// Installs an event handler on `global` for the `message` event: see
// * https://developer.mozilla.org/en/DOM/window.postMessage
// * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages
var MESSAGE_PREFIX = 'cujojs/poly.setImmediate' + Math.random();
function isStringAndStartsWith (string, putativeStart) {
return typeof string === 'string' && string.substring(0, putativeStart.length) === putativeStart;
}
function onGlobalMessage (event) {
// This will catch all incoming messages (even from other windows!), so we need to try reasonably hard to
// avoid letting anyone else trick us into firing off. We test the origin is still this window, and that a
// (randomly generated) unpredictable identifying prefix is present.
if (event.source === global && isStringAndStartsWith(event.data, MESSAGE_PREFIX)) {
var handle = event.data.substring(MESSAGE_PREFIX.length);
tasks.runIfPresent(handle);
}
}
global.addEventListener('message', onGlobalMessage, false);
attachTo.setImmediate = function () {
var handle = tasks.addFromSetImmediateArguments(arguments);
// Make `global` post a message to itself with the handle and identifying prefix, thus asynchronously
// invoking our onGlobalMessage listener above.
global.postMessage(MESSAGE_PREFIX + handle, '*');
return handle;
};
}
function installReadyStateChangeImplementation(attachTo) {
attachTo.setImmediate = function () {
var handle = tasks.addFromSetImmediateArguments(arguments);
// Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
// into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
var scriptEl = global.document.createElement('script');
scriptEl.onreadystatechange = function () {
tasks.runIfPresent(handle);
scriptEl.onreadystatechange = null;
scriptEl.parentNode.removeChild(scriptEl);
scriptEl = null;
};
global.document.documentElement.appendChild(scriptEl);
return handle;
};
}
function installSetTimeoutImplementation(attachTo) {
attachTo.setImmediate = function () {
var handle = tasks.addFromSetImmediateArguments(arguments);
global.setTimeout(function () {
tasks.runIfPresent(handle);
}, 0);
return handle;
};
}
add('setimmediate', function (g) {
return base.isFunction(g.setImmediate);
});
add('ms-setimmediate', function (g) {
return base.isFunction(g.msSetImmediate);
});
add('post-message', function (g) {
// Note: this is only for the async postMessage, not the buggy sync
// version in IE8
var postMessageIsAsynchronous,
oldOnMessage;
postMessageIsAsynchronous = true;
oldOnMessage = g.onmessage;
if (!g.postMessage) {
return false;
}
g.onmessage = function () {
postMessageIsAsynchronous = false;
};
g.postMessage('', '*');
g.onmessage = oldOnMessage;
return postMessageIsAsynchronous;
});
add('script-onreadystatechange', function (g) {
return 'document' in g && 'onreadystatechange' in g.document.createElement('script');
});
if (!has('setimmediate')) {
if (has('ms-setimmediate')) {
aliasMicrosoftImplementation(global);
}
else {
if (has('post-message')) {
installPostMessageImplementation(global);
}
else if (has('script-onreadystatechange')) {
installReadyStateChangeImplementation(global);
}
else {
installSetTimeoutImplementation(global);
}
global.clearImmediate = tasks.remove;
}
}
});
}(this.global || this));
/* /*
poly/strict poly/strict
(c) copyright 2011-2012 Brian Cavalier and John Hann (c) copyright 2011-2013 Brian Cavalier and John Hann
This module is part of the cujo.js family of libraries (http://cujojs.com/). This module is part of the cujo.js family of libraries (http://cujojs.com/).
Licensed under the MIT License at: Licensed under the MIT License at:
http://www.opensource.org/licenses/mit-license.php http://www.opensource.org/licenses/mit-license.php
*/ */
/**
* @deprecated Please use poly/es5-strict
*/
define(['./object', './string', './date', './array', './function', './json', './xhr'], function (object, string, date) { define(['./object', './string', './date', './array', './function', './json', './xhr'], function (object, string, date) {
var failTestRx; var failTestRx;
......
/** /**
* String polyfill / shims * String polyfill / shims
* *
* (c) copyright 2011-2012 Brian Cavalier and John Hann * (c) copyright 2011-2013 Brian Cavalier and John Hann
* *
* This module is part of the cujo.js family of libraries (http://cujojs.com/). * This module is part of the cujo.js family of libraries (http://cujojs.com/).
* *
......
/*
json2.js
2011-10-19
Public Domain.
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
See http://www.JSON.org/js.html
This code should be minified before deployment.
See http://javascript.crockford.com/jsmin.html
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
NOT CONTROL.
This file creates a global JSON object containing two methods: stringify
and parse.
JSON.stringify(value, replacer, space)
value any JavaScript value, usually an object or array.
replacer an optional parameter that determines how object
values are stringified for objects. It can be a
function or an array of strings.
space an optional parameter that specifies the indentation
of nested structures. If it is omitted, the text will
be packed without extra whitespace. If it is a number,
it will specify the number of spaces to indent at each
level. If it is a string (such as '\t' or '&nbsp;'),
it contains the characters used to indent at each level.
This method produces a JSON text from a JavaScript value.
When an object value is found, if the object contains a toJSON
method, its toJSON method will be called and the result will be
stringified. A toJSON method does not serialize: it returns the
value represented by the name/value pair that should be serialized,
or undefined if nothing should be serialized. The toJSON method
will be passed the key associated with the value, and this will be
bound to the value
For example, this would serialize Dates as ISO strings.
Date.prototype.toJSON = function (key) {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
return this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z';
};
You can provide an optional replacer method. It will be passed the
key and value of each member, with this bound to the containing
object. The value that is returned from your method will be
serialized. If your method returns undefined, then the member will
be excluded from the serialization.
If the replacer parameter is an array of strings, then it will be
used to select the members to be serialized. It filters the results
such that only members with keys listed in the replacer array are
stringified.
Values that do not have JSON representations, such as undefined or
functions, will not be serialized. Such values in objects will be
dropped; in arrays they will be replaced with null. You can use
a replacer function to replace those with JSON values.
JSON.stringify(undefined) returns undefined.
The optional space parameter produces a stringification of the
value that is filled with line breaks and indentation to make it
easier to read.
If the space parameter is a non-empty string, then that string will
be used for indentation. If the space parameter is a number, then
the indentation will be that many spaces.
Example:
text = JSON.stringify(['e', {pluribus: 'unum'}]);
// text is '["e",{"pluribus":"unum"}]'
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
text = JSON.stringify([new Date()], function (key, value) {
return this[key] instanceof Date ?
'Date(' + this[key] + ')' : value;
});
// text is '["Date(---current time---)"]'
JSON.parse(text, reviver)
This method parses a JSON text to produce an object or array.
It can throw a SyntaxError exception.
The optional reviver parameter is a function that can filter and
transform the results. It receives each of the keys and values,
and its return value is used instead of the original value.
If it returns what it received, then the structure is not modified.
If it returns undefined then the member is deleted.
Example:
// Parse the text. Values that look like ISO date strings will
// be converted to Date objects.
myData = JSON.parse(text, function (key, value) {
var a;
if (typeof value === 'string') {
a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (a) {
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+a[5], +a[6]));
}
}
return value;
});
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
var d;
if (typeof value === 'string' &&
value.slice(0, 5) === 'Date(' &&
value.slice(-1) === ')') {
d = new Date(value.slice(5, -1));
if (d) {
return d;
}
}
return value;
});
This is a reference implementation. You are free to copy, modify, or
redistribute.
*/
/*jslint evil: true, regexp: true */
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
lastIndex, length, parse, prototype, push, replace, slice, stringify,
test, toJSON, toString, valueOf
*/
// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.
var JSON;
if (!JSON) {
JSON = {};
}
(function () {
'use strict';
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
if (typeof Date.prototype.toJSON !== 'function') {
Date.prototype.toJSON = function (key) {
return isFinite(this.valueOf())
? this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z'
: null;
};
String.prototype.toJSON =
Number.prototype.toJSON =
Boolean.prototype.toJSON = function (key) {
return this.valueOf();
};
}
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
gap,
indent,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
},
rep;
function quote(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
escapable.lastIndex = 0;
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 'string'
? c
: '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' : '"' + string + '"';
}
function str(key, holder) {
// Produce a string from holder[key].
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
mind = gap,
partial,
value = holder[key];
// If the value has a toJSON method, call it to obtain a replacement value.
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === 'function') {
value = rep.call(holder, key, value);
}
// What happens next depends on the value's type.
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.
return String(value);
// If the type is 'object', we might be dealing with an object or an array or
// null.
case 'object':
// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.
if (!value) {
return 'null';
}
// Make an array to hold the partial results of stringifying this object value.
gap += indent;
partial = [];
// Is the value an array?
if (Object.prototype.toString.apply(value) === '[object Array]') {
// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value) || 'null';
}
// Join all of the elements together, separated with commas, and wrap them in
// brackets.
v = partial.length === 0
? '[]'
: gap
? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
: '[' + partial.join(',') + ']';
gap = mind;
return v;
}
// If the replacer is an array, use it to select the members to be stringified.
if (rep && typeof rep === 'object') {
length = rep.length;
for (i = 0; i < length; i += 1) {
if (typeof rep[i] === 'string') {
k = rep[i];
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
} else {
// Otherwise, iterate through all of the keys in the object.
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
}
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
v = partial.length === 0
? '{}'
: gap
? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
: '{' + partial.join(',') + '}';
gap = mind;
return v;
}
}
// If the JSON object does not yet have a stringify method, give it one.
if (typeof JSON.stringify !== 'function') {
JSON.stringify = function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.
var i;
gap = '';
indent = '';
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (typeof space === 'number') {
for (i = 0; i < space; i += 1) {
indent += ' ';
}
// If the space parameter is a string, it will be used as the indent string.
} else if (typeof space === 'string') {
indent = space;
}
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if (replacer && typeof replacer !== 'function' &&
(typeof replacer !== 'object' ||
typeof replacer.length !== 'number')) {
throw new Error('JSON.stringify');
}
// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.
return str('', {'': value});
};
}
// If the JSON object does not yet have a parse method, give it one.
if (typeof JSON.parse !== 'function') {
JSON.parse = function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.
var j;
function walk(holder, key) {
// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.
var k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}
// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.
text = String(text);
cx.lastIndex = 0;
if (cx.test(text)) {
text = text.replace(cx, function (a) {
return '\\u' +
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
});
}
// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.
// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
if (/^[\],:{}\s]*$/
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.
j = eval('(' + text + ')');
// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.
return typeof reviver === 'function'
? walk({'': j}, '')
: j;
}
// If the text is not JSON parseable, then a SyntaxError is thrown.
throw new SyntaxError('JSON.parse');
};
}
}());
// AMD FTW
define(function () { return JSON; });
\ No newline at end of file
/*! JSON v3.2.4 | http://bestiejs.github.com/json3 | Copyright 2012, Kit Cambridge | http://kit.mit-license.org */
;(function () {
// Convenience aliases.
var getClass = {}.toString, isProperty, forEach, undef;
// Detect the `define` function exposed by asynchronous module loaders. The
// strict `define` check is necessary for compatibility with `r.js`.
var isLoader = typeof define === "function" && define.amd, JSON3 = !isLoader && typeof exports == "object" && exports;
if (JSON3 || isLoader) {
if (typeof JSON == "object" && JSON) {
// Delegate to the native `stringify` and `parse` implementations in
// asynchronous module loaders and CommonJS environments.
if (isLoader) {
JSON3 = JSON;
} else {
JSON3.stringify = JSON.stringify;
JSON3.parse = JSON.parse;
}
} else if (isLoader) {
JSON3 = this.JSON = {};
}
} else {
// Export for web browsers and JavaScript engines.
JSON3 = this.JSON || (this.JSON = {});
}
// Local variables.
var Escapes, toPaddedString, quote, serialize;
var fromCharCode, Unescapes, abort, lex, get, walk, update, Index, Source;
// Test the `Date#getUTC*` methods. Based on work by @Yaffle.
var isExtended = new Date(-3509827334573292), floor, Months, getDay;
try {
// The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical
// results for certain dates in Opera >= 10.53.
isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() == 1 &&
// Safari < 2.0.2 stores the internal millisecond time value correctly,
// but clips the values returned by the date methods to the range of
// signed 32-bit integers ([-2 ** 31, 2 ** 31 - 1]).
isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708;
} catch (exception) {}
// Internal: Determines whether the native `JSON.stringify` and `parse`
// implementations are spec-compliant. Based on work by Ken Snyder.
function has(name) {
var stringifySupported, parseSupported, value, serialized = '{"A":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}', all = name == "json";
if (all || name == "json-stringify" || name == "json-parse") {
// Test `JSON.stringify`.
if (name == "json-stringify" || all) {
if ((stringifySupported = typeof JSON3.stringify == "function" && isExtended)) {
// A test function object with a custom `toJSON` method.
(value = function () {
return 1;
}).toJSON = value;
try {
stringifySupported =
// Firefox 3.1b1 and b2 serialize string, number, and boolean
// primitives as object literals.
JSON3.stringify(0) === "0" &&
// FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object
// literals.
JSON3.stringify(new Number()) === "0" &&
JSON3.stringify(new String()) == '""' &&
// FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or
// does not define a canonical JSON representation (this applies to
// objects with `toJSON` properties as well, *unless* they are nested
// within an object or array).
JSON3.stringify(getClass) === undef &&
// IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and
// FF 3.1b3 pass this test.
JSON3.stringify(undef) === undef &&
// Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s,
// respectively, if the value is omitted entirely.
JSON3.stringify() === undef &&
// FF 3.1b1, 2 throw an error if the given value is not a number,
// string, array, object, Boolean, or `null` literal. This applies to
// objects with custom `toJSON` methods as well, unless they are nested
// inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON`
// methods entirely.
JSON3.stringify(value) === "1" &&
JSON3.stringify([value]) == "[1]" &&
// Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of
// `"[null]"`.
JSON3.stringify([undef]) == "[null]" &&
// YUI 3.0.0b1 fails to serialize `null` literals.
JSON3.stringify(null) == "null" &&
// FF 3.1b1, 2 halts serialization if an array contains a function:
// `[1, true, getClass, 1]` serializes as "[1,true,],". These versions
// of Firefox also allow trailing commas in JSON objects and arrays.
// FF 3.1b3 elides non-JSON values from objects and arrays, unless they
// define custom `toJSON` methods.
JSON3.stringify([undef, getClass, null]) == "[null,null,null]" &&
// Simple serialization test. FF 3.1b1 uses Unicode escape sequences
// where character escape codes are expected (e.g., `\b` => `\u0008`).
JSON3.stringify({ "A": [value, true, false, null, "\0\b\n\f\r\t"] }) == serialized &&
// FF 3.1b1 and b2 ignore the `filter` and `width` arguments.
JSON3.stringify(null, value) === "1" &&
JSON3.stringify([1, 2], null, 1) == "[\n 1,\n 2\n]" &&
// JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly
// serialize extended years.
JSON3.stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' &&
// The milliseconds are optional in ES 5, but required in 5.1.
JSON3.stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' &&
// Firefox <= 11.0 incorrectly serializes years prior to 0 as negative
// four-digit years instead of six-digit years. Credits: @Yaffle.
JSON3.stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' &&
// Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond
// values less than 1000. Credits: @Yaffle.
JSON3.stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"';
} catch (exception) {
stringifySupported = false;
}
}
if (!all) {
return stringifySupported;
}
}
// Test `JSON.parse`.
if (name == "json-parse" || all) {
if (typeof JSON3.parse == "function") {
try {
// FF 3.1b1, b2 will throw an exception if a bare literal is provided.
// Conforming implementations should also coerce the initial argument to
// a string prior to parsing.
if (JSON3.parse("0") === 0 && !JSON3.parse(false)) {
// Simple parsing test.
value = JSON3.parse(serialized);
if ((parseSupported = value.A.length == 5 && value.A[0] == 1)) {
try {
// Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings.
parseSupported = !JSON3.parse('"\t"');
} catch (exception) {}
if (parseSupported) {
try {
// FF 4.0 and 4.0.1 allow leading `+` signs, and leading and
// trailing decimal points. FF 4.0, 4.0.1, and IE 9-10 also
// allow certain octal literals.
parseSupported = JSON3.parse("01") != 1;
} catch (exception) {}
}
}
}
} catch (exception) {
parseSupported = false;
}
}
if (!all) {
return parseSupported;
}
}
return stringifySupported && parseSupported;
}
}
if (!has("json")) {
// Define additional utility methods if the `Date` methods are buggy.
if (!isExtended) {
floor = Math.floor;
// A mapping between the months of the year and the number of days between
// January 1st and the first of the respective month.
Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
// Internal: Calculates the number of days between the Unix epoch and the
// first day of the given month.
getDay = function (year, month) {
return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400);
};
}
// Internal: Determines if a property is a direct property of the given
// object. Delegates to the native `Object#hasOwnProperty` method.
if (!(isProperty = {}.hasOwnProperty)) {
isProperty = function (property) {
var members = {}, constructor;
if ((members.__proto__ = null, members.__proto__ = {
// The *proto* property cannot be set multiple times in recent
// versions of Firefox and SeaMonkey.
"toString": 1
}, members).toString != getClass) {
// Safari <= 2.0.3 doesn't implement `Object#hasOwnProperty`, but
// supports the mutable *proto* property.
isProperty = function (property) {
// Capture and break the object's prototype chain (see section 8.6.2
// of the ES 5.1 spec). The parenthesized expression prevents an
// unsafe transformation by the Closure Compiler.
var original = this.__proto__, result = property in (this.__proto__ = null, this);
// Restore the original prototype chain.
this.__proto__ = original;
return result;
};
} else {
// Capture a reference to the top-level `Object` constructor.
constructor = members.constructor;
// Use the `constructor` property to simulate `Object#hasOwnProperty` in
// other environments.
isProperty = function (property) {
var parent = (this.constructor || constructor).prototype;
return property in this && !(property in parent && this[property] === parent[property]);
};
}
members = null;
return isProperty.call(this, property);
};
}
// Internal: Normalizes the `for...in` iteration algorithm across
// environments. Each enumerated key is yielded to a `callback` function.
forEach = function (object, callback) {
var size = 0, Properties, members, property, forEach;
// Tests for bugs in the current environment's `for...in` algorithm. The
// `valueOf` property inherits the non-enumerable flag from
// `Object.prototype` in older versions of IE, Netscape, and Mozilla.
(Properties = function () {
this.valueOf = 0;
}).prototype.valueOf = 0;
// Iterate over a new instance of the `Properties` class.
members = new Properties();
for (property in members) {
// Ignore all properties inherited from `Object.prototype`.
if (isProperty.call(members, property)) {
size++;
}
}
Properties = members = null;
// Normalize the iteration algorithm.
if (!size) {
// A list of non-enumerable properties inherited from `Object.prototype`.
members = ["valueOf", "toString", "toLocaleString", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "constructor"];
// IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable
// properties.
forEach = function (object, callback) {
var isFunction = getClass.call(object) == "[object Function]", property, length;
for (property in object) {
// Gecko <= 1.0 enumerates the `prototype` property of functions under
// certain conditions; IE does not.
if (!(isFunction && property == "prototype") && isProperty.call(object, property)) {
callback(property);
}
}
// Manually invoke the callback for each non-enumerable property.
for (length = members.length; property = members[--length]; isProperty.call(object, property) && callback(property));
};
} else if (size == 2) {
// Safari <= 2.0.4 enumerates shadowed properties twice.
forEach = function (object, callback) {
// Create a set of iterated properties.
var members = {}, isFunction = getClass.call(object) == "[object Function]", property;
for (property in object) {
// Store each property name to prevent double enumeration. The
// `prototype` property of functions is not enumerated due to cross-
// environment inconsistencies.
if (!(isFunction && property == "prototype") && !isProperty.call(members, property) && (members[property] = 1) && isProperty.call(object, property)) {
callback(property);
}
}
};
} else {
// No bugs detected; use the standard `for...in` algorithm.
forEach = function (object, callback) {
var isFunction = getClass.call(object) == "[object Function]", property, isConstructor;
for (property in object) {
if (!(isFunction && property == "prototype") && isProperty.call(object, property) && !(isConstructor = property === "constructor")) {
callback(property);
}
}
// Manually invoke the callback for the `constructor` property due to
// cross-environment inconsistencies.
if (isConstructor || isProperty.call(object, (property = "constructor"))) {
callback(property);
}
};
}
return forEach(object, callback);
};
// Public: Serializes a JavaScript `value` as a JSON string. The optional
// `filter` argument may specify either a function that alters how object and
// array members are serialized, or an array of strings and numbers that
// indicates which properties should be serialized. The optional `width`
// argument may be either a string or number that specifies the indentation
// level of the output.
if (!has("json-stringify")) {
// Internal: A map of control characters and their escaped equivalents.
Escapes = {
"\\": "\\\\",
'"': '\\"',
"\b": "\\b",
"\f": "\\f",
"\n": "\\n",
"\r": "\\r",
"\t": "\\t"
};
// Internal: Converts `value` into a zero-padded string such that its
// length is at least equal to `width`. The `width` must be <= 6.
toPaddedString = function (width, value) {
// The `|| 0` expression is necessary to work around a bug in
// Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`.
return ("000000" + (value || 0)).slice(-width);
};
// Internal: Double-quotes a string `value`, replacing all ASCII control
// characters (characters with code unit values between 0 and 31) with
// their escaped equivalents. This is an implementation of the
// `Quote(value)` operation defined in ES 5.1 section 15.12.3.
quote = function (value) {
var result = '"', index = 0, symbol;
for (; symbol = value.charAt(index); index++) {
// Escape the reverse solidus, double quote, backspace, form feed, line
// feed, carriage return, and tab characters.
result += '\\"\b\f\n\r\t'.indexOf(symbol) > -1 ? Escapes[symbol] :
// If the character is a control character, append its Unicode escape
// sequence; otherwise, append the character as-is.
(Escapes[symbol] = symbol < " " ? "\\u00" + toPaddedString(2, symbol.charCodeAt(0).toString(16)) : symbol);
}
return result + '"';
};
// Internal: Recursively serializes an object. Implements the
// `Str(key, holder)`, `JO(value)`, and `JA(value)` operations.
serialize = function (property, object, callback, properties, whitespace, indentation, stack) {
var value = object[property], className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, any, result;
if (typeof value == "object" && value) {
className = getClass.call(value);
if (className == "[object Date]" && !isProperty.call(value, "toJSON")) {
if (value > -1 / 0 && value < 1 / 0) {
// Dates are serialized according to the `Date#toJSON` method
// specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15
// for the ISO 8601 date time string format.
if (getDay) {
// Manually compute the year, month, date, hours, minutes,
// seconds, and milliseconds if the `getUTC*` methods are
// buggy. Adapted from @Yaffle's `date-shim` project.
date = floor(value / 864e5);
for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++);
for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++);
date = 1 + date - getDay(year, month);
// The `time` value specifies the time within the day (see ES
// 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used
// to compute `A modulo B`, as the `%` operator does not
// correspond to the `modulo` operation for negative numbers.
time = (value % 864e5 + 864e5) % 864e5;
// The hours, minutes, seconds, and milliseconds are obtained by
// decomposing the time within the day. See section 15.9.1.10.
hours = floor(time / 36e5) % 24;
minutes = floor(time / 6e4) % 60;
seconds = floor(time / 1e3) % 60;
milliseconds = time % 1e3;
} else {
year = value.getUTCFullYear();
month = value.getUTCMonth();
date = value.getUTCDate();
hours = value.getUTCHours();
minutes = value.getUTCMinutes();
seconds = value.getUTCSeconds();
milliseconds = value.getUTCMilliseconds();
}
// Serialize extended years correctly.
value = (year <= 0 || year >= 1e4 ? (year < 0 ? "-" : "+") + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) +
"-" + toPaddedString(2, month + 1) + "-" + toPaddedString(2, date) +
// Months, dates, hours, minutes, and seconds should have two
// digits; milliseconds should have three.
"T" + toPaddedString(2, hours) + ":" + toPaddedString(2, minutes) + ":" + toPaddedString(2, seconds) +
// Milliseconds are optional in ES 5.0, but required in 5.1.
"." + toPaddedString(3, milliseconds) + "Z";
} else {
value = null;
}
} else if (typeof value.toJSON == "function" && ((className != "[object Number]" && className != "[object String]" && className != "[object Array]") || isProperty.call(value, "toJSON"))) {
// Prototype <= 1.6.1 adds non-standard `toJSON` methods to the
// `Number`, `String`, `Date`, and `Array` prototypes. JSON 3
// ignores all `toJSON` methods on these objects unless they are
// defined directly on an instance.
value = value.toJSON(property);
}
}
if (callback) {
// If a replacement function was provided, call it to obtain the value
// for serialization.
value = callback.call(object, property, value);
}
if (value === null) {
return "null";
}
className = getClass.call(value);
if (className == "[object Boolean]") {
// Booleans are represented literally.
return "" + value;
} else if (className == "[object Number]") {
// JSON numbers must be finite. `Infinity` and `NaN` are serialized as
// `"null"`.
return value > -1 / 0 && value < 1 / 0 ? "" + value : "null";
} else if (className == "[object String]") {
// Strings are double-quoted and escaped.
return quote(value);
}
// Recursively serialize objects and arrays.
if (typeof value == "object") {
// Check for cyclic structures. This is a linear search; performance
// is inversely proportional to the number of unique nested objects.
for (length = stack.length; length--;) {
if (stack[length] === value) {
// Cyclic structures cannot be serialized by `JSON.stringify`.
throw TypeError();
}
}
// Add the object to the stack of traversed objects.
stack.push(value);
results = [];
// Save the current indentation level and indent one additional level.
prefix = indentation;
indentation += whitespace;
if (className == "[object Array]") {
// Recursively serialize array elements.
for (index = 0, length = value.length; index < length; any || (any = true), index++) {
element = serialize(index, value, callback, properties, whitespace, indentation, stack);
results.push(element === undef ? "null" : element);
}
result = any ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]";
} else {
// Recursively serialize object members. Members are selected from
// either a user-specified list of property names, or the object
// itself.
forEach(properties || value, function (property) {
var element = serialize(property, value, callback, properties, whitespace, indentation, stack);
if (element !== undef) {
// According to ES 5.1 section 15.12.3: "If `gap` {whitespace}
// is not the empty string, let `member` {quote(property) + ":"}
// be the concatenation of `member` and the `space` character."
// The "`space` character" refers to the literal space
// character, not the `space` {width} argument provided to
// `JSON.stringify`.
results.push(quote(property) + ":" + (whitespace ? " " : "") + element);
}
any || (any = true);
});
result = any ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}";
}
// Remove the object from the traversed object stack.
stack.pop();
return result;
}
};
// Public: `JSON.stringify`. See ES 5.1 section 15.12.3.
JSON3.stringify = function (source, filter, width) {
var whitespace, callback, properties, index, length, value;
if (typeof filter == "function" || typeof filter == "object" && filter) {
if (getClass.call(filter) == "[object Function]") {
callback = filter;
} else if (getClass.call(filter) == "[object Array]") {
// Convert the property names array into a makeshift set.
properties = {};
for (index = 0, length = filter.length; index < length; value = filter[index++], ((getClass.call(value) == "[object String]" || getClass.call(value) == "[object Number]") && (properties[value] = 1)));
}
}
if (width) {
if (getClass.call(width) == "[object Number]") {
// Convert the `width` to an integer and create a string containing
// `width` number of space characters.
if ((width -= width % 1) > 0) {
for (whitespace = "", width > 10 && (width = 10); whitespace.length < width; whitespace += " ");
}
} else if (getClass.call(width) == "[object String]") {
whitespace = width.length <= 10 ? width : width.slice(0, 10);
}
}
// Opera <= 7.54u2 discards the values associated with empty string keys
// (`""`) only if they are used directly within an object member list
// (e.g., `!("" in { "": 1})`).
return serialize("", (value = {}, value[""] = source, value), callback, properties, whitespace, "", []);
};
}
// Public: Parses a JSON source string.
if (!has("json-parse")) {
fromCharCode = String.fromCharCode;
// Internal: A map of escaped control characters and their unescaped
// equivalents.
Unescapes = {
"\\": "\\",
'"': '"',
"/": "/",
"b": "\b",
"t": "\t",
"n": "\n",
"f": "\f",
"r": "\r"
};
// Internal: Resets the parser state and throws a `SyntaxError`.
abort = function() {
Index = Source = null;
throw SyntaxError();
};
// Internal: Returns the next token, or `"$"` if the parser has reached
// the end of the source string. A token may be a string, number, `null`
// literal, or Boolean literal.
lex = function () {
var source = Source, length = source.length, symbol, value, begin, position, sign;
while (Index < length) {
symbol = source.charAt(Index);
if ("\t\r\n ".indexOf(symbol) > -1) {
// Skip whitespace tokens, including tabs, carriage returns, line
// feeds, and space characters.
Index++;
} else if ("{}[]:,".indexOf(symbol) > -1) {
// Parse a punctuator token at the current position.
Index++;
return symbol;
} else if (symbol == '"') {
// Advance to the next character and parse a JSON string at the
// current position. String tokens are prefixed with the sentinel
// `@` character to distinguish them from punctuators.
for (value = "@", Index++; Index < length;) {
symbol = source.charAt(Index);
if (symbol < " ") {
// Unescaped ASCII control characters are not permitted.
abort();
} else if (symbol == "\\") {
// Parse escaped JSON control characters, `"`, `\`, `/`, and
// Unicode escape sequences.
symbol = source.charAt(++Index);
if ('\\"/btnfr'.indexOf(symbol) > -1) {
// Revive escaped control characters.
value += Unescapes[symbol];
Index++;
} else if (symbol == "u") {
// Advance to the first character of the escape sequence.
begin = ++Index;
// Validate the Unicode escape sequence.
for (position = Index + 4; Index < position; Index++) {
symbol = source.charAt(Index);
// A valid sequence comprises four hexdigits that form a
// single hexadecimal value.
if (!(symbol >= "0" && symbol <= "9" || symbol >= "a" && symbol <= "f" || symbol >= "A" && symbol <= "F")) {
// Invalid Unicode escape sequence.
abort();
}
}
// Revive the escaped character.
value += fromCharCode("0x" + source.slice(begin, Index));
} else {
// Invalid escape sequence.
abort();
}
} else {
if (symbol == '"') {
// An unescaped double-quote character marks the end of the
// string.
break;
}
// Append the original character as-is.
value += symbol;
Index++;
}
}
if (source.charAt(Index) == '"') {
Index++;
// Return the revived string.
return value;
}
// Unterminated string.
abort();
} else {
// Parse numbers and literals.
begin = Index;
// Advance the scanner's position past the sign, if one is
// specified.
if (symbol == "-") {
sign = true;
symbol = source.charAt(++Index);
}
// Parse an integer or floating-point value.
if (symbol >= "0" && symbol <= "9") {
// Leading zeroes are interpreted as octal literals.
if (symbol == "0" && (symbol = source.charAt(Index + 1), symbol >= "0" && symbol <= "9")) {
// Illegal octal literal.
abort();
}
sign = false;
// Parse the integer component.
for (; Index < length && (symbol = source.charAt(Index), symbol >= "0" && symbol <= "9"); Index++);
// Floats cannot contain a leading decimal point; however, this
// case is already accounted for by the parser.
if (source.charAt(Index) == ".") {
position = ++Index;
// Parse the decimal component.
for (; position < length && (symbol = source.charAt(position), symbol >= "0" && symbol <= "9"); position++);
if (position == Index) {
// Illegal trailing decimal.
abort();
}
Index = position;
}
// Parse exponents.
symbol = source.charAt(Index);
if (symbol == "e" || symbol == "E") {
// Skip past the sign following the exponent, if one is
// specified.
symbol = source.charAt(++Index);
if (symbol == "+" || symbol == "-") {
Index++;
}
// Parse the exponential component.
for (position = Index; position < length && (symbol = source.charAt(position), symbol >= "0" && symbol <= "9"); position++);
if (position == Index) {
// Illegal empty exponent.
abort();
}
Index = position;
}
// Coerce the parsed value to a JavaScript number.
return +source.slice(begin, Index);
}
// A negative sign may only precede numbers.
if (sign) {
abort();
}
// `true`, `false`, and `null` literals.
if (source.slice(Index, Index + 4) == "true") {
Index += 4;
return true;
} else if (source.slice(Index, Index + 5) == "false") {
Index += 5;
return false;
} else if (source.slice(Index, Index + 4) == "null") {
Index += 4;
return null;
}
// Unrecognized token.
abort();
}
}
// Return the sentinel `$` character if the parser has reached the end
// of the source string.
return "$";
};
// Internal: Parses a JSON `value` token.
get = function (value) {
var results, any, key;
if (value == "$") {
// Unexpected end of input.
abort();
}
if (typeof value == "string") {
if (value.charAt(0) == "@") {
// Remove the sentinel `@` character.
return value.slice(1);
}
// Parse object and array literals.
if (value == "[") {
// Parses a JSON array, returning a new JavaScript array.
results = [];
for (;; any || (any = true)) {
value = lex();
// A closing square bracket marks the end of the array literal.
if (value == "]") {
break;
}
// If the array literal contains elements, the current token
// should be a comma separating the previous element from the
// next.
if (any) {
if (value == ",") {
value = lex();
if (value == "]") {
// Unexpected trailing `,` in array literal.
abort();
}
} else {
// A `,` must separate each array element.
abort();
}
}
// Elisions and leading commas are not permitted.
if (value == ",") {
abort();
}
results.push(get(value));
}
return results;
} else if (value == "{") {
// Parses a JSON object, returning a new JavaScript object.
results = {};
for (;; any || (any = true)) {
value = lex();
// A closing curly brace marks the end of the object literal.
if (value == "}") {
break;
}
// If the object literal contains members, the current token
// should be a comma separator.
if (any) {
if (value == ",") {
value = lex();
if (value == "}") {
// Unexpected trailing `,` in object literal.
abort();
}
} else {
// A `,` must separate each object member.
abort();
}
}
// Leading commas are not permitted, object property names must be
// double-quoted strings, and a `:` must separate each property
// name and value.
if (value == "," || typeof value != "string" || value.charAt(0) != "@" || lex() != ":") {
abort();
}
results[value.slice(1)] = get(lex());
}
return results;
}
// Unexpected token encountered.
abort();
}
return value;
};
// Internal: Updates a traversed object member.
update = function(source, property, callback) {
var element = walk(source, property, callback);
if (element === undef) {
delete source[property];
} else {
source[property] = element;
}
};
// Internal: Recursively traverses a parsed JSON object, invoking the
// `callback` function for each value. This is an implementation of the
// `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2.
walk = function (source, property, callback) {
var value = source[property], length;
if (typeof value == "object" && value) {
if (getClass.call(value) == "[object Array]") {
for (length = value.length; length--;) {
update(value, length, callback);
}
} else {
// `forEach` can't be used to traverse an array in Opera <= 8.54,
// as `Object#hasOwnProperty` returns `false` for array indices
// (e.g., `![1, 2, 3].hasOwnProperty("0")`).
forEach(value, function (property) {
update(value, property, callback);
});
}
}
return callback.call(source, property, value);
};
// Public: `JSON.parse`. See ES 5.1 section 15.12.2.
JSON3.parse = function (source, callback) {
var result, value;
Index = 0;
Source = source;
result = get(lex());
// If a JSON string contains multiple tokens, it is invalid.
if (lex() != "$") {
abort();
}
// Reset the parser state.
Index = Source = null;
return callback && getClass.call(callback) == "[object Function]" ? walk((value = {}, value[""] = result, value), "", callback) : result;
};
}
}
// Export for asynchronous module loaders.
if (isLoader) {
define(function () {
return JSON3;
});
}
}).call(this);
\ No newline at end of file
/** /**
* XHR polyfill / shims * XHR polyfill / shims
* *
* (c) copyright 2011-2012 Brian Cavalier and John Hann * (c) copyright 2011-2013 Brian Cavalier and John Hann
* *
* This module is part of the cujo.js family of libraries (http://cujojs.com/). * This module is part of the cujo.js family of libraries (http://cujojs.com/).
* *
...@@ -9,34 +9,31 @@ ...@@ -9,34 +9,31 @@
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
* *
*/ */
define(['./lib/_base'], function (base) { define(function () {
var progIds, xhrCtor; var progIds;
progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
// find XHR implementation // find XHR implementation
if (typeof XMLHttpRequest != 'undefined') { if (typeof XMLHttpRequest == 'undefined') {
xhrCtor = XMLHttpRequest; // create xhr impl that will fail if called.
assignCtor(function () { throw new Error("poly/xhr: XMLHttpRequest not available"); });
// keep trying progIds until we find the correct one,
progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
while (progIds.length && tryProgId(progIds.shift())) {}
} }
else {
var noXhr; function assignCtor (ctor) {
// keep trying progIds until we find the correct one, then rewrite the getXhr method // assign window.XMLHttpRequest function
// to always return that one. window.XMLHttpRequest = ctor;
noXhr = xhrCtor = function () {
throw new Error("poly/xhr: XMLHttpRequest not available");
};
while (progIds.length > 0 && xhrCtor == noXhr) (function (progId) {
try {
new ActiveXObject(progId);
xhrCtor = function () { return new ActiveXObject(progId); };
}
catch (ex) {}
}(progIds.shift()));
} }
if (!window.XMLHttpRequest) { function tryProgId (progId) {
window.XMLHttpRequest = xhrCtor; try {
new ActiveXObject(progId);
assignCtor(function () { return new ActiveXObject(progId); });
return true;
}
catch (ex) {}
} }
}); });
...@@ -136,10 +136,6 @@ ...@@ -136,10 +136,6 @@
} }
function getFile(file, callback) { function getFile(file, callback) {
if (!location.host) {
return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.');
}
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.open('GET', findRoot() + file, true); xhr.open('GET', findRoot() + file, true);
......
.npmignore
.idea/
experiments/
node_modules/
.externs.js
{
"browser": true,
"node": true,
"es5": true,
"predef": [
"define",
"module",
"system"
],
"boss": true,
"curly": true,
"eqnull": true,
"expr": true,
"globalstrict": false,
"laxbreak": true,
"newcap": true,
"noarg": true,
"noempty": true,
"nonew": true,
"quotmark": "single",
"strict": false,
"trailing": true,
"undef": true,
"unused": true,
"maxdepth": 3,
"maxcomplexity": 5
}
\ No newline at end of file
language: node_js
node_js:
- 0.8
script: npm run-script ci
branches:
only:
- dev
- master
env:
global:
- SELENIUM_USERNAME="cujojs-when"
- secure: "Vja3i39gQE7KnDMbIvblwJAfQK5ydeA6NKsAULQ//nvuV9muNeKgIxIMMPDw\nttjS6CY75how9tt87WSJQur/8PaSx+dj6RPvok4sf8DWgMMz97oI8e2l72wI\nIiHK1+btvI4/KG3QHLGCOJ8IxJA3Oluzo6ZDvRKXAvJoIrjmJjY="
\ No newline at end of file
### 2.1.0
* New [`when.settle`](docs/api.md#whensettle) that settles an array of promises, regardless of whether the fulfill or reject.
* New [`when/guard`](docs/api.md#whenguard) generalized concurrency guarding and limiting
* New [`promise.inspect`](docs/api.md#inspect) for synchronously getting a snapshot of a promise's state at a particular instant.
* Significant performance improvements when resolving promises with non-primitives (Arrays, Objects, etc.)
* Experimental [vert.x](http://vertx.io) support
* **DEPRECATED**: `onFulfilled`, `onRejected`, `onProgress` handler arguments to `when.all`, `when.any`, `when.some`. Use the returned promise's `then()` (or `otherwise()`, `ensure()`, etc) to register handlers instead.
* For example, do this: `when.all(array).then(onFulfilled, onRejected)` instead of this: `when.all(array, onFulfilled, onRejected)`. The functionality is equivalent.
### 2.0.1
* Account for the fact that Mocha creates a global named `process`. Thanks [Narsul](https://github.com/cujojs/when/pull/136)
### 2.0.0
* Fully asynchronous resolutions.
* [Promises/A+](http://promises-aplus.github.com/promises-spec) compliance.
* New [`when/keys`](docs/api.md#object-keys) module with `all()` and `map()` for object keys/values.
* New [`promise.ensure`](docs/api.md#ensure) as a better, and safer, replacement for `promise.always`. [See discussion](https://github.com/cujojs/when/issues/103) as to why `promise.always` is mistake-prone.
* **DEPRECATED:** `promise.always`
* `lift()` is now the preferred name for what was `bind()` in [when/function](docs/api.md#synchronous-functions), [when/node/function](docs/api.md#node-style-asynchronous-functions), and [when/callbacks](docs/api.md#asynchronous-functions).
* **DEPRECATED:** `bind()` in `when/function`, `when/node/function`, and `when/callbacks`. Use `lift()` instead.
### 1.8.1
* Last 1.x.x release before 2.0.0 barring critical fixes.
* To prepare for 2.0.0, [test your code against the dev-200 branch](https://github.com/cujojs/when/tree/dev-200). It is fully API compatible, but has fully asynchronous resolutions.
* Performance improvements for [when/function](docs/api.md#synchronous-functions).
* [Documentation](docs/api.md) updates and fixes. Thanks, [@unscriptable](https://github.com/unscriptable)!
* **DEPRECATED:** `deferred.progress` and `deferred.resolver.progress`. Use [`deferred.notify`](docs/api.md#progress-events) and [`deferred.resolver.notify`](docs/api.md#progress-events) instead.
* **DEPRECATED:** [`when.chain`](docs/api.md#whenchain). Use [`resolver.resolve(promise)`](docs/api.md#resolver) or `resolver.resolve(promise.yield)` ([see `promise.yield`](docs/api.md#yield)) instead.
* **DEPRECATED:** `when/timed` module. Use [`when/delay`](docs/api.md#whendelay) and [`when/timeout`](docs/api.md#whentimeout) modules instead.
### 1.8.0
* New [when/function](docs/api.md#synchronous-functions), [when/node/function](docs/api.md#node-style-asynchronous-functions), and [when/callbacks](docs/api.md#asynchronous-functions) with functional programming goodness, and adapters for turning callback-based APIs into promise-based APIs. Kudos [@riccieri](https://github.com/riccieri)!
* New [when/unfold](docs/api.md#whenunfold), and [when/unfold/list](docs/api.md#whenunfoldlist) promise-aware anamorphic unfolds that can be used to generate and/or process unbounded lists.
* New [when/poll](docs/api.md#whenpoll) promise-based periodic polling and task execution. Kudos [@scothis](https://github.com/scothis)!
### 1.7.1
* Removed leftover internal usages of `deferred.then`.
* [when/debug](https://github.com/cujojs/when/wiki/when-debug) allows configuring the set of "fatal" error types that will be rethrown to the host env.
### 1.7.0
* **DEPRECATED:** `deferred.then` [is deprecated](docs/api.md#deferred) and will be removed in an upcoming release. Use `deferred.promise.then` instead.
* [promise.yield](docs/api.md#yield)(promiseOrValue) convenience API for substituting a new value into a promise chain.
* [promise.spread](docs/api.md#spread)(variadicFunction) convenience API for spreading an array onto a fulfill handler that accepts variadic arguments. [Mmmm, buttery](http://s.shld.net/is/image/Sears/033W048977110001_20100422100331516?hei=1600&wid=1600&op_sharpen=1&resMode=sharp&op_usm=0.9,0.5,0,0)
* Doc improvements:
* [when()](docs/api.md#when) and [promise.then()](docs/api.md#main-promise-api) have more info about callbacks and chaining behavior.
* More info and clarifications about the roles of [Deferred](docs/api.md#deferred) and [Resolver](docs/api.md#resolver)
* Several minor clarifications for various APIs
* Internal improvements to assimilation and interoperability with other promise implementations.
### 1.6.1
* Fix for accidental coercion of non-promises. See [#62](https://github.com/cujojs/when/issues/60).
### 1.6.0
* New [when.join](docs/api.md#whenjoin) - Joins 2 or more promises together into a single promise.
* [when.some](docs/api.md#whensome) and [when.any](docs/api.md#whenany) now act like competitive races, and have generally more useful behavior. [Read the discussion in #60](https://github.com/cujojs/when/issues/60).
* *Experimental* progress event propagation. Progress events will propagate through promise chains. [Read the details here](docs/api.md#progress-events).
* *Temporarily* removed calls to `Object.freeze`. Promises are no longer frozen due to a horrendous v8 performance penalty. [Read discussion here](https://groups.google.com/d/topic/cujojs/w_olYqorbsY/discussion).
* **IMPORTANT:** Continue to treat promises as if they are frozen, since `freeze()` will be reintroduced once v8 performance improves.
* [when/debug](https://github.com/cujojs/when/wiki/when-debug) now allows setting global a debugging callback for rejected promises.
### 1.5.2
* Integrate @domenic's [Promises/A Test Suite](https://github.com/domenic/promise-tests). Runs via `npm test`.
* No functional change
### 1.5.1
* Performance optimization for [when.defer](docs/api.md#whendefer), up to 1.5x in some cases.
* [when/debug](docs/api.md#whendebug) can now log exceptions and rejections in deeper promise chains, in some cases, even when the promises involved aren't when.js promises.
### 1.5.0
* New task execution and concurrency management: [when/sequence](docs/api.md#whensequence), [when/pipeline](docs/api.md#whenpipeline), and [when/parallel](docs/api.md#whenparallel).
* Performance optimizations for [when.all](docs/api.md#whenall) and [when.map](docs/api.md#whenmap), up to 2x in some cases.
* Options for disabling [paranoid mode](docs/api.md#paranoid-mode) that provides a significant performance gain in v8 (e.g. Node and Chrome). See this [v8 performance problem with Object.freeze](http://stackoverflow.com/questions/8435080/any-performance-benefit-to-locking-down-javascript-objects) for more info.
* **Important:** `deferred` and `deferred.resolver` no longer throw when resolved/rejected multiple times. They will return silently as if the they had succeeded. This prevents parties to whom *only* the `resolver` has been given from using `try/catch` to determine the state of the associated promise.
* For debugging, you can use the [when/debug](https://github.com/cujojs/when/wiki/when-debug) module, which will still throw when a deferred is resolved/rejected multiple times.
### 1.4.4
* Change UMD boilerplate to check for `exports` to avoid a problem with QUnit. See [#54](https://github.com/cujojs/when/issues/54) for more info.
### 1.4.3
* Fix for infinite promise coercion between when.js and Q (See [#50](https://github.com/cujojs/when/issues/50)). Thanks [@kriskowal](https://github.com/kriskowal) and [@domenic](https://github.com/domenic)
### 1.4.2
* Fix for IE8 infinite recursion (See [#49](https://github.com/cujojs/when/issues/49))
### 1.4.1
* Code and unit test cleanup and streamlining--no functional changes.
### 1.4.0
* Create a resolved promise: `when.resolve(value)` creates a resolved promise for `value`. See [API docs](docs/api.md#whenresolve).
* Resolve/reject return something useful: `deferred.resolve` and `deferred.reject` now return a promise for the fulfilled or rejected value.
* Resolve a deferred with another promise: `deferred.resolve(promise)` - when `promise` resolves or rejects, so will `deferred`.
### 1.3.0
* Fixed a deviation from the Promises/A spec where returning undefined from a callback or errback would cause the previous value to be forwarded. See [#31](https://github.com/cujojs/when/issues/31)
* *This could be a breaking change* if you depended on this behavior. If you encounter problems, the solution is to ensure that your promise callbacks (registered either with `when()` or `.then()`) return what you intend, keeping in mind that not returning something is equivalent to returning `undefined`.
* This change also restores compatibility with the promises returned by `jQuery.get()`, which seem to reject with themselves as the rejection value. See [issue #41](https://github.com/cujojs/when/issues/43) for more information and discussion. Thanks to [@KidkArolis](https://github.com/KidkArolis) for raising the issue.
### 1.2.0
* `promise.otherwise(errback)` as a shortcut for `promise.then(null, errback)`. See discussion [here](https://github.com/cujojs/when/issues/13) and [here](https://github.com/cujojs/when/issues/29). Thanks to [@jonnyreeves](https://github.com/jonnyreeves/) for suggesting the name "otherwise".
* [when/debug](https://github.com/cujojs/when/wiki/when-debug) now detects exceptions that typically represent coding errors, such as SyntaxError, ReferenceError, etc. and propagates them to the host environment. In other words, you'll get a very loud stack trace.
### 1.1.1
* Updated [wiki](https://github.com/cujojs/when/wiki) map/reduce examples, and added simple promise forwarding example
* Fix for calling `when.any()` without a callback ([#33](https://github.com/cujojs/when/issues/33))
* Fix version number in `when.js` source ([#36](https://github.com/cujojs/when/issues/36))
### 1.1.0
* `when.all/any/some/map/reduce` can all now accept a promise for an array in addition to an actual array as input. This allows composing functions to do interesting things like `when.reduce(when.map(...))`
* `when.reject(promiseOrValue)` that returns a new, rejected promise.
* `promise.always(callback)` as a shortcut for `promise.then(callback, callback)`
* **Highly experimental** [when/debug](https://github.com/cujojs/when/wiki/when-debug) module: a drop-in replacement for the main `when` module that enables debug logging for promises created or consumed by when.js
### 1.0.4
* [Travis CI](http://travis-ci.org/cujojs/when) integration
* Fix for cancelable deferred not invoking progress callbacks. ([#24](https://github.com/cujojs/when/pull/24) Thanks [@scothis](https://github.com/scothis))
* The promise returned by `when.chain` now rejects when the input promise rejects.
### 1.0.3
* Fix for specific situation where `null` could incorrectly be used as a promise resolution value ([#23](https://github.com/cujojs/when/pull/23))
### 1.0.2
* Updated README for running unit tests in both Node and Browsers. See **Running the Unit Tests** below.
* Set package name to 'when' in package.json
### 1.0.1
* Fix for rejections propagating in some cases when they shouldn't have been ([#19](https://github.com/cujojs/when/issues/19))
* Using [buster.js](http://busterjs.org/) for unit tests now.
### 1.0.0
* First official when.js release as a part of [cujojs](https://github.com/cujojs).
* Added [when/cancelable](https://github.com/cujojs/when/wiki/when-cancelable) decorator for creating cancelable deferreds
* Added [when/delay](https://github.com/cujojs/when/wiki/when-delay) and [when/timeout](https://github.com/cujojs/when/wiki/when-timeout) helpers for creating delayed promises and promises that timeout and reject if not resolved first.
### 0.11.1
* Added [when/apply](https://github.com/cujojs/when/wiki/when-apply) helper module for using arguments-based and variadic callbacks with `when.all`, `when.some`, `when.map`, or any promise that resolves to an array. ([#14](https://github.com/cujojs/when/issues/14))
* `.then()`, `when()`, and all other methods that accept callback/errback/progress handlers will throw if you pass something that's not a function. ([#15](https://github.com/cujojs/when/issues/15))
### 0.11.0
* `when.js` now *assimilates* thenables that pass the [Promises/A duck-type test](http://wiki.commonjs.org/wiki/Promises/A), but which may not be fully Promises/A compliant, such as [jQuery's Deferred](http://api.jquery.com/category/deferred-object/) and [curl's global API](https://github.com/cujojs/curl) (See the **API at a glance** section)
* `when()`, and `when.all/some/any/map/reduce/chain()` are all now guaranteed to return a fully Promises/A compliant promise, even when their input is not compliant.
* Any non-compliant thenable returned by a callback or errback will also be assimilated to protect subsequent promises and callbacks in a promise chain, and preserve Promises/A forwarding guarantees.
### 0.10.4
* **Important Fix for some AMD build/optimizer tools**: Switching back to more verbose, builder-friendly boilerplate
* If you are using when.js 0.10.3 with the dojo or RequireJS build tools, you should update to v.10.4 as soon as possible.
### 0.10.3
**Warning**: This version will not work with most AMD build tools. You should update to 0.10.4 as soon as possible.
* Minor `package.json` updates
* Slightly smaller module boilerplate
### 0.10.2
* Performance optimizations for `when.map()` (thanks @[smitranic](https://github.com/smitranic)), especially for large arrays where the `mapFunc` is also async (i.e. returns a promise)
* `when.all/some/any/map/reduce` handle sparse arrays (thanks @[rwaldrn](https://github.com/rwldrn/))
* Other minor performance optimizations
### 0.10.1
* Minor tweaks (thanks @[johan](https://github.com/johan))
* Add missing semis that WebStorm didn't catch
* Fix DOH submodule ref, and update README with info for running unit tests
### 0.10.0
* `when.map` and `when.reduce` - just like Array.map and Array.reduce, but they operate on promises and arrays of promises
* Lots of internal size and performance optimizations
* Still only 1k!
### 0.9.4
* Important fix for break in promise chains
<a href="http://promises-aplus.github.com/promises-spec"><img src="http://promises-aplus.github.com/promises-spec/assets/logo-small.png" alt="Promises/A+ logo" align="right" /></a>
[![Build Status](https://secure.travis-ci.org/cujojs/when.png)](http://travis-ci.org/cujojs/when)
# when.js
When.js is cujoJS's lightweight [Promises/A+](http://promises-aplus.github.com/promises-spec) and `when()` implementation that powers the async core of [wire.js](https://github.com/cujojs/wire), cujoJS's IOC Container. It features:
* A rock solid, battle-tested Promise implementation
* Resolving, settling, mapping, and reducing arrays of promises
* Executing tasks in parallel and sequence
* Transforming Node-style and other callback-based APIs into promise-based APIs
It passes the [Promises/A+ Test Suite](https://github.com/promises-aplus/promises-tests), is [very fast](https://github.com/cujojs/promise-perf-tests#test-results), is under 1.5k when compiled with Google Closure + gzip, and has no external dependencies.
# What's New?
### 2.1.0
* New [`when.settle`](docs/api.md#whensettle) that settles an array of promises
* New [`when/guard`](docs/api.md#whenguard) generalized concurrency guarding and limiting
* New [`promise.inspect`](docs/api.md#inspect) for synchronously getting a snapshot of a promise's state at a particular instant.
* Significant performance improvements when resolving promises with non-primitives (e.g. with Arrays, Objects, etc.)
* Experimental [vert.x](http://vertx.io) support
* **DEPRECATED**: `onFulfilled`, `onRejected`, `onProgress` handler arguments to `when.all`, `when.any`, `when.some`. Use the returned promise's `then()` (or `otherwise()`, `ensure()`, etc) to register handlers instead.
* For example, do this: `when.all(array).then(onFulfilled, onRejected)` instead of this: `when.all(array, onFulfilled, onRejected)`. The functionality is equivalent.
### 2.0.1
* Account for the fact that Mocha creates a global named `process`. Thanks [Narsul](https://github.com/cujojs/when/pull/136)
### 2.0.0
* Fully asynchronous resolutions.
* [Promises/A+](http://promises-aplus.github.com/promises-spec) compliance.
* New [`when/keys`](docs/api.md#object-keys) module with `all()` and `map()` for object keys/values.
* New [`promise.ensure`](docs/api.md#ensure) as a better, and safer, replacement for `promise.always`. [See discussion](https://github.com/cujojs/when/issues/103) as to why `promise.always` is mistake-prone.
* **DEPRECATED:** `promise.always`
* `lift()` is now the preferred name for what was `bind()` in [when/function](docs/api.md#synchronous-functions), [when/node/function](docs/api.md#node-style-asynchronous-functions), and [when/callbacks](docs/api.md#asynchronous-functions).
* **DEPRECATED:** `bind()` in `when/function`, `when/node/function`, and `when/callbacks`. Use `lift()` instead.
[Full Changelog](CHANGES.md)
# Docs & Examples
[API docs](docs/api.md#api)
[More info on the wiki](https://github.com/cujojs/when/wiki)
[Examples](https://github.com/cujojs/when/wiki/Examples)
Quick Start
===========
### AMD
1. Get it
- `bower install when` or `yeoman install when`, *or*
- `git clone https://github.com/cujojs/when` or `git submodule add https://github.com/cujojs/when`
1. Configure your loader with a package:
```js
packages: [
{ name: 'when', location: 'path/to/when/', main: 'when' },
// ... other packages ...
]
```
1. `define(['when', ...], function(when, ...) { ... });` or `require(['when', ...], function(when, ...) { ... });`
### Node
1. `npm install when`
1. `var when = require('when');`
### RingoJS
1. `ringo-admin install cujojs/when`
1. `var when = require('when');`
### Legacy environments
1. `git clone https://github.com/cujojs/when` or `git submodule add https://github.com/cujojs/when`
1. Add a transient `define` shim, and a `<script>` element for when.js
```html
<script>
window.define = function(factory) {
try{ delete window.define; } catch(e){ window.define = void 0; } // IE
window.when = factory();
};
window.define.amd = {};
</script>
<script src="path/to/when/when.js"></script>
```
1. `when` will be available as `window.when`
# Running the Unit Tests
## Node
Note that when.js includes the [Promises/A+ Test Suite](https://github.com/promises-aplus/promise-tests). Running unit tests in Node will run both when.js's own test suite, and the Promises/A+ Test Suite.
1. `npm install`
1. `npm test`
## Browsers
1. `npm install`
1. `npm start` - starts buster server & prints a url
1. Point browsers at <buster server url>/capture, e.g. `localhost:1111/capture`
1. `npm run-script test-browser`
References
----------
Much of this code was inspired by the async innards of [wire.js](https://github.com/cujojs/wire), and has been influenced by the great work in [Q](https://github.com/kriskowal/q), [Dojo's Deferred](https://github.com/dojo/dojo), and [uber.js](https://github.com/phiggins42/uber.js).
{
"name": "when",
"version": "2.1.0",
"repository": {
"type": "git",
"url": "git://github.com/cujojs/when.git"
}
}
\ No newline at end of file
{
"name": "when",
"version": "2.1.0",
"description": "A lightweight Promises/A+ and when() implementation, plus other async goodies.",
"keywords": ["Promises/A+", "promises-aplus", "promise", "promises", "deferred", "deferreds", "when", "async", "asynchronous", "cujo"],
"homepage": "http://cujojs.com",
"licenses": [
{
"type": "MIT",
"url": "http://www.opensource.org/licenses/mit-license.php"
}
],
"repositories": [
{
"type": "git",
"url": "https://github.com/cujojs/when"
}
],
"bugs": "https://github.com/cujojs/when/issues",
"maintainers": [
{
"name": "Brian Cavalier",
"web": "http://hovercraftstudios.com"
},
{
"name": "John Hann",
"web": "http://unscriptable.com"
}
],
"contributors": [
{
"name": "Brian Cavalier",
"web": "http://hovercraftstudios.com"
},
{
"name": "John Hann",
"web": "http://unscriptable.com"
},
{
"name": "Scott Andrews"
}
],
"devDependencies": {
"curl": "https://github.com/cujojs/curl/tarball/0.7.3",
"test-support": "~0.2",
"promises-aplus-tests": "~1"
},
"main": "when",
"directories": {
"test": "test"
},
"scripts": {
"test": "jshint . && buster test -e node && promises-aplus-tests test/promises-aplus-adapter.js",
"ci": "npm test && sauceme",
"start": "buster static -e browser"
}
}
\ No newline at end of file
...@@ -25,7 +25,7 @@ define(function(require) { ...@@ -25,7 +25,7 @@ define(function(require) {
return function list(generator, condition, seed) { return function list(generator, condition, seed) {
var result = []; var result = [];
return unfold(generator, condition, append, seed).yield(result); return unfold(generator, condition, append, seed)['yield'](result);
function append(value, newSeed) { function append(value, newSeed) {
result.push(value); result.push(value);
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* @author Brian Cavalier * @author Brian Cavalier
* @author John Hann * @author John Hann
* @version 2.1.0 * @version 2.1.1
*/ */
(function(define, global) { 'use strict'; (function(define, global) { 'use strict';
define(function () { define(function () {
...@@ -88,7 +88,7 @@ define(function () { ...@@ -88,7 +88,7 @@ define(function () {
* @returns {Promise} * @returns {Promise}
*/ */
ensure: function(onFulfilledOrRejected) { ensure: function(onFulfilledOrRejected) {
return this.then(injectHandler, injectHandler).yield(this); return this.then(injectHandler, injectHandler)['yield'](this);
function injectHandler() { function injectHandler() {
return resolve(onFulfilledOrRejected()); return resolve(onFulfilledOrRejected());
......
root = true
[*]
indent_style = tab
indent_size = 4
end_of_line = LF
\ No newline at end of file
node_modules/
experiments/
.npmignore
experiments/
*~
.idea/
tools/
curl/
test/lib
!test/dojo
!wire/dojo
dijit/
dojo.js
bin/
jquery.js
\ No newline at end of file
[submodule "test/util"]
path = test/util
url = https://github.com/dojo/util.git
[submodule "test/curl"]
path = test/curl
url = git://github.com/cujojs/curl.git
[submodule "support/sizzle"]
path = support/sizzle
url = https://github.com/cujojs/sizzle.git
[submodule "support/meld"]
path = support/meld
url = https://github.com/cujojs/meld.git
[submodule "support/when"]
path = support/when
url = git://github.com/cujojs/when.git
[submodule "test/requirejs"]
path = test/requirejs
url = https://github.com/jrburke/requirejs.git
[submodule "test/requirejs-domReady"]
path = test/requirejs-domReady
url = https://github.com/requirejs/domReady.git
[submodule "support/poly"]
path = support/poly
url = git://github.com/cujojs/poly.git
{
// Settings
"passfail" : false, // Stop on first error.
"maxerr" : 20, // Maximum error before stopping.
// Predefined globals whom JSHint will ignore.
"browser" : true, // Standard browser globals e.g. `window`, `document`.
"node" : true,
"rhino" : false,
"couch" : false,
"wsh" : false, // Windows Scripting Host.
"jquery" : false,
"prototypejs" : false,
"mootools" : false,
"dojo" : false,
"predef" : [ // Custom globals.
"define",
"module"
],
// Development.
"debug" : false, // Allow debugger statements e.g. browser breakpoints.
"devel" : false, // Allow developments statements e.g. `console.log();`.
// ECMAScript 5.
"es5" : false, // Allow ECMAScript 5 syntax.
"strict" : false, // Require `use strict` pragma in every file.
"globalstrict" : false, // Allow global "use strict" (also enables 'strict').
// The Good Parts.
"asi" : false, // Tolerate Automatic Semicolon Insertion (no semicolons).
"laxbreak" : true, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons.
"bitwise" : false, // Prohibit bitwise operators (&, |, ^, etc.).
"boss" : true, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments.
"curly" : true, // Require {} for every new block or scope.
"eqeqeq" : false, // Require triple equals i.e. `===`.
"eqnull" : true, // Tolerate use of `== null`.
"evil" : false, // Tolerate use of `eval`.
"expr" : true, // Tolerate `ExpressionStatement` as Programs.
"forin" : false, // Tolerate `for in` loops without `hasOwnPrototype`.
"immed" : false, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
"latedef" : false, // Prohibit variable use before definition.
"loopfunc" : false, // Allow functions to be defined within loops.
"noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`.
"nonstandard" : false, // Defines non-standard but widely adopted globals such as escape and unescape.
"regexp" : false, // Prohibit `.` and `[^...]` in regular expressions.
"regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`.
"scripturl" : true, // Tolerate script-targeted URLs.
"shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`.
"supernew" : false, // Tolerate `new function () { ... };` and `new Object;`.
"undef" : true, // Require all non-global variables be declared before they are used.
// Personal styling preferences.
"newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`.
"noempty" : false, // Prohibit use of empty blocks.
"nonew" : true, // Prohibit use of constructors for side-effects.
"nomen" : false, // Prohibit use of initial or trailing underbars in names.
"onevar" : false, // Allow only one `var` statement per function.
"plusplus" : false, // Prohibit use of `++` & `--`.
"sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`.
"trailing" : true, // Prohibit trailing whitespaces.
"white" : false, // Check against strict whitespace and indentation rules.
"indent" : 4, // Specify indentation spacing
"smarttabs" : true // Suppresses warnings about mixed tabs and spaces when the latter are used for alignmnent only
}
\ No newline at end of file
# wire.js
Wire is an [Inversion of Control Container](http://martinfowler.com/articles/injection.html "Inversion of Control Containers and the Dependency Injection pattern") for Javascript apps, and acts as the Application Composition layer for [cujo.js](http://cujojs.com).
Wire provides architectural plumbing that allows you to create and manage application components, and to connect those components together in loosely coupled and non-invasive ways. Consequently, your components will be more modular, easier to unit test and refactor, and your application will be easier to evolve and maintain.
To find out more, read the [full introduction](docs/introduction.md), more about the [concepts behind wire](docs/concepts.md), and check out a few [example applications](docs/introduction.md#example-apps).
# Documentation
1. [Getting Started](docs/get.md)
1. [Reference Documentation](docs/TOC.md)
1. [Example Code and Apps](docs/introduction.md#example-apps)
# What's new
### 0.9.4
* Fix for [render factory](docs/dom.md#rendering-dom-elements) in IE8.
### 0.9.3
* Compatibility with when.js 1.5.0 - 2.0.x. If you use when >= 2.0.0, you *MUST* update to wire 0.9.3. There are no other changes in 0.9.3.
### 0.9.2
* IE-specific fix for `wire/debug`'s `trace` option. See [#78](https://github.com/cujojs/wire/issues/78)
### 0.9.1
* Fix for compose factory. See [#69](https://github.com/cujojs/wire/issues/69)
### 0.9.0
* [Get it!](docs/get.md)
* [All new documentation](docs/TOC.md)
* [Even more DOM support](docs/dom.md), including DOM event connections via wire/on and cloning DOM elements.
* [Functions are first-class citizens](docs/functions.md) that can be used in very powerful ways.
* [Transform connections](docs/connections.md#transform-connections) use functions to transform data as it flows through connections (including DOM event connections).
* Built on latest [cujo.js](http://cujojs.com) platform:
* [curl](https://github.com/cujojs/curl) >= 0.7.1, or 0.6.8
* [when](https://github.com/cujojs/when) >= 1.5.0 (including 2.0.x)
* [meld](https://github.com/cujojs/meld) >= 1.0.0
* [poly](https://github.com/cujojs/poly) >= 0.5.0
[Full Changelog](https://github.com/cujojs/wire/wiki/Changelog)
# License
wire.js is licensed under [The MIT License](http://www.opensource.org/licenses/mit-license.php).
...@@ -10,10 +10,15 @@ ...@@ -10,10 +10,15 @@
* Licensed under the MIT License at: * Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
*/ */
(function(define) { (function(define) { 'use strict';
define(['meld', 'when', './lib/async', './lib/connection'], function(meld, when, async, connection) { define(function(require) {
var adviceTypes, adviceStep, undef; var meld, when, sequence, connection, adviceTypes, adviceStep, undef;
meld = require('meld');
when = require('when');
sequence = require('when/sequence');
connection = require('./lib/connection');
// "after" is not included in these standard advice types because // "after" is not included in these standard advice types because
// it is created as promise-aware advice. // it is created as promise-aware advice.
...@@ -24,13 +29,13 @@ define(['meld', 'when', './lib/async', './lib/connection'], function(meld, when, ...@@ -24,13 +29,13 @@ define(['meld', 'when', './lib/async', './lib/connection'], function(meld, when,
// Decoration // Decoration
// //
function applyDecorator(target, Decorator, args) { function applyDecorator(target, Decorator, args) {
args = args ? [target].concat(args) : [target]; args = args ? [target].concat(args) : [target];
Decorator.apply(null, args); Decorator.apply(null, args);
} }
function makeDecorator(decorator, args, wire) { function makeDecorator(decorator, args, wire) {
return function(target) { return function(target) {
function apply(Decorator) { function apply(Decorator) {
return args return args
...@@ -42,21 +47,21 @@ define(['meld', 'when', './lib/async', './lib/connection'], function(meld, when, ...@@ -42,21 +47,21 @@ define(['meld', 'when', './lib/async', './lib/connection'], function(meld, when,
return when(wire.resolveRef(decorator), apply); return when(wire.resolveRef(decorator), apply);
}; };
} }
function decorateFacet(resolver, facet, wire) { function decorateFacet(resolver, facet, wire) {
var target, options, tasks; var target, options, tasks;
target = facet.target; target = facet.target;
options = facet.options; options = facet.options;
tasks = []; tasks = [];
for(var decoratorRefName in options) { for(var decoratorRefName in options) {
tasks.push(makeDecorator(decoratorRefName, options[decoratorRefName], wire)); tasks.push(makeDecorator(decoratorRefName, options[decoratorRefName], wire));
} }
resolver.resolve(async.sequence(tasks, target)); resolver.resolve(sequence(tasks, target));
} }
// //
// Simple advice // Simple advice
...@@ -65,12 +70,19 @@ define(['meld', 'when', './lib/async', './lib/connection'], function(meld, when, ...@@ -65,12 +70,19 @@ define(['meld', 'when', './lib/async', './lib/connection'], function(meld, when,
function addSingleAdvice(addAdviceFunc, advices, proxy, advice, options, wire) { function addSingleAdvice(addAdviceFunc, advices, proxy, advice, options, wire) {
function handleAopConnection(srcObject, srcMethod, adviceHandler) { function handleAopConnection(srcObject, srcMethod, adviceHandler) {
checkAdvisable(srcObject, srcMethod);
advices.push(addAdviceFunc(srcObject, srcMethod, adviceHandler)); advices.push(addAdviceFunc(srcObject, srcMethod, adviceHandler));
} }
return connection.parse(proxy, advice, options, wire, handleAopConnection); return connection.parse(proxy, advice, options, wire, handleAopConnection);
} }
function checkAdvisable(source, method) {
if (!(typeof method == 'function' || typeof source[method] == 'function')) {
throw new TypeError('Cannot add advice to non-method: ' + method);
}
}
function makeSingleAdviceAdd(adviceType) { function makeSingleAdviceAdd(adviceType) {
return function (source, sourceMethod, advice) { return function (source, sourceMethod, advice) {
return meld[adviceType](source, sourceMethod, advice); return meld[adviceType](source, sourceMethod, advice);
...@@ -180,83 +192,74 @@ define(['meld', 'when', './lib/async', './lib/connection'], function(meld, when, ...@@ -180,83 +192,74 @@ define(['meld', 'when', './lib/async', './lib/connection'], function(meld, when,
}, target)); }, target));
} }
return { /**
/** * Creates wire/aop plugin instances.
* Creates wire/aop plugin instances. *
* * @param options {Object} options passed to the plugin
* @param ready {Promise} promise that will be resolved when the context has been wired, */
* rejected if there is an error during the wiring process, and will receive progress return function(options) {
* events for object creation, property setting, and initialization.
* @param destroyed {Promise} promise that will be resolved when the context has been destroyed, // Track aspects so they can be removed when the context is destroyed
* rejected if there is an error while destroying the context, and will receive progress var woven, plugin, i, len, adviceType;
* events for objects being destroyed.
* @param options {Object} woven = [];
*/
wire$plugin: function(ready, destroyed, options) { /**
* Function to add an aspect and remember it in the current context
// Track aspects so they can be removed when the context is destroyed * so that it can be removed when the context is destroyed.
var woven, plugin, i, len, adviceType; * @param target
* @param pointcut
woven = []; * @param aspect
*/
// Remove all aspects that we added in this context function add(target, pointcut, aspect) {
when(destroyed, function() { woven.push(meld.add(target, pointcut, aspect));
for(var i = woven.length - 1; i >= 0; --i) { }
woven[i].remove();
}
});
/**
* Function to add an aspect and remember it in the current context
* so that it can be removed when the context is destroyed.
* @param target
* @param pointcut
* @param aspect
*/
function add(target, pointcut, aspect) {
woven.push(meld.add(target, pointcut, aspect));
}
function makeFacet(step, callback) { function makeFacet(step, callback) {
var facet = {}; var facet = {};
facet[step] = function(resolver, proxy, wire) { facet[step] = function(resolver, proxy, wire) {
callback(resolver, proxy, wire); callback(resolver, proxy, wire);
}; };
return facet; return facet;
} }
// Plugin // Plugin
plugin = { plugin = {
facets: { context: {
decorate: makeFacet('configure:after', decorateFacet), destroy: function(resolver) {
afterFulfilling: makeFacet(adviceStep, makeAdviceFacet(addAfterFulfillingAdvice, woven)), woven.forEach(function(aspect) {
afterRejecting: makeFacet(adviceStep, makeAdviceFacet(addAfterRejectingAdvice, woven)), aspect.remove();
after: makeFacet(adviceStep, makeAdviceFacet(addAfterPromiseAdvice, woven)) });
} resolver.resolve();
}; }
},
if(options.aspects) { facets: {
plugin.create = function(resolver, proxy, wire) { decorate: makeFacet('configure:after', decorateFacet),
weave(resolver, proxy, wire, options, add); afterFulfilling: makeFacet(adviceStep, makeAdviceFacet(addAfterFulfillingAdvice, woven)),
}; afterRejecting: makeFacet(adviceStep, makeAdviceFacet(addAfterRejectingAdvice, woven)),
after: makeFacet(adviceStep, makeAdviceFacet(addAfterPromiseAdvice, woven))
} }
};
// Add all regular single advice facets if(options.aspects) {
for(i = 0, len = adviceTypes.length; i<len; i++) { plugin.create = function(resolver, proxy, wire) {
adviceType = adviceTypes[i]; weave(resolver, proxy, wire, options, add);
plugin.facets[adviceType] = makeFacet(adviceStep, makeAdviceFacet(makeSingleAdviceAdd(adviceType), woven)); };
} }
return plugin; // Add all regular single advice facets
} for(i = 0, len = adviceTypes.length; i<len; i++) {
}; adviceType = adviceTypes[i];
plugin.facets[adviceType] = makeFacet(adviceStep, makeAdviceFacet(makeSingleAdviceAdd(adviceType), woven));
}
return plugin;
};
}); });
})(typeof define == 'function' })(typeof define == 'function'
// use define for AMD if available // use define for AMD if available
? define ? define
: function(deps, factory) { : function(factory) { module.exports = factory(require); }
module.exports = factory.apply(this, deps.map(require));
}
); );
{
"name": "wire",
"version": "0.9.4",
"repository": {
"type": "git",
"url": "git://github.com/cujojs/wire.git"
}
}
\ No newline at end of file
/** @license MIT License (c) copyright B Cavalier & J Hann */
/**
* wire/cram/builder plugin
* Builder plugin for cram
* https://github.com/cujojs/cram
*
* wire is part of the cujo.js family of libraries (http://cujojs.com/)
*
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*/
(function(define) {
define(function(require) {
var when, unfold, defaultModuleRegex, defaultSpecRegex, replaceIdsRegex,
removeCommentsRx, splitSpecsRegex;
when = require('when');
unfold = require('when/unfold');
// default dependency regex
defaultModuleRegex = /\.(module|create)$/;
defaultSpecRegex = /\.(wire\.spec|wire)$/;
// adapted from cram's scan function:
//replaceIdsRegex = /(define)\s*\(\s*(?:\s*["']([^"']*)["']\s*,)?(?:\s*\[([^\]]+)\]\s*,)?\s*(function)?\s*(?:\(([^)]*)\))?/g;
replaceIdsRegex = /(define)\s*\(\s*(?:\s*["']([^"']*)["']\s*,)?(?:\s*\[([^\]]*)\]\s*,)?/;
removeCommentsRx = /\/\*[\s\S]*?\*\/|\/\/.*?[\n\r]/g;
splitSpecsRegex = /\s*,\s*/;
return {
normalize: normalize,
compile: compile
};
function normalize(resourceId, toAbsId) {
return resourceId ? toAbsId(resourceId.split("!")[0]) : resourceId;
}
function compile(wireId, resourceId, require, io, config) {
// Track all modules seen in wire spec, so we only include them once
var specIds, defines, seenModules, childSpecRegex,
moduleRegex;
defines = [];
seenModules = {};
moduleRegex = defaultModuleRegex;
childSpecRegex = defaultSpecRegex;
// Get config values
if(config) {
if(config.moduleRegex) moduleRegex = new RegExp(config.moduleRegex);
if(config.childSpecRegex) childSpecRegex = new RegExp(config.childSpecRegex);
}
// Grab the spec module id, *or comma separated list of spec module ids*
// Split in case it's a comma separated list of spec ids
specIds = resourceId.split(splitSpecsRegex);
return when.map(specIds, function(specId) {
return processSpec(specId);
}).then(write, io.error);
// For each spec id, add the spec itself as a dependency, and then
// scan the spec contents to find all modules that it needs (e.g.
// "module" and "create")
function processSpec(specId) {
var dependencies, ids;
dependencies = [];
ids = [specId];
addDep(wireId);
return unfold(fetchNextSpec, endOfList, scanSpec, ids)
.then(function() {
return generateDefine(specId, dependencies);
}
);
function fetchNextSpec() {
var id, dfd;
id = ids.shift();
dfd = when.defer();
require(
[id],
function(spec) { dfd.resolve([spec, ids]); },
dfd.reject
);
return dfd.promise;
}
function scanSpec(spec) {
scanPlugins(spec);
scanObj(spec);
}
function scanObj(obj, path) {
// Scan all keys. This might be the spec itself, or any sub-object-literal
// in the spec.
for (var name in obj) {
scanItem(obj[name], createPath(path, name));
}
}
function scanItem(it, path) {
// Determine the kind of thing we're looking at
// 1. If it's a string, and the key is module or create, then assume it
// is a moduleId, and add it as a dependency.
// 2. If it's an object or an array, scan it recursively
// 3. If it's a wire spec, add it to the list of spec ids
if (isSpec(path) && typeof it === 'string') {
addSpec(it);
} else if (isDep(path) && typeof it === 'string') {
// Get module def
addDep(it);
} else if (isStrictlyObject(it)) {
// Descend into subscope
scanObj(it, path);
} else if (Array.isArray(it)) {
// Descend into array
var arrayPath = path + '[]';
it.forEach(function(arrayItem) {
scanItem(arrayItem, arrayPath);
});
}
}
function scanPlugins(spec) {
var plugins = spec.$plugins || spec.plugins;
if(Array.isArray(plugins)) {
plugins.forEach(addPlugin);
} else if(typeof plugins === 'object') {
Object.keys(plugins).forEach(function(key) {
addPlugin(plugins[key]);
});
}
}
function addPlugin(plugin) {
if(typeof plugin === 'string') {
addDep(plugin);
} else if(typeof plugin === 'object' && plugin.module) {
addDep(plugin.module);
}
}
function addDep(moduleId) {
if(!(moduleId in seenModules)) {
dependencies.push(moduleId);
seenModules[moduleId] = moduleId;
}
}
function addSpec(specId) {
if(!(specId in seenModules)) {
ids.push(specId);
}
addDep(specId);
}
}
function generateDefine(specId, dependencies) {
var dfd, buffer;
dfd = when.defer();
io.read(ensureExtension(specId, 'js'), function(specText) {
buffer = injectIds(specText, specId, dependencies);
defines.push(buffer);
dfd.resolve();
}, dfd.reject);
return dfd.promise;
}
function write() {
// protect against prior code that may have omitted a semi-colon
io.write('\n;' + defines.join('\n'));
}
function isDep(path) {
return moduleRegex.test(path);
}
function isSpec(path) {
return childSpecRegex.test(path);
}
}
function createPath(path, name) {
return path ? (path + '.' + name) : name
}
function isStrictlyObject(it) {
return (it && Object.prototype.toString.call(it) == '[object Object]');
}
function ensureExtension(id, ext) {
return id.lastIndexOf('.') <= id.lastIndexOf('/')
? id + '.' + ext
: id;
}
function injectIds (moduleText, absId, moduleIds) {
// note: replaceIdsRegex removes commas, parens, and brackets
return moduleText.replace(removeCommentsRx, '').replace(replaceIdsRegex, function (m, def, mid, depIds) {
// merge deps, but not args since they're not referenced in module
if (depIds) moduleIds = moduleIds.concat(depIds);
moduleIds = moduleIds.map(quoted).join(', ');
if (moduleIds) moduleIds = '[' + moduleIds + '], ';
return def + '(' + quoted(absId) + ', ' + moduleIds;
});
}
function quoted (id) {
return '"' + id + '"';
}
function endOfList(ids) {
return !ids.length;
}
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));
...@@ -44,62 +44,48 @@ ...@@ -44,62 +44,48 @@
define(['when', 'meld', './lib/functional', './lib/connection'], define(['when', 'meld', './lib/functional', './lib/connection'],
function(when, meld, functional, connection) { function(when, meld, functional, connection) {
return { return function eventsPlugin(/* options */) {
wire$plugin: function eventsPlugin(ready, destroyed /*, options */) {
var connectHandles = []; var connectHandles = [];
/** function handleConnection(instance, methodName, handler) {
* Create a single connection from source[event] to target[method] so that connectHandles.push(meld.on(instance, methodName, handler));
* when source[event] is invoked, target[method] will be invoked afterward }
* with the same params.
*
* @param source source object
* @param event source method
* @param handler {Function} function to invoke
*/
function doConnectOne(source, event, handler) {
return meld.on(source, event, handler);
}
function handleConnection(source, eventName, handler) { function doConnect(proxy, connect, options, wire) {
connectHandles.push(doConnectOne(source, eventName, handler)); return connection.parse(proxy, connect, options, wire, handleConnection);
} }
function doConnect(proxy, connect, options, wire) {
return connection.parse(proxy, connect, options, wire, handleConnection);
}
function connectFacet(wire, facet) {
var connect, promises, connects;
connects = facet.options; function connectFacet(wire, facet) {
var promises, connects;
promises = []; connects = facet.options;
promises = Object.keys(connects).map(function(key) {
return doConnect(facet, key, connects[key], wire);
});
for(connect in connects) { return when.all(promises);
promises.push(doConnect(facet, connect, connects[connect], wire)); }
}
return when.all(promises); return {
} context: {
destroy: function(resolver) {
destroyed.then(function onContextDestroy() { connectHandles.forEach(function(handle) {
for (var i = connectHandles.length - 1; i >= 0; i--){ handle.remove();
connectHandles[i].remove(); });
} resolver.resolve();
}); }
},
return { facets: {
facets: { // A facet named "connect" that runs during the connect
connect: { // lifecycle phase
connect: function(resolver, facet, wire) { connect: {
resolver.resolve(connectFacet(wire, facet)); connect: function(resolver, facet, wire) {
} resolver.resolve(connectFacet(wire, facet));
} }
} }
}; }
} };
}; };
}); });
})(typeof define == 'function' })(typeof define == 'function'
......
/** @license MIT License (c) copyright B Cavalier & J Hann */
/**
* wire/cram/builder plugin
* Builder plugin for cram
* https://github.com/cujojs/cram
*
* wire is part of the cujo.js family of libraries (http://cujojs.com/)
*
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*/
define([], function() {
var defaultModuleRegex;
// default dependency regex
defaultModuleRegex = /\.(module|create)$/;
function cramAnalyze(myId, api, addDep, config) {
// Track all modules seen in wire spec, so we only include them once
var seenModules, specs, spec, i, childSpecRegex, moduleRegex;
seenModules = {};
moduleRegex = defaultModuleRegex;
// Get config values
if(config) {
if(config.moduleRegex) moduleRegex = new RegExp(config.moduleRegex);
if(config.childSpecRegex) childSpecRegex = new RegExp(config.childSpecRegex);
}
function addAbsoluteDep(absoluteId) {
// Only add the moduleId if we haven't already
if (absoluteId in seenModules) return;
seenModules[absoluteId] = 1;
addDep(absoluteId);
}
function addDependency(moduleId) {
addAbsoluteDep(api.toAbsMid(moduleId));
}
function addChildSpec(specId) {
addAbsoluteDep('wire' + '!' + api.toAbsMid(specId));
}
function scanObj(obj, path) {
// Scan all keys. This might be the spec itself, or any sub-object-literal
// in the spec.
for (var name in obj) {
scanItem(obj[name], path ? ([path, name].join('.')) : name);
}
}
function scanItem(it, path) {
// Determine the kind of thing we're looking at
// 1. If it's a string, and the key is module or create, then assume it
// is a moduleId, and add it as a dependency.
// 2. If it's an object or an array, scan it recursively
if (typeof it === 'string') {
// If it's a regular module, add it as a dependency
// If it's child spec, add it as a wire! dependency
if (isDep(path)) {
addDependency(it);
} else if (isWireDep(path)) {
addChildSpec(it);
}
}
if (isDep(path) && typeof it === 'string') {
// Get module def
addDependency(it);
} else if (isStrictlyObject(it)) {
// Descend into subscope
scanObj(it, path);
} else if (isArray(it)) {
// Descend into array
var arrayPath = path + '[]';
for (var i = 0, len = it.length; i < len; i++) {
scanItem(it[i], arrayPath);
}
}
}
function isWireDep(path) {
return childSpecRegex && childSpecRegex.test(path);
}
function isDep(path) {
return moduleRegex.test(path);
}
// Grab the spec module id, *or comma separated list of spec module ids*
// Split in case it's a comma separated list of spec ids
specs = myId.split(',');
// For each spec id, add the spec itself as a dependency, and then
// scan the spec contents to find all modules that it needs (e.g.
// "module" and "create")
for (i = 0; (spec = specs[i++]);) {
scanObj(api.load(spec));
addDependency(spec);
}
}
function isStrictlyObject(it) {
return (it && Object.prototype.toString.call(it) == '[object Object]');
}
return {
analyze: cramAnalyze
};
});
\ No newline at end of file
...@@ -79,10 +79,12 @@ ...@@ -79,10 +79,12 @@
*/ */
(function(global, define) { (function(global, define) {
define(['meld'], function(meld) { define(['meld'], function(meld) {
var timer, defaultTimeout, logger, createTracer; var timer, defaultTimeout, logger, createTracer, ownProp;
function noop() {} function noop() {}
ownProp = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);
// Setup console for node, sane browsers, or IE // Setup console for node, sane browsers, or IE
logger = typeof console != 'undefined' logger = typeof console != 'undefined'
? console ? console
...@@ -234,7 +236,7 @@ define(['meld'], function(meld) { ...@@ -234,7 +236,7 @@ define(['meld'], function(meld) {
/** Default pointcut query to match methods that will be traced */ /** Default pointcut query to match methods that will be traced */
defaultPointcut = /^[^_]/; defaultPointcut = /^[^_]/;
function logAfter(context, tag, start, val) { function logAfter(context, tag, start, val) {
console.log(context + tag + (new Date().getTime() - start.getTime()) + 'ms): ', val); console.log(context + tag + (new Date().getTime() - start.getTime()) + 'ms): ', val);
} }
...@@ -247,7 +249,7 @@ define(['meld'], function(meld) { ...@@ -247,7 +249,7 @@ define(['meld'], function(meld) {
return { return {
around:function (joinpoint) { around:function (joinpoint) {
var val, context, start, indent; var val, context, start, indent;
// Setup current indent level // Setup current indent level
indent = padding.substr(0, depth); indent = padding.substr(0, depth);
// Form full path to invoked method // Form full path to invoked method
...@@ -355,168 +357,200 @@ define(['meld'], function(meld) { ...@@ -355,168 +357,200 @@ define(['meld'], function(meld) {
logger.log('---------------------------------------------------'); logger.log('---------------------------------------------------');
} }
return { return function wireDebug(options) {
wire$plugin:function debugPlugin(ready, destroyed, options) {
var contextTimer, timeout, paths, count, tag, logCreated, logDestroyed, checkPathsTimeout, var contextTimer, timeout, paths, count, tag,
verbose, filter, plugin, tracer; logCreated, logDestroyed, checkPathsTimeout,
verbose, filter, plugin, context, tracer;
verbose = options.verbose; verbose = options.verbose;
contextTimer = createTimer(); contextTimer = createTimer();
count = 0; count = 0;
tag = "WIRING"; tag = "WIRING";
tracer = { trace: noop, untrace: noop }; tracer = { trace: noop, untrace: noop };
filter = createPathFilter(options.filter); filter = createPathFilter(options.filter);
function contextTime(msg) {
return time(msg, contextTimer);
}
function contextTime(msg) { logger.log(contextTime("Context debug"));
return time(msg, contextTimer);
context = {
initialize: function(resolver) {
logger.log(contextTime("Context init"));
resolver.resolve();
},
ready: function(resolver) {
cancelPathsTimeout();
logger.log(contextTime("Context ready"));
resolver.resolve();
},
destroy: function(resolver) {
tracer.untrace();
logger.log(contextTime("Context destroyed"));
resolver.resolve();
},
error: function(resolver, api, err) {
cancelPathsTimeout();
console.error(contextTime("Context ERROR: ") + err, err);
logStack(err);
resolver.reject(err);
} }
};
logger.log(contextTime("Context init")); function makeListener(step, verbose) {
return function (promise, proxy /*, wire */) {
ready.then( cancelPathsTimeout();
function onContextReady(context) {
cancelPathsTimeout();
logger.log(contextTime("Context ready"), context);
},
function onContextError(err) {
cancelPathsTimeout();
console.error(contextTime("Context ERROR: ") + err, err);
logStack(err);
}
);
destroyed.then(
function onContextDestroyed() {
tracer.untrace();
logger.log(contextTime("Context destroyed"));
},
function onContextDestroyError(err) {
tracer.untrace();
logger.error(contextTime("Context destroy ERROR") + err, err);
logStack(err);
}
);
function makeListener(step, verbose) { var path = proxy.path;
return function (promise, proxy /*, wire */) {
cancelPathsTimeout();
var path = proxy.path; if (paths[path]) {
paths[path].status = step;
}
if (paths[path]) { if (verbose && filter(path)) {
paths[path].status = step; var message = time(step + ' ' + (path || proxy.id || ''), contextTimer);
if (proxy.target) {
logger.log(message, proxy.target, proxy.metadata);
} else {
logger.log(message, proxy);
} }
}
if (verbose && filter(path)) { if(count) {
var message = time(step + ' ' + (path || proxy.id || ''), contextTimer); checkPathsTimeout = setTimeout(checkPaths, timeout);
if (proxy.target) { }
logger.log(message, proxy.target, proxy.spec);
} else {
logger.log(message, proxy);
}
}
if(count) { promise.resolve();
checkPathsTimeout = setTimeout(checkPaths, timeout); };
} }
promise.resolve(); paths = {};
}; timeout = options.timeout || defaultTimeout;
} logCreated = makeListener('created', verbose);
logDestroyed = makeListener('destroyed', true);
paths = {}; function cancelPathsTimeout() {
timeout = options.timeout || defaultTimeout; clearTimeout(checkPathsTimeout);
logCreated = makeListener('created', verbose); checkPathsTimeout = null;
logDestroyed = makeListener('destroyed', true); }
function cancelPathsTimeout() { function checkPaths() {
clearTimeout(checkPathsTimeout); if (!checkPathsTimeout) {
checkPathsTimeout = null; return;
} }
function checkPaths() { var p, component, msg, ready, notReady;
if (!checkPathsTimeout) {
return;
}
var p, component, msg, ready, notReady;
logSeparator(); logSeparator();
if(count) { if(count) {
ready = []; ready = [];
notReady = []; notReady = [];
logger.error(tag + ': No progress in ' + timeout + 'ms, status:'); logger.error(tag + ': No progress in ' + timeout + 'ms, status:');
for (p in paths) { for (p in paths) {
component = paths[p]; component = paths[p];
msg = p + ': ' + component.status; msg = p + ': ' + component.status;
(component.status == 'ready' ? ready : notReady).push( (component.status == 'ready' ? ready : notReady).push(
{ msg: msg, spec: component.spec } { msg: msg, metadata: component.metadata }
); );
} }
if(notReady.length > 0) { if(notReady.length > 0) {
logSeparator(); logSeparator();
logger.log('Components that DID NOT finish wiring'); logger.log('Components that DID NOT finish wiring');
for(p = notReady.length-1; p >= 0; --p) { for(p = notReady.length-1; p >= 0; --p) {
component = notReady[p]; component = notReady[p];
logger.error(component.msg, component.spec); logger.error(component.msg, component.metadata);
}
} }
}
if(ready.length > 0) { if(ready.length > 0) {
logSeparator(); logSeparator();
logger.log('Components that finished wiring'); logger.log('Components that finished wiring');
for(p = ready.length-1; p >= 0; --p) { for(p = ready.length-1; p >= 0; --p) {
component = ready[p]; component = ready[p];
logger.log(component.msg, component.spec); logger.log(component.msg, component.metadata);
}
} }
} else {
logger.error(tag + ': No components created after ' + timeout + 'ms');
} }
} else {
logger.error(tag + ': No components created after ' + timeout + 'ms');
}
logSeparator();
}
logSeparator(); /**
* Adds a named constructor function property to the supplied component
* so that the name shows up in debug inspectors. Squelches all errors.
*/
function makeConstructor(name, component) {
/*jshint evil:true*/
var ctor;
try {
// Generate a named function to use as the constructor
name = name.replace(/^[^a-zA-Z$_]|[^a-zA-Z0-9$_]+/g, '_');
eval('ctor = function ' + name + ' () {}');
// Be friendly and make configurable and writable just in case
// some other library or application code tries to set constructor.
Object.defineProperty(component, 'constructor', {
configurable: true,
writable: true,
value: ctor
});
} catch(e) {
// Oh well, ignore.
} }
}
plugin = {
context: context,
create:function (promise, proxy) {
var path, component;
path = proxy.path;
component = proxy.target;
count++;
paths[path || ('(unnamed-' + count + ')')] = {
metadata: proxy.metadata
};
plugin = { if(component && typeof component == 'object'
create:function (promise, proxy) { && !ownProp(component, 'constructor')) {
var path = proxy.path; makeConstructor(proxy.id, component);
count++;
paths[path || ('(unnamed-' + count + ')')] = {
spec:proxy.spec
};
logCreated(promise, proxy);
},
configure: makeListener('configured', verbose),
initialize: makeListener('initialized', verbose),
ready: makeListener('ready', true),
destroy: function(promise, proxy) {
// stop tracking destroyed components, since we don't
// care anymore
delete paths[proxy.path];
count--;
tag = "DESTROY";
logDestroyed(promise, proxy);
} }
};
if (options.trace) { logCreated(promise, proxy);
tracer = createTracer(options, plugin, filter); },
configure: makeListener('configured', verbose),
initialize: makeListener('initialized', verbose),
ready: makeListener('ready', true),
destroy: function(promise, proxy) {
// stop tracking destroyed components, since we don't
// care anymore
delete paths[proxy.path];
count--;
tag = "DESTROY";
logDestroyed(promise, proxy);
} }
};
checkPathsTimeout = setTimeout(checkPaths, timeout); if (options.trace) {
tracer = createTracer(options, plugin, filter);
return plugin;
} }
checkPathsTimeout = setTimeout(checkPaths, timeout);
return plugin;
}; };
}); });
......
...@@ -13,9 +13,11 @@ ...@@ -13,9 +13,11 @@
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
*/ */
define(['when'], function(when) { define(function() {
/** var pluginInstance;
/**
* Reference resolver for "datastore!url" for easy references to * Reference resolver for "datastore!url" for easy references to
* legacy dojo/data datastores. * legacy dojo/data datastores.
* *
...@@ -25,7 +27,8 @@ define(['when'], function(when) { ...@@ -25,7 +27,8 @@ define(['when'], function(when) {
* @param wire * @param wire
*/ */
function dataStoreResolver(resolver, name, refObj, wire) { function dataStoreResolver(resolver, name, refObj, wire) {
var promise = wire({
var dataStore = wire({
create: { create: {
module: 'dojo/data/ObjectStore', module: 'dojo/data/ObjectStore',
args: { args: {
...@@ -37,23 +40,23 @@ define(['when'], function(when) { ...@@ -37,23 +40,23 @@ define(['when'], function(when) {
} }
} }
} }
}); });
resolver.resolve(promise); resolver.resolve(dataStore);
} }
/** /**
* The plugin instance. Can be the same for all wiring runs * The plugin instance. Can be the same for all wiring runs
*/ */
var plugin = { pluginInstance = {
resolvers: { resolvers: {
datastore: dataStoreResolver datastore: dataStoreResolver
} }
}; };
return { return {
wire$plugin: function datastorePlugin(/* ready, destroyed, options */) { wire$plugin: function datastorePlugin(/* options */) {
return plugin; return pluginInstance;
} }
}; };
}); });
\ No newline at end of file
...@@ -14,8 +14,8 @@ ...@@ -14,8 +14,8 @@
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
*/ */
define(['dojo', 'dojo/parser', 'dijit', 'dijit/_Widget'], function(dojo, parser, dijit, Widget) { define(['dojo', 'dojo/parser', 'dijit', 'dijit/_Widget', '../lib/WireProxy'], function(dojo, parser, dijit, Widget, WireProxy) {
var parsed, isArray, loadTheme, placeAtFacet; var parsed, isArray, loadTheme, placeAtFacet, pluginInstance;
parsed = false; parsed = false;
...@@ -47,44 +47,33 @@ define(['dojo', 'dojo/parser', 'dijit', 'dijit/_Widget'], function(dojo, parser, ...@@ -47,44 +47,33 @@ define(['dojo', 'dojo/parser', 'dijit', 'dijit/_Widget'], function(dojo, parser,
return it instanceof Widget; return it instanceof Widget;
} }
function createDijitProxy(object /*, spec */) { var dijitProxy = {
var proxy; get: function(name) {
return this.target.get(name);
if (isDijit(object)) { },
proxy = { set: function(name, val) {
get:function(property) { return this.target.set(name, val);
return object.get(property); },
}, destroy: function() {
set:function(property, value) { return destroyDijit(this.target);
return object.set(property, value); },
}, clone: function() {
invoke:function(method, args) { return dojo.clone(this.target);
if (typeof method === 'string') { }
method = object[method]; };
}
function proxyDijit(proxy) {
return method.apply(object, args); if (isDijit(proxy.target)) {
}, return WireProxy.extend(proxy, dijitProxy);
destroy:function() {
destroyDijit(object);
},
clone: function (options) {
return dojo.clone(object);
}
};
} }
return proxy;
} }
function destroyDijit(target) { function destroyDijit(target) {
// Prefer destroyRecursive over destroy // Prefer destroyRecursive over destroy
if (typeof target.destroyRecursive == 'function') { if (typeof target.destroyRecursive == 'function') {
target.destroyRecursive(false); target.destroyRecursive(false);
} else if (typeof target.destroy == 'function') { } else if (typeof target.destroy == 'function') {
target.destroy(false); target.destroy(false);
} }
} }
...@@ -131,8 +120,20 @@ define(['dojo', 'dojo/parser', 'dijit', 'dijit/_Widget'], function(dojo, parser, ...@@ -131,8 +120,20 @@ define(['dojo', 'dojo/parser', 'dijit', 'dijit/_Widget'], function(dojo, parser,
} }
}; };
pluginInstance = {
resolvers:{
dijit:dijitById
},
proxies:[
proxyDijit
],
facets: {
placeAt: placeAtFacet
}
};
return { return {
wire$plugin:function(ready, destroy, options) { wire$plugin:function(options) {
// Only ever parse the page once, even if other child // Only ever parse the page once, even if other child
// contexts are created with this plugin present. // contexts are created with this plugin present.
if (options.parse && !parsed) { if (options.parse && !parsed) {
...@@ -147,17 +148,7 @@ define(['dojo', 'dojo/parser', 'dijit', 'dijit/_Widget'], function(dojo, parser, ...@@ -147,17 +148,7 @@ define(['dojo', 'dojo/parser', 'dijit', 'dijit/_Widget'], function(dojo, parser,
if (theme) loadTheme(theme); if (theme) loadTheme(theme);
// Return plugin // Return plugin
return { return pluginInstance;
resolvers:{
dijit:dijitById
},
proxies:[
createDijitProxy
],
facets: {
placeAt: placeAtFacet
}
};
} }
}; };
}); });
\ No newline at end of file
...@@ -17,7 +17,7 @@ define(['when', '../lib/connection', 'dojo', 'dojo/_base/event'], ...@@ -17,7 +17,7 @@ define(['when', '../lib/connection', 'dojo', 'dojo/_base/event'],
function(when, connection, events) { function(when, connection, events) {
return { return {
wire$plugin: function eventsPlugin(ready, destroyed /*, options*/) { wire$plugin: function eventsPlugin(/*, options*/) {
var connectHandles = []; var connectHandles = [];
...@@ -69,13 +69,15 @@ function(when, connection, events) { ...@@ -69,13 +69,15 @@ function(when, connection, events) {
return when.all(promises); return when.all(promises);
} }
destroyed.then(function onContextDestroy() {
for (var i = connectHandles.length - 1; i >= 0; i--){
events.disconnect(connectHandles[i]);
}
});
return { return {
context: {
destroy: function(resolver) {
for (var i = connectHandles.length - 1; i >= 0; i--){
events.disconnect(connectHandles[i]);
}
resolver.resolve();
}
},
facets: { facets: {
connect: { connect: {
connect: function(resolver, facet, wire) { connect: function(resolver, facet, wire) {
......
...@@ -42,7 +42,7 @@ define(['../lib/plugin-base/on', 'dojo/on', 'dojo/query'], function(createOnPlug ...@@ -42,7 +42,7 @@ define(['../lib/plugin-base/on', 'dojo/on', 'dojo/query'], function(createOnPlug
on.wire$plugin = createOnPlugin({ on.wire$plugin = createOnPlugin({
on: on on: on
}).wire$plugin; });
return on; return on;
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
define(['dojo', 'meld', 'dojo/_base/connect'], function(pubsub, meld) { define(['dojo', 'meld', 'dojo/_base/connect'], function(pubsub, meld) {
return { return {
wire$plugin: function pubsubPlugin(ready, destroyed /*, options */) { wire$plugin: function pubsubPlugin(/*, options */) {
var destroyHandlers = []; var destroyHandlers = [];
...@@ -73,15 +73,17 @@ define(['dojo', 'meld', 'dojo/_base/connect'], function(pubsub, meld) { ...@@ -73,15 +73,17 @@ define(['dojo', 'meld', 'dojo/_base/connect'], function(pubsub, meld) {
} }
} }
// When the context is destroyed, remove all publish and
// subscribe hooks created in this context
destroyed.then(function onContextDestroy() {
for (var i = destroyHandlers.length - 1; i >= 0; --i){
destroyHandlers[i]();
}
});
return { return {
context: {
destroy: function(resolver) {
// When the context is destroyed, remove all publish and
// subscribe hooks created in this context
for (var i = destroyHandlers.length - 1; i >= 0; --i){
destroyHandlers[i]();
}
resolver.resolve();
}
},
facets: { facets: {
publish: { publish: {
connect: function(promise, facet, wire) { connect: function(promise, facet, wire) {
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
define([], function() { define([], function() {
var pluginInstance;
/** /**
* If wait === true, waits for dataPromise to complete and resolves * If wait === true, waits for dataPromise to complete and resolves
* the reference to the resulting concrete data. If wait !== true, * the reference to the resulting concrete data. If wait !== true,
...@@ -74,15 +76,15 @@ define([], function() { ...@@ -74,15 +76,15 @@ define([], function() {
/** /**
* The plugin instance. Can be the same for all wiring runs * The plugin instance. Can be the same for all wiring runs
*/ */
var plugin = { pluginInstance = {
resolvers: { resolvers: {
resource: resolveResource resource: resolveResource
} }
}; };
return { return {
wire$plugin: function restPlugin(/* ready, destroyed, options */) { wire$plugin: function restPlugin(/* options */) {
return plugin; return pluginInstance;
} }
}; };
}); });
\ No newline at end of file
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
*/ */
define(['./lib/plugin-base/dom', './lib/dom/base', './domReady'], function(createDomPlugin, base, domReady) { define(['./lib/plugin-base/dom', './lib/dom/base'], function(createDomPlugin, base) {
return createDomPlugin({ return createDomPlugin({
addClass: base.addClass, addClass: base.addClass,
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
define(['./../lib/dom/base', 'when'], function (base, when) { define(['./../lib/dom/base', 'when'], function (base, when) {
var parentTypes, parseTemplateRx, getFirstTagNameRx, isPlainTagNameRx, var parentTypes, parseTemplateRx, getFirstTagNameRx, isPlainTagNameRx,
undef; pluginInstance, undef;
// elements that could be used as root nodes and their natural parent type // elements that could be used as root nodes and their natural parent type
parentTypes = { parentTypes = {
...@@ -42,10 +42,9 @@ define(['./../lib/dom/base', 'when'], function (base, when) { ...@@ -42,10 +42,9 @@ define(['./../lib/dom/base', 'when'], function (base, when) {
* @param template {String} html template * @param template {String} html template
* @param hashmap {Object} string replacements hash * @param hashmap {Object} string replacements hash
* @param optRefNode {HTMLElement} node to replace with root node of rendered template * @param optRefNode {HTMLElement} node to replace with root node of rendered template
* @param optCss {Object} unused
* @returns {HTMLElement} * @returns {HTMLElement}
*/ */
function render (template, hashmap, optRefNode, optCss) { function render (template, hashmap, optRefNode /*, optCss */) {
var node; var node;
// replace tokens (before attempting to find top tag name) // replace tokens (before attempting to find top tag name)
...@@ -67,15 +66,17 @@ define(['./../lib/dom/base', 'when'], function (base, when) { ...@@ -67,15 +66,17 @@ define(['./../lib/dom/base', 'when'], function (base, when) {
return node; return node;
} }
render.wire$plugin = function (/*ready, destroyed, options*/) { pluginInstance = {
return { factories: {
factories: { render: domRenderFactory
render: domRenderFactory },
}, proxies: [
proxies: [ base.proxyNode
base.nodeProxy ]
] };
};
render.wire$plugin = function (/* options */) {
return pluginInstance;
}; };
/** /**
...@@ -143,11 +144,11 @@ define(['./../lib/dom/base', 'when'], function (base, when) { ...@@ -143,11 +144,11 @@ define(['./../lib/dom/base', 'when'], function (base, when) {
/** /**
* Creates rendered dom trees for the "render" factory. * Creates rendered dom trees for the "render" factory.
* @param resolver * @param resolver
* @param spec * @param componentDef
* @param wire * @param wire
*/ */
function domRenderFactory (resolver, spec, wire) { function domRenderFactory (resolver, componentDef, wire) {
when(wire(spec.render), function (options) { when(wire(componentDef.options), function (options) {
var template; var template;
template = options.template || options; template = options.template || options;
return render(template, options.replace, options.at, options.css); return render(template, options.replace, options.at, options.css);
...@@ -205,7 +206,7 @@ define(['./../lib/dom/base', 'when'], function (base, when) { ...@@ -205,7 +206,7 @@ define(['./../lib/dom/base', 'when'], function (base, when) {
if (!missing) { if (!missing) {
missing = blankIfMissing; missing = blankIfMissing;
} }
return template.replace(parseTemplateRx, function (m, token) { return template.replace(parseTemplateRx, function (m, token) {
return missing(findProperty(hashmap, token)); return missing(findProperty(hashmap, token));
}); });
......
/** @license MIT License (c) copyright B Cavalier & J Hann */ /** @license MIT License (c) copyright 2011-2013 original author or authors */
/** /**
* wire/domReady plugin * wire/domReady
* A base wire/domReady module that plugins can use if they need domReady. Simply * A base wire/domReady module that plugins can use if they need domReady. Simply
* add 'wire/domReady' to your plugin module dependencies * add 'wire/domReady' to your plugin module dependencies
* (e.g. require(['wire/domReady', ...], function(domReady, ...) { ... })) and you're * (e.g. require(['wire/domReady', ...], function(domReady, ...) { ... })) and you're
...@@ -21,6 +21,9 @@ ...@@ -21,6 +21,9 @@
* paths: { * paths: {
* 'wire/domReady': 'path/to/my/domReady' * 'wire/domReady': 'path/to/my/domReady'
* } * }
*
* @author Brian Cavalier
* @author John Hann
*/ */
(function(global) { (function(global) {
......
...@@ -46,14 +46,14 @@ define(['../lib/plugin-base/on', 'jquery'], function(createOnPlugin, jquery) { ...@@ -46,14 +46,14 @@ define(['../lib/plugin-base/on', 'jquery'], function(createOnPlugin, jquery) {
on.wire$plugin = createOnPlugin({ on.wire$plugin = createOnPlugin({
on: on on: on
}).wire$plugin; });
return on; return on;
function makeEventHandler (handler, selector) { function makeEventHandler (handler, selector) {
return function (e) { return function (e, o) {
if (selector) e.selectorTarget = this; if (selector) e.selectorTarget = this;
handler(e); handler(e, o);
} }
} }
......
/** @license MIT License (c) copyright 2011-2013 original author or authors */
/**
* Allows declarative creation of jQuery UI widgets.
* wire is part of the cujo.js family of libraries (http://cujojs.com/)
*
* @author Brian Cavalier
* @author John Hann
*
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*/
define(['when', 'jquery', '../lib/WireProxy'], function (when, $, WireProxy) {
var typeDataProp, proxyMixin, pluginInstance;
typeDataProp = 'wire$type';
proxyMixin = getWidgetProxyMixin();
pluginInstance = {
factories: {
widget: widgetFactory
},
proxies: [
proxyWidget
]
};
return {
wire$plugin: function jQueryUIPlugin (/* options */) {
return pluginInstance;
}
};
/**
* Creates a jQuery UI widget on top of a dom node.
* Since jQuery UI widgets don't really have a normal API, the proxy has to
* hunt around for "properties" and/or "methods" in the proxy.
* jQuery UI widgets don't actually have properties, but they do have options
* and data attributes. The proxy will get/set these instead.
* In order to allow widgets to be bound to eachother via property changes,
* the proxy adds a feature to parse methods that look like "getXXX" or
* "setXXX" into property getters and setters. These getters and setters can
* be used in a function pipeline, for instance.
* @param {Deferred} resolver
* @param {Object} spec
* @param {Function} wire
*/
function widgetFactory (resolver, spec, wire) {
var type, widget;
type = spec.options.type;
if (!type) throw new Error('widget factory requires a "type" property.');
// jQuery UI widgets place things at $[type] $.ui[type] and $.fn[type].
// however, wijmo widgets only appear at $.fn[type].
if (typeof $.fn[type] != 'function') {
throw new Error('widget factory could not find a jQuery UI Widget constructor for ' + type);
}
widget = when.join(wire(spec.options.node), wire(spec.options.options || {}))
.spread(createWidget);
resolver.resolve(widget);
function createWidget (el, options) {
var $w;
if (!isNode(el) && !isjQWrapped(el)) throw new Error('widget factory could not resolve "node" property: ' + spec.options.node);
$w = $(el);
$w.data(typeDataProp, type);
return $w[type](options);
}
}
/**
* Extends the base wire proxy if the target is a jQuery UI widget.
* @param {Object} proxy
* @return {Object} proxy
*/
function proxyWidget (proxy) {
if (isWidget(proxy.target)) {
return WireProxy.extend(proxy, proxyMixin);
}
}
/**
* Creates an object to use to extend the base wire proxy.
* @private
* @return {Object} an object ot mix into the base proxy.
*/
function getWidgetProxyMixin () {
return {
get: function (name) {
var $w, type, data;
$w = this.target;
type = $w.data(typeDataProp);
// if there is a method with this name, call it to get value
if (typeof $w[name] == 'function') return $w[name]();
// if there is an option (not undefined), then return that
if (hasOption($w, type, name)) return $w[type]('option', name);
// check if it's a data item
data = $w.data();
if (name in data) {
return data[name];
}
// try an auto-getter/setter property
return createAccessor(this, $w, name);
},
set: function (name, value) {
var $w, type;
$w = this.target;
type = $w.data(typeDataProp);
// if there is a function with this name, call it to set value
if (typeof $w[name] == 'function') return $w[name](value);
// if there is an option (not undefined), set it
if (hasOption($w, type, name)) return $w[type]('option', name, value);
// must be a data item
return $w.data(name, value);
},
invoke: function (method, args) {
var $w, type, func, margs;
$w = this.target;
type = $w.data(typeDataProp);
func = typeof method == 'function'
? method // wrapped function
: this.get(method); // wire property
if (typeof func == 'function') {
return func.apply(null, args);
}
else {
margs = [method];
if (args && args.length) {
// using margs's slice to ensure args is an array (instead of Arguments)
margs = margs.concat(margs.slice.apply(args));
}
return $w[type].apply($w, margs);
}
},
destroy: function () {
var $w = this.target;
$w.destroy();
},
clone: function (options) {
var $w = this.target;
// default is to clone deep (when would anybody not want deep?)
return $w.clone(!('deep' in options) || options.deep);
}
};
}
function hasOption ($w, type, name) {
// thankfully, all options should be pre-defined in a jquery ui widget
var options = $w[type]('option');
return options && name in options;
}
/**
* Creates a getter or setter, if there is a matching "property".
* @private
* @param {Object} proxy
* @param {Object} $w jQuery-wrapped node of the widget.
* @param {String} name of the "property"
*/
function createAccessor (proxy, $w, name) {
var type, prop;
type = name.substr(0, 3);
prop = name.charAt(3).toLowerCase() + name.substr(4);
if (type == 'get') {
return function () {
return proxy.get(prop);
};
}
else if (type == 'set') {
return function (value) {
return proxy.set(prop, value);
};
}
}
function isWidget (it) {
return it
&& it.data
&& !!it.data(typeDataProp);
}
function isNode (it) {
return typeof Node == 'object'
? it instanceof Node
: it && typeof it == 'object' && typeof it.nodeType == 'number' && typeof it.nodeName == 'string';
}
function isjQWrapped (it) {
return typeof it == 'function' && typeof it.jquery == 'function';
}
});
/** @license MIT License (c) copyright 2010-2013 original author or authors */
/**
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*
* @author: Brian Cavalier
* @author: John Hann
*/
(function(define) { 'use strict';
define(function(require) {
var when, object, WireProxy, undef;
when = require('when');
object = require('./object');
WireProxy = require('./WireProxy');
function ComponentFactory(lifecycle, plugins, pluginApi) {
this.plugins = plugins;
this.pluginApi = pluginApi;
this.lifecycle = lifecycle;
this.proxies = [];
}
ComponentFactory.prototype = {
create: function(component) {
var found;
// Look for a factory, then use it to create the object
found = this.getFactory(component.spec);
return found
? this._create(component, found.factory, found.options)
: when.reject(component);
},
_create: function(component, factory, options) {
var instance, self;
instance = when.defer();
self = this;
factory(instance.resolver, options,
this.pluginApi.contextualize(component.id));
return instance.promise.then(function(instance) {
return self.processComponent(component, instance);
});
},
processComponent: function(component, instance) {
var self, proxy;
self = this;
proxy = this.createProxy(instance, component);
return self.initInstance(proxy).then(
function(proxy) {
return self.startupInstance(proxy);
}
).then(WireProxy.getTarget);
},
initInstance: function(proxy) {
return this.lifecycle.init(proxy);
},
startupInstance: function(proxy) {
return this.lifecycle.startup(proxy);
},
createProxy: function(instance, component) {
var proxy;
if (WireProxy.isProxy(instance)) {
proxy = instance;
instance = WireProxy.getTarget(proxy);
} else {
proxy = WireProxy.create(instance);
}
if(component) {
proxy.id = component.id;
proxy.metadata = component;
}
return this.initProxy(proxy);
},
initProxy: function(proxy) {
var proxiers = this.plugins.proxiers;
// Allow proxy plugins to process/modify the proxy
proxy = proxiers.reduce(
function(proxy, proxier) {
var overridden = proxier(proxy);
return WireProxy.isProxy(overridden) ? overridden : proxy;
},
proxy
);
this._registerProxy(proxy);
return proxy;
},
destroy: function() {
var proxies, lifecycle;
proxies = this.proxies;
lifecycle = this.lifecycle;
return shutdownComponents().then(destroyComponents);
function shutdownComponents() {
return when.reduce(proxies,
function(_, proxy) { return lifecycle.shutdown(proxy); },
undef);
}
function destroyComponents() {
return when.reduce(proxies,
function(_, proxy) { return proxy.destroy(); },
undef);
}
},
_registerProxy: function(proxy) {
if(proxy.metadata) {
proxy.path = proxy.metadata.path;
this.proxies.push(proxy);
}
},
getFactory: function(spec) {
var f, factories, found;
factories = this.plugins.factories;
for (f in factories) {
if (object.hasOwn(spec, f)) {
found = {
factory: factories[f],
options: {
options: spec[f],
spec: spec
}
};
break;
}
}
// Intentionally returns undefined if no factory found
return found;
}
};
return ComponentFactory;
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));
/** @license MIT License (c) copyright B Cavalier & J Hann */
/**
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*/
(function(define){ 'use strict';
define(function(require) {
var when, advice, WireContext, Scope, PluginRegistry, defaultPlugins,
DirectedGraph, trackInflightRefs, slice, scopeProto, undef;
when = require('when');
advice = require('./advice');
WireContext = require('./WireContext');
Scope = require('./scope');
PluginRegistry = require('./plugin/registry');
defaultPlugins = require('./plugin/defaultPlugins');
DirectedGraph = require('./graph/DirectedGraph');
trackInflightRefs = require('./graph/trackInflightRefs');
slice = Array.prototype.slice;
scopeProto = Scope.prototype;
function Container() {
Scope.apply(this, arguments);
}
/**
* Container inherits from Scope, adding plugin support and
* context level events.
*/
Container.prototype = Object.create(scopeProto, {
_inheritInstances: { value: function(parent) {
var publicApi = {
wire: this._createChild.bind(this),
destroy: this.destroy.bind(this),
resolve: this._resolveRef.bind(this)
};
return WireContext.inherit(parent.instances, publicApi);
}},
_init: { value: advice.after(
scopeProto._init,
function() {
this.plugins = new PluginRegistry();
return this._installDefaultPlugins();
}
)},
_startup: { value: advice.after(
scopeProto._startup,
function(started) {
var self = this;
return when(started).otherwise(function(e) {
return self._contextEvent('error', e).yield(started);
});
}
)},
_installDefaultPlugins: { value: function() {
return this._installPlugins(defaultPlugins);
}},
_installPlugins: { value: function(plugins) {
if(!plugins) {
return when.resolve();
}
var self, registry, installed;
self = this;
registry = this.plugins;
if(Array.isArray(plugins)) {
installed = plugins.map(function(plugin) {
return installPlugin(plugin);
});
} else {
installed = Object.keys(plugins).map(function(namespace) {
return installPlugin(plugins[namespace], namespace);
});
}
return when.all(installed);
function installPlugin(pluginSpec, namespace) {
var module, t;
t = typeof pluginSpec;
if(t == 'string') {
module = pluginSpec;
pluginSpec = {};
} else if(typeof pluginSpec.module == 'string') {
module = pluginSpec.module;
} else {
module = pluginSpec;
}
return self.getModule(module).then(function(plugin) {
return registry.scanModule(plugin, pluginSpec, namespace);
});
}
}},
_createResolver: { value: advice.after(
scopeProto._createResolver,
function(resolver) {
return trackInflightRefs(
new DirectedGraph(), resolver, this.refCycleTimeout);
}
)},
_contextEvent: { value: function (type, data) {
var api, listeners;
if(!this.contextEventApi) {
this.contextEventApi = this._pluginApi.contextualize(this.path);
}
api = this.contextEventApi;
listeners = this.plugins.contextListeners;
return when.reduce(listeners, function(undef, listener) {
var d;
if(listener[type]) {
d = when.defer();
listener[type](d.resolver, api, data);
return d.promise;
}
return undef;
}, undef);
}},
_createComponents: { value: advice.beforeAsync(
scopeProto._createComponents,
function(parsed) {
var self = this;
return this._installPlugins(parsed.plugins).then(function() {
return self._contextEvent('initialize');
});
}
)},
_awaitInstances: { value: advice.afterAsync(
scopeProto._awaitInstances,
function() {
return this._contextEvent('ready');
}
)},
_destroyComponents: { value: advice.beforeAsync(
scopeProto._destroyComponents,
function() {
return this._contextEvent('shutdown');
}
)},
_releaseResources: { value: advice.beforeAsync(
scopeProto._releaseResources,
function() {
return this._contextEvent('destroy');
}
)}
});
return Container;
});
}(typeof define === 'function' ? define : function(factory) { module.exports = factory(require); }));
/** @license MIT License (c) copyright B Cavalier & J Hann */
/**
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*/
(function(define){ 'use strict';
define(function(require) {
var object, undef;
object = require('./object');
function WireContext() {}
WireContext.inherit = function(parent, api) {
var contextApi, context;
contextApi = object.inherit(parent);
object.mixin(contextApi, api);
WireContext.prototype = contextApi;
context = new WireContext();
WireContext.prototype = undef;
return context;
};
return WireContext;
});
}(typeof define === 'function' ? define : function(factory) { module.exports = factory(require); }));
/** @license MIT License (c) copyright B Cavalier & J Hann */
/**
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*/
(function(define){ 'use strict';
define(function(require) {
var object, array;
object = require('./object');
array = require('./array');
/**
* A base proxy for all components that wire creates. It allows wire's
* internals and plugins to work with components using a standard interface.
* WireProxy instances may be extended to specialize the behavior of the
* interface for a particular type of component. For example, there is a
* specialized version for DOM Nodes.
* @param {*} target value to be proxied
* @constructor
*/
function WireProxy(target) {
// read-only target
Object.defineProperty(this, 'target', { value: target });
}
WireProxy.prototype = {
/**
* Get the value of the named property. Sub-types should
* override to get properties from their targets in whatever
* specialized way is necessary.
* @param {string} property
* @returns {*} the value or undefined
*/
get: function (property) {
return this.target[property];
},
/**
* Set the value of the named property. Sub-types should
* override to set properties on their targets in whatever
* specialized way is necessary.
* @param {string} property
* @param {*} value
* @returns {*}
*/
set: function (property, value) {
this.target[property] = value;
return value;
},
/**
* Invoke the method, with the supplied args, on the proxy's
* target. Sub-types should override to invoke methods their
* targets in whatever specialized way is necessary.
* @param {string|function} method name of method to invoke or
* a function to call using proxy's target as the thisArg
* @param {array} args arguments to pass to method
* @returns {*} the method's return value
*/
invoke: function (method, args) {
var target = this.target;
if (typeof method === 'string') {
method = target[method];
}
return method.apply(target, array.fromArguments(args));
},
/**
* Destroy the proxy's target. Sub-types should override
* to destroy their targets in whatever specialized way is
* necessary.
*/
destroy: function() {},
/**
* Attempt to clone this proxy's target. Sub-types should
* override to clone their targets in whatever specialized
* way is necessary.
* @param {object|array|function} thing thing to clone
* @param {object} options
* @param {boolean} options.deep if true and thing is an Array, try to deep clone its contents
* @param {boolean} options.inherited if true and thing is an object, clone inherited and own properties.
* @returns {*}
*/
clone: function (options) {
// don't try to clone a primitive
var target = this.target;
if (typeof target == 'function') {
// cloneThing doesn't clone functions, so clone here:
return target.bind();
} else if (typeof target != 'object') {
return target;
}
return cloneThing(target, options || {});
}
};
WireProxy.create = createProxy;
WireProxy.isProxy = isProxy;
WireProxy.getTarget = getTarget;
WireProxy.extend = extendProxy;
return WireProxy;
/**
* Creates a new WireProxy for the supplied target. See WireProxy
* @param {*} target value to be proxied
* @returns {WireProxy}
*/
function createProxy(target) {
return new WireProxy(target);
}
/**
* Returns a new WireProxy, whose prototype is proxy, with extensions
* as own properties. This is the "official" way to extend the functionality
* of an existing WireProxy.
* @param {WireProxy} proxy proxy to extend
* @param extensions
* @returns {*}
*/
function extendProxy(proxy, extensions) {
if(!isProxy(proxy)) {
throw new Error('Cannot extend non-WireProxy');
}
return object.mixin(Object.create(proxy), extensions);
}
/**
* Returns true if it is a WireProxy
* @param {*} it
* @returns {boolean}
*/
function isProxy(it) {
return it instanceof WireProxy;
}
/**
* If it is a WireProxy (see isProxy), returns it's target. Otherwise,
* returns it;
* @param {*} it
* @returns {*}
*/
function getTarget(it) {
return isProxy(it) ? it.target : it;
}
/**
* Try to clone thing, which can be an object, Array, or Function
* @param {object|array|function} thing thing to clone
* @param {object} options
* @param {boolean} options.deep if true and thing is an Array, try to deep clone its contents
* @param {boolean} options.inherited if true and thing is an object, clone inherited and own properties.
* @returns {array|object|function} cloned thing
*/
function cloneThing (thing, options) {
var deep, inherited, clone, prop;
deep = options.deep;
inherited = options.inherited;
// Note: this filters out primitive properties and methods
if (typeof thing != 'object') {
return thing;
}
else if (thing instanceof Date) {
return new Date(thing.getTime());
}
else if (thing instanceof RegExp) {
return new RegExp(thing);
}
else if (Array.isArray(thing)) {
return deep
? thing.map(function (i) { return cloneThing(i, options); })
: thing.slice();
}
else {
clone = thing.constructor ? new thing.constructor() : {};
for (prop in thing) {
if (inherited || object.hasOwn(thing, prop)) {
clone[prop] = deep
? cloneThing(thing[prop], options)
: thing[prop];
}
}
return clone;
}
}
});
})(typeof define == 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }
);
\ No newline at end of file
/** @license MIT License (c) copyright 2010-2013 original author or authors */
/**
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*
* @author: Brian Cavalier
* @author: John Hann
*/
(function(define) { 'use strict';
define(function(require) {
var when;
when = require('when');
// Very simple advice functions for internal wire use only.
// This is NOT a replacement for meld. These advices stack
// differently and will not be as efficient.
return {
after: after,
beforeAsync: beforeAsync,
afterAsync: afterAsync
};
/**
* Execute advice after f, passing f's return value to advice
* @param {function} f function to advise
* @param {function} advice function to execute after f
* @returns {function} advised function
*/
function after(f, advice) {
return function() {
return advice.call(this, f.apply(this, arguments));
}
}
/**
* Execute f after a promise returned by advice fulfills. The same args
* will be passed to both advice and f.
* @param {function} f function to advise
* @param {function} advice function to execute before f
* @returns {function} advised function which always returns a promise
*/
function beforeAsync(f, advice) {
return function() {
var self, args;
self = this;
args = arguments;
return when(args, function() {
return advice.apply(self, args);
}).then(function() {
return f.apply(self, args);
});
}
}
/**
* Execute advice after a promise returned by f fulfills. The same args
* will be passed to both advice and f.
* @param {function} f function to advise
* @param {function} advice function to execute after f
* @returns {function} advised function which always returns a promise
*/
function afterAsync(f, advice) {
return function() {
var self = this;
return when(arguments, function(args) {
return f.apply(self, args);
}).then(function(result) {
return advice.call(self, result);
});
}
}
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));
...@@ -5,15 +5,16 @@ ...@@ -5,15 +5,16 @@
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
*/ */
(function(define){ (function(define){ 'use strict';
define(function() { define(function() {
"use strict";
var slice = [].slice; var slice = [].slice;
return { return {
delegate: delegateArray, delegate: delegateArray,
fromArguments: fromArguments fromArguments: fromArguments,
union: union
}; };
/** /**
...@@ -30,6 +31,28 @@ define(function() { ...@@ -30,6 +31,28 @@ define(function() {
return slice.call(args, index||0); return slice.call(args, index||0);
} }
/**
* Returns a new set that is the union of the two supplied sets
* @param {Array} a1 set
* @param {Array} a2 set
* @returns {Array} union of a1 and a2
*/
function union(a1, a2) {
// If either is empty, return the other
if(!a1.length) {
return a2.slice();
} else if(!a2.length) {
return a1.slice();
}
return a2.reduce(function(union, a2item) {
if(union.indexOf(a2item) === -1) {
union.push(a2item);
}
return union;
}, a1.slice());
}
}); });
})(typeof define == 'function' })(typeof define == 'function'
// AMD // AMD
......
/** @license MIT License (c) copyright B Cavalier & J Hann */
/**
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*/
(function(define){
define(function(require) {
"use strict";
var when, array;
when = require('when');
array = require('./array');
return {
sequence: sequence,
until: until
};
/**
* Run the supplied async tasks in sequence, with no overlap.
* @param tasks {Array} array of functions
* @return {Promise} promise that resolves when all tasks
* have completed
*/
function sequence(tasks /*, args... */) {
var args = Array.prototype.slice.call(arguments, 1);
return when.reduce(tasks, function(results, task) {
return when(task.apply(null, args), function(result) {
results.push(result);
return results;
});
}, []);
};
function until(work, interval, verifier) {
var deferred = when.defer();
verifier = verifier || function () { return false; };
function schedule() {
setTimeout(vote, interval);
}
function vote() {
when(work(),
function (result) {
when(verifier(result), handleNext, schedule);
function handleNext(verification) {
return verification ? deferred.resolve(result) : schedule();
}
},
deferred.reject
);
}
schedule();
return deferred.promise;
}
});
})(typeof define == 'function' && define.amd ? define : function(factory) { module.exports = factory(require); });
\ No newline at end of file
...@@ -29,9 +29,14 @@ ...@@ -29,9 +29,14 @@
* *
*/ */
(function(define){ (function(define){ 'use strict';
define(['when', './array', './functional'], function(when, array, functional) { define(function(require) {
"use strict";
var when, array, functional;
when = require('when');
array = require('./array');
functional = require('./functional');
return { return {
parse: parse, parse: parse,
...@@ -201,9 +206,5 @@ define(['when', './array', './functional'], function(when, array, functional) { ...@@ -201,9 +206,5 @@ define(['when', './array', './functional'], function(when, array, functional) {
// AMD // AMD
? define ? define
// CommonJS // CommonJS
: function(deps, factory) { : function(factory) { module.exports = factory(require); }
module.exports = factory.apply(this, deps.map(function(x) {
return require(x);
}));
}
); );
\ No newline at end of file
...@@ -5,19 +5,13 @@ ...@@ -5,19 +5,13 @@
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
*/ */
(function(define){ (function(define){ 'use strict';
define(['require', 'when', './array', './object', './async', './moduleLoader', './lifecycle', './resolver', '../base'], define(function(require) {
function(require, when, array, object, async, createModuleLoader, Lifecycle, Resolver, basePlugin) {
"use strict"; var loader, Container;
var defer, emptyObject, undef; loader = require('./loader');
Container = require('./Container');
// Local refs to when.js
defer = when.defer;
emptyObject = {};
function WireContext() {}
/** /**
* Creates a new context from the supplied specs, with the supplied parent context. * Creates a new context from the supplied specs, with the supplied parent context.
...@@ -25,874 +19,33 @@ function(require, when, array, object, async, createModuleLoader, Lifecycle, Res ...@@ -25,874 +19,33 @@ function(require, when, array, object, async, createModuleLoader, Lifecycle, Res
* literal specs. All spec module ids will be loaded, and then all specs will be * literal specs. All spec module ids will be loaded, and then all specs will be
* merged from left-to-right (rightmost wins), and the resulting, merged spec will * merged from left-to-right (rightmost wins), and the resulting, merged spec will
* be wired. * be wired.
*
* @private * @private
* *
* @param specs {String|Array|*} * @param {String|Object|String[]|Object[]} specs
* @param parent {Object} parent content * @param {Object} parent context
* @param options {Object} * @param {Object} [options]
* *
* @return {Promise} a promise for the new context * @return {Promise} a promise for the new context
*/ */
function createContext(specs, parent, options) { return function createContext(specs, parent, options) {
// Do the actual wiring after all specs have been loaded // Do the actual wiring after all specs have been loaded
function doWireContext(spec) {
return createScope(spec, parent, options);
}
var moduleLoader = getModuleLoader(parent, options);
return when(specs, function(specs) {
return Array.isArray(specs)
? when(ensureAllSpecsLoaded(specs, moduleLoader), doWireContext)
: when(isString(specs) ? moduleLoader(specs) : specs, doWireContext);
});
}
return createContext;
/**
* Do the work of creating a new scope and fully wiring its contents
* @private
*
* @param scopeDef {Object} The spec (or portion of a spec) to be wired into a new scope
* @param parent {scope} scope to use as the parent, and thus from which to inherit
* plugins, components, etc.
* @param [options] {Object} scope options
*
* @return {Promise} a promise for the new scope
*/
function createScope(scopeDef, parent, options) {
var scope, scopeParent, context, config, contextHandlers,
proxiedComponents, components, lifecycle, resolver, inflightRefs,
pluginApi, resolvers, factories, facets, listeners, proxiers,
moduleLoader, modulesToLoad, plugins,
wireApi, modulesReady, scopeReady, scopeDestroyed, doDestroy;
inflightRefs = [];
// Empty parent scope if none provided
if(!parent) { parent = {}; }
if(!options) { options = {}; } if(!options) { options = {}; }
if(!parent) { parent = {}; }
inheritFromParent(parent, options); options.createContext = createContext;
createPluginApi();
// TODO: Find a better way to load and scan the base plugin
scanPlugin(basePlugin);
configureContext(options);
pluginApi.resolver = resolver;
// Setup overwritable doDestroy so that this context
// can only be destroyed once
doDestroy = function () {
// Retain a do-nothing doDestroy() func, in case
// it is called again for some reason.
doDestroy = function () {
};
return when(destroyContext(), executeDestroyers);
};
context = {
spec: scopeDef,
components: components,
config: config
};
function executeInitializers() {
return async.sequence(contextHandlers.init, context);
}
function executeDestroyers() {
return async.sequence(contextHandlers.destroy, context);
}
return executeInitializers()
.then(function() {
var componentsToCreate = parseSpec(scopeDef, scopeReady);
createComponents(componentsToCreate, scopeDef);
// Once all modules are loaded, all the components can finish
ensureAllModulesLoaded();
// Return promise
// Context will be ready when this promise resolves
return scopeReady.promise;
});
//
// Initialization
//
function inheritFromParent(parent, options) {
// Descend scope and plugins from parent so that this scope can
// use them directly via the prototype chain
WireContext.prototype = createWireApi(object.inherit(parent.components));
components = new WireContext();
WireContext.prototype = undef;
resolvers = object.inherit(parent.resolvers);
factories = object.inherit(parent.factories);
facets = object.inherit(parent.facets);
// Set/override integral resolvers and factories
resolvers.wire = wireResolver;
factories.wire = wireFactory;
listeners = array.delegate(parent.listeners);
// Proxies is an array, have to concat
proxiers = array.delegate(parent.proxiers);
proxiedComponents = [];
plugins = [];
modulesToLoad = [];
modulesReady = defer();
scopeReady = defer();
scopeDestroyed = defer();
moduleLoader = getModuleLoader(parent, options);
// A proxy of this scope that can be used as a parent to
// any child scopes that may be created.
scopeParent = {
moduleLoader: moduleLoader,
components: components,
destroyed: scopeDestroyed.promise
};
// Full scope definition. This will be given to sub-scopes,
// but should never be given to child contexts
scope = Object.create(scopeParent);
scope.resolvers = resolvers;
scope.factories = factories;
scope.facets = facets;
scope.listeners = listeners;
scope.proxiers = proxiers;
scope.resolveRef = resolveRefName;
scope.destroy = destroy;
scope.path = createPath(options.name, parent.path);
// When the parent begins its destroy phase, this child must
// begin its destroy phase and complete it before the parent.
// The context hierarchy will be destroyed from child to parent.
if (parent.destroyed) {
when(parent.destroyed, destroy);
}
}
function createWireApi(context) {
wireApi = context.wire = wireChild;
wireApi.destroy = context.destroy = apiDestroy;
// Consider deprecating resolve
// Any reference you could resolve using this should simply be
// injected instead.
wireApi.resolve = context.resolve = apiResolveRef;
return context;
}
function createPluginApi() {
// Plugin API
// wire() API that is passed to plugins.
pluginApi = function (spec, name, path) {
return createItem(spec, createPath(name, path));
};
pluginApi.resolveRef = apiResolveRef;
pluginApi.getProxy = getProxy;
pluginApi.loadModule = getModule;
}
function configureContext(options) {
// TODO: This configuration object needs to be abstracted and reused
config = {
pluginApi: pluginApi,
resolvers: resolvers,
facets: facets,
listeners: listeners
};
lifecycle = new Lifecycle(config);
resolver = new Resolver(config);
contextHandlers = {
init: array.delegate(options.init),
destroy: array.delegate(options.destroy)
};
}
function parseSpec(scopeDef, scopeReady) {
var promises, componentsToCreate, d;
promises = [];
componentsToCreate = {};
// Setup a promise for each item in this scope
for (var name in scopeDef) {
// An initializer may have inserted concrete components
// into the context. If so, they override components of the
// same name from the input spec
if(!(components.hasOwnProperty(name))) {
// Hack for 0.9.x + when 2.0 compat only. To be removed in 0.10.0
d = defer();
componentsToCreate[name] = d.resolver;
components[name] = d.promise;
promises.push(d.promise);
}
}
// When all scope item promises are resolved, the scope
// is ready. When this scope is ready, resolve the promise
// with the objects that were created
when.all(promises).then(
function() {
scopeReady.resolve(components);
},
scopeReady.reject
);
return componentsToCreate;
}
//
// Context Startup
//
function createComponents(componentsToCreate, spec) {
// Process/create each item in scope and resolve its
// promise when completed.
for (var name in componentsToCreate) {
createScopeItem(name, spec[name], componentsToCreate[name]);
}
}
function ensureAllModulesLoaded() {
async.until(waitForModules, 0, allModulesLoaded).then(
function() { modulesReady.resolve(); },
function() { modulesReady.reject(); }
);
function waitForModules() {
var modules = modulesToLoad;
modulesToLoad = [];
return when.all(modules);
}
function allModulesLoaded() {
return modulesToLoad.length === 0;
}
}
//
// Context Destroy
//
function destroyContext() {
var shutdown;
scopeDestroyed.resolve();
// TODO: Clear out the context prototypes?
shutdown = when.reduce(proxiedComponents, function(unused, proxied) {
return lifecycle.shutdown(proxied);
}, undef);
return when(shutdown, function () {
function deleteAll(container) {
for(var p in container) {
delete container[p];
}
}
deleteAll(components);
deleteAll(scope);
return when.reduce(proxiedComponents, function(p, proxied) {
when(p, function() {
proxied.destroy();
});
}, undef)
.then(function() {
// Free Objects
components = scope = parent
= resolvers = factories = facets = listeners
= wireApi = proxiedComponents = proxiers = plugins
= undef;
return scopeDestroyed.promise;
});
});
}
//
// Context API
//
// API of a wired context that is returned, via promise, to
// the caller. It will also have properties for all the
// objects that were created in this scope.
/**
* Resolves a reference in the current context, using any reference resolvers
* available in the current context
*
* @param ref {String} reference name (may contain resolver prefix, e.g. "resolver!refname"
*/
function apiResolveRef(ref, onBehalfOf) {
return when(resolveRefName(ref, {}, onBehalfOf));
}
/**
* Destroys the current context
*/
function apiDestroy() {
return destroy();
}
/**
* Wires a child spec with this context as its parent
* @param spec
*/
function wireChild(spec, options) {
return createContext(spec, scopeParent, options);
}
//
// Scope functions
//
function createPath(name, basePath) {
var path = basePath || scope.path;
return (path && name) ? (path + '.' + name) : name;
}
function createScopeItem(name, val, resolver) {
// NOTE: Order is important here.
// The object & local property assignment MUST happen before
// the chain resolves so that the concrete item is in place.
// Otherwise, the whole scope can be marked as resolved before
// the final item has been resolved.
var p = createItem(val, name);
return when(p, function (resolved) {
makeResolvable(name, resolved);
resolver.resolve(resolved);
}, resolver.reject);
}
/**
* Make a component resolvable under the given name
* @param name {String} name by which to allow the component to be resolved
* @param component {*} component
*/
function makeResolvable(name, component) {
components[name] = getResolvedValue(component);
}
function createItem(val, name) {
var created;
if (resolver.isRef(val)) {
// Reference
created = resolveRef(val, name);
} else if (Array.isArray(val)) {
// Array
created = createArray(val, name);
} else if (object.isObject(val)) {
// component spec, create the component
created = realizeComponent(val, name);
} else {
// Plain value
created = when.resolve(val);
}
return created;
}
function getModule(moduleId, spec) {
var module = defer();
scanPluginWhenLoaded(typeof moduleId == 'string'
? moduleLoader(moduleId)
: moduleId, module.resolver);
return module.promise; var moduleLoader = loader(options.require, parent.moduleLoader);
function scanPluginWhenLoaded(loadModulePromise, moduleReadyResolver) { return moduleLoader.merge(specs).then(function(spec) {
var container = new Container(parent, options);
var loadPromise = when(loadModulePromise, function (module) { // Expose only the component instances and controlled API
return when(scanPlugin(module, spec), function() { return container.init(spec).then(function(self) {
modulesReady.promise.then(function() { return self.instances;
return module;
}).then(moduleReadyResolver.resolve, moduleReadyResolver.reject);
}, moduleReadyResolver.reject);
}, moduleReadyResolver.reject);
modulesToLoad && modulesToLoad.push(loadPromise);
}
}
function scanPlugin(module, spec) {
if (module && isFunction(module.wire$plugin) && plugins.indexOf(module.wire$plugin) === -1) {
// Add to singleton plugins list to only allow one instance
// of this plugin in the current context.
plugins.push(module.wire$plugin);
// Initialize the plugin for this context
return when(module.wire$plugin(scopeReady.promise, scopeDestroyed.promise, spec),
function(plugin) {
plugin && registerPlugin(plugin);
return module;
}
);
}
return module;
}
function registerPlugin(plugin) {
addPlugin(plugin.resolvers, resolvers);
addPlugin(plugin.factories, factories);
addPlugin(plugin.facets, facets);
listeners.push(plugin);
addProxies(plugin.proxies);
}
function addProxies(proxiesToAdd) {
if (!proxiesToAdd) { return; }
var newProxiers, p, i = 0;
newProxiers = [];
while (p = proxiesToAdd[i++]) {
if (proxiers.indexOf(p) < 0) {
newProxiers.push(p);
}
}
scope.proxiers = proxiers = newProxiers.concat(proxiers);
}
function addPlugin(src, registry) {
var name;
for (name in src) {
if (registry.hasOwnProperty(name)) {
throw new Error("Two plugins for same type in scope: " + name);
}
registry[name] = src[name];
}
}
function createArray(arrayDef, name) {
// Minor optimization, if it's an empty array spec, just return
// an empty array.
return arrayDef.length
? when.map(arrayDef, function(item) {
return createItem(item, name + '[]');
})
: [];
}
/**
* Fully realize a component from a spec: create, initialize, then
* startup.
* @param spec {Object} component spec
* @param name {String} component name
* @return {Promise} promise for the fully realized component
*/
function realizeComponent(spec, name) {
// Look for a factory, then use it to create the object
return when(findFactory(spec),
function (factory) {
var component = defer();
if (!spec.id) {
spec.id = name;
}
factory(component.resolver, spec, pluginApi);
return processComponent(component.promise, spec, name);
},
function () {
// No factory found, treat object spec as a nested scope
return createScope(spec, scope, { name: name }).then(function(context) {
return safeMixin({}, context);
});
}
);
}
/**
* Move component through all phases of the component lifecycle up
* to ready.
* @param component {*} component or promise for a component
* @param spec {Object} component spec
* @param name {String} component name
* @return {Promise} promise for the component in the ready state
*/
function processComponent(component, spec, name) {
return when(component, function(component) {
return when(createProxy(component, spec), function(proxy) {
return lifecycle.init(proxy);
}).then(function(proxy) {
// Components become resolvable after the initialization phase
// This allows circular references to be resolved after init
makeResolvable(name, proxy.target);
return lifecycle.startup(proxy);
}).then(function(proxy) {
return proxy.target;
});
});
}
/**
* Select the factory plugin to use to create a component
* for the supplied component spec
* @param spec {Object} component spec
* @return {Promise} promise for factory, rejected if no suitable
* factory can be found
*/
function findFactory(spec) {
// FUTURE: Should not have to wait for all modules to load,
// but rather only the module containing the particular
// factory we need. But how to know which factory before
// they are all loaded?
// Maybe need a special syntax for factories, something like:
// create: "factory!whatever-arg-the-factory-takes"
// args: [factory args here]
function getFactory() {
var f, factory;
for (f in factories) {
if (spec.hasOwnProperty(f)) {
factory = factories[f];
break;
}
}
// Intentionally returns undefined if no factory found
return factory;
}
return getFactory() || when(modulesReady.promise, function () {
return getFactory() || when.reject(spec);
});
}
function createProxy(object, spec) {
return when(modulesReady.promise, function() {
var proxier, proxy, id, i;
i = 0;
while ((proxier = proxiers[i++]) && !(proxy = proxier(object, spec))) {}
proxy.target = object;
proxy.spec = spec;
if(spec) {
id = spec && spec.id;
proxy.id = id;
proxy.path = createPath(id);
proxiedComponents.push(proxy);
}
return proxy;
}); });
} });
};
/**
* Return a proxy for the component name, or concrete component
* @param nameOrComponent {String|*} if it's a string, consider it to be a component name
* otherwise, consider it to be a concrete component
* @return {Object|Promise} proxy or promise for proxy of the component
*/
function getProxy(nameOrComponent, onBehalfOf) {
return typeof nameOrComponent != 'string'
? createProxy(nameOrComponent)
: when(resolveRefName(nameOrComponent, {}, onBehalfOf), function(component) {
return createProxy(component);
});
}
//
// Destroy
//
/**
* Destroy the current context. Waits for the context to finish
* wiring and then immediately destroys it.
*
* @return {Promise} a promise that will resolve once the context
* has been destroyed
*/
function destroy() {
return when(scopeReady.promise, doDestroy, doDestroy);
}
//
// Reference resolution
//
/**
* Resolves the supplied ref as a local component name, or delegates
* to registered resolver plugins
* @param ref {Object} reference object returned by resolver.parse or resolver.create
* @param scope {Object} scope for resolving local component names
* @param [onBehalfOf] {*} optional indicator of the party requesting the reference
* @return {Promise} a promise for the fully resolved reference value
*/
function doResolveRef(ref, scope, onBehalfOf) {
return ref.resolver
? when(modulesReady.promise, ref.resolve)
: doResolveDeepRef(ref.name, scope);
}
/**
* Resolves a component references, traversing one level of "." nesting
* if necessarily (e.g. "thing.property")
* @param name {String} component name or dot-separated path
* @param scope {Object} scope in which to resolve the reference
* @return {Promise} promise for the referenced component or property
*/
function doResolveDeepRef(name, scope) {
var parts = name.split('.');
if(parts.length > 2) {
return when.reject('Only 1 "." is allowed in refs: ' + name);
}
return when.reduce(parts, function(scope, segment) {
return segment in scope
? scope[segment]
: when.reject('Cannot resolve ref: ' + name);
}, scope);
}
/**
* @param ref {*} any reference format supported by the registered resolver
* @param name {String} component name to which the the resolved value of the reference
* will eventually be assigned. Used to avoid self-circular references.
* @return {Promise} a promise for the fully resolved reference value
*/
function resolveRef(ref, name) {
var scope;
ref = resolver.parse(ref);
scope = name == ref.name && parent.components ? parent.components : components;
return doResolveRef(ref, scope, name);
}
/**
*
* @param refName {String} name of reference to resolve. Can be either a
* component name, or a plugin-style reference, e.g. plugin!reference
* @param [options] {Object} additional options to pass to reference resolver
* plugins if the refName requires a plugin to resolve
* @param [onBehalfOf] {*} optional indicator of the party requesting the reference
* @return {Promise} a promise for the fully resolved reference value
*/
function resolveRefName(refName, options, onBehalfOf) {
return doResolveRef(resolver.create(refName, options), components, onBehalfOf);
}
/**
* Builtin reference resolver that resolves to the context-specific
* wire function.
*
* @param resolver {Resolver} resolver to resolve
*/
function wireResolver(resolver /*, name, refObj, wire*/) {
resolver.resolve(wireApi);
}
//
// Built-in Factories
//
/**
* Factory that creates either a child context, or a *function* that will create
* that child context. In the case that a child is created, this factory returns
* a promise that will resolve when the child has completed wiring.
*
* @param resolver {Resolver} resolver to resolve with the created component
* @param spec {Object} portion of the spec for the component to be created
*/
function wireFactory(resolver, spec, wire) {
//
// TODO: Move wireFactory to its own module
//
var options, module, provide, defer, waitParent;
options = spec.wire;
// Get child spec and options
if (isString(options)) {
module = options;
} else {
module = options.spec;
waitParent = options.waitParent;
defer = options.defer;
provide = options.provide;
}
function init(context) {
if(provide) {
return when(wire(provide), function(provides) {
safeMixin(context.components, provides);
});
}
}
function createChild(/** {Object|String}? */ mixin) {
var spec, config;
spec = mixin ? [].concat(module, mixin) : module;
config = { init: init };
var child = wireChild(spec, config);
return defer ? child
: when(child, function(child) {
return child.hasOwnProperty('$exports') ? child.$exports : child;
});
}
if (defer) {
// Resolve with the createChild *function* itself
// which can be used later to wire the spec
resolver.resolve(createChild);
} else if(waitParent) {
var childPromise = when(scopeReady, function() {
// ensure nothing is passed to createChild here
return createChild();
});
resolver.resolve(new ResolvedValue(childPromise));
} else {
resolver.resolve(createChild(spec));
}
}
} // createScope
function getModuleLoader(context, options) {
return options && options.require
? createModuleLoader(options.require)
: context.moduleLoader;
}
/**
* Given a mixed array of strings and non-strings, returns a promise that will resolve
* to an array containing resolved modules by loading all the strings found in the
* specs array as module ids
* @private
*
* @param specs {Array} mixed array of strings and non-strings
*
* @returns {Promise} a promise that resolves to an array of resolved modules
*/
function ensureAllSpecsLoaded(specs, loadModule) {
return when.reduce(specs, function(merged, module) {
return isString(module)
? when(loadModule(module), function(spec) { return safeMixin(merged, spec); })
: safeMixin(merged, module);
}, {});
}
/**
* Add components in from to those in to. If duplicates are found, it
* is an error.
* @param to {Object} target object
* @param from {Object} source object
*/
function safeMixin(to, from) {
for (var name in from) {
if (from.hasOwnProperty(name) && !(name in emptyObject)) {
if (to.hasOwnProperty(name)) {
throw new Error("Duplicate component name in sibling specs: " + name);
} else {
to[name] = from[name];
}
}
}
return to;
}
function isString(it) {
return typeof it == 'string';
}
/**
* Standard function test
* @param it
*/
function isFunction(it) {
return typeof it == 'function';
}
/**
* Special object to hold a Promise that should not be resolved, but
* rather should be passed through a promise chain *as the resolution value*
* @param val
*/
function ResolvedValue(val) {
this.value = val;
}
/**
* If it is a PromiseKeeper, return it.value, otherwise return it. See
* PromiseKeeper above for an explanation.
* @param it anything
*/
function getResolvedValue(it) {
return it instanceof ResolvedValue ? it.value : it;
}
// TODO: Start using this after compatible curl release
// TODO: Move to somewhere more logical and modular, like lib/resolver.js
}); });
})(typeof define == 'function' }(typeof define === 'function' ? define : function(factory) { module.exports = factory(require); }));
// AMD
? define
// CommonJS
: function(deps, factory) {
module.exports = factory.apply(this, [require].concat(deps.slice(1).map(function(x) {
return require(x);
})));
}
);
\ No newline at end of file
...@@ -10,9 +10,12 @@ ...@@ -10,9 +10,12 @@
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
*/ */
(function (define) { (function (define) {
define(function () { define(function (require) {
var classRx, trimLeadingRx, splitClassNamesRx, nodeProxyInvoke; var WireProxy, priority, classRx, trimLeadingRx, splitClassNamesRx, nodeProxyInvoke;
WireProxy = require('../WireProxy');
priority = require('../plugin/priority');
classRx = '(\\s+|^)(classNames)(\\b(?![\\-_])|$)'; classRx = '(\\s+|^)(classNames)(\\b(?![\\-_])|$)';
trimLeadingRx = /^\s+/; trimLeadingRx = /^\s+/;
...@@ -211,58 +214,74 @@ define(function () { ...@@ -211,58 +214,74 @@ define(function () {
parent.appendChild(node); parent.appendChild(node);
} }
function nodeProxy (node) { function isNode(it) {
return typeof Node === "object"
? it instanceof Node
: it && typeof it === "object" && typeof it.nodeType === "number" && typeof it.nodeName==="string";
}
function NodeProxy() {}
if (!node || !node.nodeType || !node.setAttribute || !node.getAttribute) { NodeProxy.prototype = {
return; get: function (name) {
} var node = this.target;
return { if (name in node) {
return node[name];
get: function (name) {
if (name in node) {
return node[name];
}
else {
return node.getAttribute(name);
}
},
set: function (name, value) {
if (name in node) {
return node[name] = value;
}
else {
return node.setAttribute(name, value);
}
},
invoke: function (method, args) {
return nodeProxyInvoke(node, method, args);
},
destroy: function () {
// if we added a destroy method on the node, call it.
// TODO: find a better way to release events instead of using this mechanism
if (node.destroy) {
node.destroy();
}
// removal from document will destroy node as soon as all
// references to it go out of scope.
var parent = node.parentNode;
if (parent) {
parent.removeChild(node);
}
},
clone: function (options) {
if (!options) {
options = {};
}
// default is to clone deep (when would anybody not want deep?)
return node.cloneNode(!('deep' in options) || options.deep);
} }
}; else {
return node.getAttribute(name);
}
},
set: function (name, value) {
var node = this.target;
if (name in node) {
return node[name] = value;
}
else {
return node.setAttribute(name, value);
}
},
invoke: function (method, args) {
return nodeProxyInvoke(this.target, method, args);
},
destroy: function () {
var node = this.target;
// if we added a destroy method on the node, call it.
// TODO: find a better way to release events instead of using this mechanism
if (node.destroy) {
node.destroy();
}
// removal from document will destroy node as soon as all
// references to it go out of scope.
var parent = node.parentNode;
if (parent) {
parent.removeChild(node);
}
},
clone: function (options) {
if (!options) {
options = {};
}
// default is to clone deep (when would anybody not want deep?)
return this.target.cloneNode(!('deep' in options) || options.deep);
}
};
proxyNode.priority = priority.basePriority;
function proxyNode (proxy) {
if (!isNode(proxy.target)) {
return proxy;
}
return WireProxy.extend(proxy, NodeProxy.prototype);
} }
return { return {
...@@ -274,7 +293,7 @@ define(function () { ...@@ -274,7 +293,7 @@ define(function () {
addClass: addClass, addClass: addClass,
removeClass: removeClass, removeClass: removeClass,
toggleClass: toggleClass, toggleClass: toggleClass,
nodeProxy: nodeProxy proxyNode: proxyNode
}; };
...@@ -282,5 +301,5 @@ define(function () { ...@@ -282,5 +301,5 @@ define(function () {
}( }(
typeof define == 'function' && define.amd typeof define == 'function' && define.amd
? define ? define
: function (factory) { module.exports = factory(); } : function (factory) { module.exports = factory(require); }
)); ));
...@@ -11,12 +11,12 @@ ...@@ -11,12 +11,12 @@
* Licensed under the MIT License at: * Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
*/ */
(function (define) { (function (define) { 'use strict';
define(['when'], function (when) { define(function (require) {
"use strict";
var slice, undef; var when, slice;
when = require('when');
slice = [].slice; slice = [].slice;
/** /**
...@@ -44,43 +44,6 @@ define(['when'], function (when) { ...@@ -44,43 +44,6 @@ define(['when'], function (when) {
}; };
} }
/**
* Creates a partial function that weaves arguments into returned function
* @param f {Function}
* @param weaves {Object} sparse array-like object (with length property)
* These weaves are spliced into the run-time arguments. Each property
* whose is a positive integer describes a point at which to splice
* an argument.
* @return {Function}
*/
function weave (f, weaves) {
return function () {
var length = Math.max(weaves.length || 0, arguments.length, f.length);
return f.apply(this, injectArgs(weaves, length, arguments));
};
}
/**
* @private
* @param weaves {Object} sparse array-like object (with length property)
* @param length {Number} how long the resulting arguments list should be
* @param args {Arguments} run-time arguments
* @return {Array} interwoven arguments
*/
function injectArgs (weaves, length, args) {
var woven = [], i;
args = slice.call(args); // copy
for (i = 0; i < length; i++) {
if (i in weaves) {
woven.push(weaves[i]);
}
if (args.length > 0) {
woven.push(args.shift());
}
}
return woven;
}
/** /**
* Compose functions * Compose functions
* @param funcs {Array} array of functions to compose * @param funcs {Array} array of functions to compose
...@@ -95,7 +58,9 @@ define(['when'], function (when) { ...@@ -95,7 +58,9 @@ define(['when'], function (when) {
return function composed() { return function composed() {
var context = this; var context = this;
return funcs.reduce(function(result, f) { return funcs.reduce(function(result, f) {
return f.call(context, result); return conditionalWhen(result, function(result) {
return f.call(context, result);
});
}, first.apply(this, arguments)); }, first.apply(this, arguments));
}; };
} }
...@@ -106,8 +71,9 @@ define(['when'], function (when) { ...@@ -106,8 +71,9 @@ define(['when'], function (when) {
* @param proxy {Object} wire proxy on which to invoke the final method of the composition * @param proxy {Object} wire proxy on which to invoke the final method of the composition
* @param composeString {String} function composition string * @param composeString {String} function composition string
* of the form: 'transform1 | transform2 | ... | methodOnProxyTarget" * of the form: 'transform1 | transform2 | ... | methodOnProxyTarget"
* @param wire.resolveRef {Function} function to use is resolving references, returns a promise * @param {function} wire
* @param wire.getProxy {Function} function used to obtain a proxy for a component * @param {function} wire.resolveRef function to use is resolving references, returns a promise
* @param {function} wire.getProxy function used to obtain a proxy for a component
* @return {Promise} a promise for the composed function * @return {Promise} a promise for the composed function
*/ */
compose.parse = function parseCompose(proxy, composeString, wire) { compose.parse = function parseCompose(proxy, composeString, wire) {
...@@ -115,7 +81,7 @@ define(['when'], function (when) { ...@@ -115,7 +81,7 @@ define(['when'], function (when) {
var bindSpecs, resolveRef, getProxy; var bindSpecs, resolveRef, getProxy;
if(typeof composeString != 'string') { if(typeof composeString != 'string') {
return wire(composeString, function(func) { return wire(composeString).then(function(func) {
return createProxyInvoker(proxy, func); return createProxyInvoker(proxy, func);
}); });
} }
...@@ -130,7 +96,7 @@ define(['when'], function (when) { ...@@ -130,7 +96,7 @@ define(['when'], function (when) {
}; };
} }
function createBound(bindSpec) { function createBound(proxy, bindSpec) {
var target, method; var target, method;
target = bindSpec.split('.'); target = bindSpec.split('.');
...@@ -151,12 +117,11 @@ define(['when'], function (when) { ...@@ -151,12 +117,11 @@ define(['when'], function (when) {
return createProxyInvoker(proxy, method); return createProxyInvoker(proxy, method);
}); });
} else { } else {
return when(resolveRef(bindSpec), if(proxy && typeof proxy.get(bindSpec) == 'function') {
null, return createProxyInvoker(proxy, bindSpec);
function() { } else {
return createProxyInvoker(proxy, bindSpec); return resolveRef(bindSpec);
} }
);
} }
} }
...@@ -166,7 +131,7 @@ define(['when'], function (when) { ...@@ -166,7 +131,7 @@ define(['when'], function (when) {
// Then add the final context[method] to the array of funcs and // Then add the final context[method] to the array of funcs and
// return the composition. // return the composition.
return when.reduce(bindSpecs, function(funcs, bindSpec) { return when.reduce(bindSpecs, function(funcs, bindSpec) {
return when(createBound(bindSpec), function(func) { return when(createBound(proxy, bindSpec), function(func) {
funcs.push(func); funcs.push(func);
return funcs; return funcs;
}); });
...@@ -178,10 +143,15 @@ define(['when'], function (when) { ...@@ -178,10 +143,15 @@ define(['when'], function (when) {
); );
}; };
function conditionalWhen(promiseOrValue, onFulfill, onReject) {
return when.isPromise(promiseOrValue)
? when(promiseOrValue, onFulfill, onReject)
: onFulfill(promiseOrValue);
}
return { return {
compose: compose, compose: compose,
partial: partial, partial: partial
weave: weave
}; };
}); });
...@@ -189,9 +159,5 @@ define(['when'], function (when) { ...@@ -189,9 +159,5 @@ define(['when'], function (when) {
// AMD // AMD
? define ? define
// CommonJS // CommonJS
: function(deps, factory) { : function(factory) { module.exports = factory(require); }
module.exports = factory.apply(this, deps.map(function(x) {
return require(x);
}));
}
); );
/** @license MIT License (c) copyright B Cavalier & J Hann */
/**
* DirectedGraph
* @author: brian@hovercraftstudios.com
*/
(function(define) {
define(function() {
/**
* A simple directed graph
* @constructor
*/
function DirectedGraph() {
this.vertices = {};
}
DirectedGraph.prototype = {
/**
* Add a new edge from one vertex to another
* @param {string} from vertex at the tail of the edge
* @param {string} to vertex at the head of the edge
*/
addEdge: function(from, to) {
this._getOrCreateVertex(to);
this._getOrCreateVertex(from).edges[to] = 1;
},
/**
* Adds and initializes new vertex, or returns an existing vertex
* if one with the supplied name already exists
* @param {string} name vertex name
* @return {object} the new vertex, with an empty edge set
* @private
*/
_getOrCreateVertex: function(name) {
var v = this.vertices[name];
if(!v) {
v = this.vertices[name] = { name: name, edges: {} };
}
return v;
},
/**
* Removes an edge, if it exits
* @param {string} from vertex at the tail of the edge
* @param {string} to vertex at the head of the edge
*/
removeEdge: function(from, to) {
var outbound = this.vertices[from];
if(outbound) {
delete outbound.edges[to];
}
},
/**
* Calls lambda once for each vertex in the graph passing
* the vertex as the only param.
* @param {function} lambda
*/
eachVertex: function(lambda) {
var vertices, v;
vertices = this.vertices;
for(v in vertices) {
lambda(vertices[v]);
}
},
/**
* Calls lambda once for every outbound edge of the supplied vertex
* @param {string} vertex vertex name whose edges will be passed to lambda
* @param {function} lambda
*/
eachEdgeFrom: function(vertex, lambda) {
var v, e, vertices;
vertices = this.vertices;
v = vertices[vertex];
if(!v) {
return;
}
for(e in v.edges) {
lambda(v, vertices[e]);
}
}
};
return DirectedGraph;
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
/** @license MIT License (c) copyright B Cavalier & J Hann */
/**
* formatCycles
* @author: brian@hovercraftstudios.com
*/
(function(define) {
define(function() {
/**
* If there are cycles, format them for output
* @param {Array} cycles array of reference resolution cycles
* @return {String} formatted string
*/
return function formatCycles(cycles) {
return cycles.map(function (sc) {
return '[' + sc.map(function (v) {
return v.name;
}
).join(', ') + ']';
}).join(', ');
}
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
/** @license MIT License (c) copyright B Cavalier & J Hann */
/**
* Tarjan directed graph cycle detection
* @author: brian@hovercraftstudios.com
*/
(function(define) {
define(function() {
var undef;
/**
* Tarjan directed graph cycle detection.
* See http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm
*
* WARNING: For efficiency, this adds properties to the vertices in the
* graph. It doesn't really matter for wire's internal purposes.
*
* @param {DirectedGraph} digraph
* @return {Array} each element is a set (Array) of vertices involved
* in a cycle.
*/
return function tarjan(digraph) {
var index, stack, scc;
index = 0;
stack = [];
scc = [];
// Clear out any old cruft that may be hanging around
// from a previous run. Maybe should do this afterward?
digraph.eachVertex(function(v) {
delete v.index;
delete v.lowlink;
delete v.onStack;
});
// Start the depth first search
digraph.eachVertex(function(v) {
if(v.index === undef) {
findStronglyConnected(digraph, v)
}
});
// Tarjan algorithm for a single node
function findStronglyConnected(dg, v) {
var vertices, vertex;
v.index = v.lowlink = index;
index += 1;
pushStack(stack, v);
dg.eachEdgeFrom(v.name, function(v, w) {
if(w.index === undef) {
// Continue depth first search
findStronglyConnected(dg, w);
v.lowlink = Math.min(v.lowlink, w.lowlink);
} else if(w.onStack) {
v.lowlink = Math.min(v.lowlink, w.index);
}
});
if(v.lowlink === v.index) {
vertices = [];
if(stack.length) {
do {
vertex = popStack(stack);
vertices.push(vertex);
} while(v !== vertex)
}
if(vertices.length) {
scc.push(vertices);
}
}
}
return scc;
};
/**
* Push a vertex on the supplied stack, but also tag the
* vertex as being on the stack so we don't have to scan the
* stack later in order to tell.
* @param {Array} stack
* @param {object} vertex
*/
function pushStack(stack, vertex) {
stack.push(vertex);
vertex.onStack = 1;
}
/**
* Pop an item off the supplied stack, being sure to un-tag it
* @param {Array} stack
* @return {object|undefined} vertex
*/
function popStack(stack) {
var v = stack.pop();
if(v) {
delete v.onStack;
}
return v;
}
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
/**
* trackInflightRefs
* @author: brian@hovercraftstudios.com
*/
(function(define) {
define(function(require) {
var timeout, findStronglyConnected, formatCycles, refCycleCheckTimeout;
timeout = require('when/timeout');
findStronglyConnected = require('./tarjan');
formatCycles = require('./formatCycles');
refCycleCheckTimeout = 5000;
/**
* Advice to track inflight refs using a directed graph
* @param {DirectedGraph} graph
* @param {Resolver} resolver
* @param {number} cycleTimeout how long to wait for any one reference to resolve
* before performing cycle detection. This basically debounces cycle detection
*/
return function trackInflightRefs(graph, resolver, cycleTimeout) {
var create = resolver.create;
if(typeof cycleTimeout != 'number') {
cycleTimeout = refCycleCheckTimeout;
}
resolver.create = function() {
var ref, resolve;
ref = create.apply(resolver, arguments);
resolve = ref.resolve;
ref.resolve = function() {
var inflight = resolve.apply(ref, arguments);
return trackInflightRef(graph, cycleTimeout, inflight, ref.name, arguments[1]);
};
return ref;
};
return resolver;
};
/**
* Add this reference to the reference graph, and setup a timeout that will fire if the refPromise
* has not resolved in a reasonable amount. If the timeout fires, check the current graph for cycles
* and fail wiring if we find any.
* @param {DirectedGraph} refGraph graph to use to track cycles
* @param {number} cycleTimeout how long to wait for any one reference to resolve
* before performing cycle detection. This basically debounces cycle detection
* @param {object} refPromise promise for reference resolution
* @param {string} refName reference being resolved
* @param {string} onBehalfOf some indication of another component on whose behalf this
* reference is being resolved. Used to build a reference graph and detect cycles
* @return {object} promise equivalent to refPromise but that may be rejected if cycles are detected
*/
function trackInflightRef(refGraph, cycleTimeout, refPromise, refName, onBehalfOf) {
onBehalfOf = onBehalfOf||'?';
refGraph.addEdge(onBehalfOf, refName);
return timeout(refPromise, cycleTimeout).then(
function(resolved) {
refGraph.removeEdge(onBehalfOf, refName);
return resolved;
},
function() {
var stronglyConnected, cycles;
stronglyConnected = findStronglyConnected(refGraph);
cycles = stronglyConnected.filter(function(node) {
return node.length > 1;
});
if(cycles.length) {
// Cycles detected
throw new Error('Possible circular refs:\n'
+ formatCycles(cycles));
}
return refPromise;
}
);
}
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));
/** @license MIT License (c) copyright B Cavalier & J Hann */ /** @license MIT License (c) copyright original author or authors */
/** /**
* Licensed under the MIT License at: * Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
*/ */
(function(define){ (function(define){ 'use strict';
define(function() { define(function() {
"use strict";
var undef; var undef;
...@@ -22,7 +21,7 @@ define(function() { ...@@ -22,7 +21,7 @@ define(function() {
* @return The result of invoking ctor with args, with or without new, depending on * @return The result of invoking ctor with args, with or without new, depending on
* the strategy selected. * the strategy selected.
*/ */
return function createComponent(ctor, args, forceConstructor) { return function instantiate(ctor, args, forceConstructor) {
var begotten, ctorResult; var begotten, ctorResult;
......
(function(define) { (function(define) {
define(function(require) { define(function() {
return function(methodName, args) { return function(methodName, args) {
return function(target) { return function(target) {
...@@ -8,4 +8,4 @@ define(function(require) { ...@@ -8,4 +8,4 @@ define(function(require) {
}; };
}); });
})(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }); })(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); });
\ No newline at end of file \ No newline at end of file
...@@ -5,91 +5,98 @@ ...@@ -5,91 +5,98 @@
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
*/ */
(function(define){ (function(define){ 'use strict';
define(['when'], function(when) { define(function(require) {
"use strict"; var when, safeNonFacetNames;
var lifecyclePhases, phase; when = require('when');
safeNonFacetNames = {
lifecyclePhases = { id: { value: 1 }
init: generateSteps(['create', 'configure', 'initialize']),
startup: generateSteps(['connect', 'ready']),
shutdown: generateSteps(['destroy'])
}; };
function Lifecycle(config) { function Lifecycle(plugins, pluginApi) {
this._config = config; this._plugins = plugins;
this._pluginApi = pluginApi;
} }
// Generate prototype from lifecyclePhases Lifecycle.prototype = {
for(phase in lifecyclePhases) { init: createLifecyclePhase(['create', 'configure', 'initialize']),
Lifecycle.prototype[phase] = createLifecyclePhase(phase); startup: createLifecyclePhase(['connect', 'ready']),
} shutdown: createLifecyclePhase(['destroy'])
};
return Lifecycle; return Lifecycle;
/** /**
* Generate a method to process all steps in a lifecycle phase * Generate a method to process all steps in a lifecycle phase
* @param phase
* @return {Function} * @return {Function}
*/ */
function createLifecyclePhase(phase) { function createLifecyclePhase(steps) {
var steps = lifecyclePhases[phase]; steps = generateSteps(steps);
return function(proxy) { return function(proxy) {
var self = this; var plugins, pluginApi;
plugins = this._plugins;
pluginApi = this._pluginApi.contextualize(proxy.id);
return when.reduce(steps, function (unused, step) { return when.reduce(steps, function (unused, step) {
return processFacets(step, proxy, self._config); return processFacets(step, proxy, pluginApi, plugins);
}, proxy); }, proxy);
}; };
} }
function processFacets(step, proxy, config) { function processFacets(step, proxy, api, plugins) {
var promises, options, name, spec, facets; var promises, metadata, options, name, spec, facets, safeNames, unprocessed;
promises = [];
spec = proxy.spec;
facets = config.facets;
for (name in facets) { promises = [];
options = spec[name]; metadata = proxy.metadata;
if (options) { spec = metadata.spec;
processStep(promises, facets[name], step, proxy, options, config.pluginApi); facets = plugins.facets;
safeNames = Object.create(plugins.factories, safeNonFacetNames);
unprocessed = [];
for(name in spec) {
if(name in facets) {
options = spec[name];
if (options) {
processStep(promises, facets[name], step, proxy, options, api);
}
} else if (!(name in safeNames)) {
unprocessed.push(name);
} }
} }
return when.all(promises).then(function () { if(unprocessed.length) {
return processListeners(step, proxy, config); return when.reject(unrecognizedFacets(proxy, unprocessed, spec));
}).then(function() { } else {
return proxy; return when.all(promises).then(function () {
}); return processListeners(step, proxy, api, plugins.listeners);
}).yield(proxy);
}
} }
function processListeners(step, proxy, config) { function processListeners(step, proxy, api, listeners) {
var listeners, listenerPromises; var listenerPromises = [];
listeners = config.listeners;
listenerPromises = [];
for (var i = 0; i < listeners.length; i++) { for (var i = 0; i < listeners.length; i++) {
processStep(listenerPromises, listeners[i], step, proxy, {}, config.pluginApi); processStep(listenerPromises, listeners[i], step, proxy, {}, api);
} }
return when.all(listenerPromises); return when.all(listenerPromises);
} }
function processStep(promises, processor, step, proxy, options, pluginApi) { function processStep(promises, processor, step, proxy, options, api) {
var facet, facetPromise; var facet, pendingFacet;
if (processor && processor[step]) { if (processor && processor[step]) {
facetPromise = when.defer(); pendingFacet = when.defer();
promises.push(facetPromise.promise); promises.push(pendingFacet.promise);
facet = Object.create(proxy); facet = Object.create(proxy);
facet.options = options; facet.options = options;
processor[step](facetPromise.resolver, facet, pluginApi); processor[step](pendingFacet.resolver, facet, api);
} }
} }
...@@ -103,14 +110,15 @@ define(['when'], function(when) { ...@@ -103,14 +110,15 @@ define(['when'], function(when) {
lifecycle.push(step + ':after'); lifecycle.push(step + ':after');
return lifecycle; return lifecycle;
} }
function unrecognizedFacets(proxy, unprocessed, spec) {
return new Error('unrecognized facets in ' + proxy.id + ', maybe you forgot a plugin? ' + unprocessed.join(', ') + '\n' + JSON.stringify(spec));
}
}); });
})(typeof define == 'function' })(typeof define == 'function'
// AMD // AMD
? define ? define
// CommonJS // CommonJS
: function(deps, factory) { : function(factory) { module.exports = factory(require); }
module.exports = factory.apply(this, deps.map(function(x) {
return require(x);
}));
}
); );
\ No newline at end of file
/** @license MIT License (c) copyright B Cavalier & J Hann */
/**
* Loading and merging modules
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*
* @author: brian@hovercraftstudios.com
*/
(function(define) { 'use strict';
define(function(require) {
var when, mixin, wrapPlatformLoader;
when = require('when');
mixin = require('./object').mixin;
// Get the platform's loader
wrapPlatformLoader = typeof exports == 'object'
? function(require) {
return function(moduleId) {
try {
return when.resolve(require(moduleId));
} catch(e) {
return when.reject(e);
}
};
}
: function (require) {
return function(moduleId) {
var deferred = when.defer();
require([moduleId], deferred.resolve, deferred.reject);
return deferred.promise;
};
};
return getModuleLoader;
/**
* Create a module loader
* @param {function} [platformLoader] platform require function with which
* to configure the module loader
* @param {function} [parentLoader] existing module loader from which
* the new module loader will inherit, if provided.
* @return {Object} module loader with load() and merge() methods
*/
function getModuleLoader(platformLoader, parentLoader) {
var loadModule = typeof platformLoader == 'function'
? wrapPlatformLoader(platformLoader)
: parentLoader || wrapPlatformLoader(require);
return {
load: loadModule,
merge: function(specs) {
return when(specs, function(specs) {
return when.resolve(Array.isArray(specs)
? mergeAll(specs, loadModule)
: (typeof specs === 'string' ? loadModule(specs) : specs));
});
}
};
}
function mergeAll(specs, loadModule) {
return when.reduce(specs, function(merged, module) {
return typeof module == 'string'
? when(loadModule(module), function(spec) { return mixin(merged, spec); })
: mixin(merged, module);
}, {});
}
});
}(typeof define === 'function' ? define : function(factory) { module.exports = factory(require); }));
/** @license MIT License (c) copyright B Cavalier & J Hann */
/**
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*/
/**
* Abstract the platform's loader
* @type {Function}
* @param require {Function} platform-specific require
* @return {Function}
*/
if(typeof define == 'function' && define.amd) {
// AMD
define(['when'], function(when) {
return function createModuleLoader(require) {
return function(moduleId) {
var deferred = when.defer();
require([moduleId], deferred.resolve, deferred.reject);
return deferred.promise;
};
};
});
} else {
// CommonJS
module.exports = function createModuleLoader(require) {
return require;
};
}
...@@ -5,13 +5,18 @@ ...@@ -5,13 +5,18 @@
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
*/ */
(function(define){ (function(define){ 'use strict';
define(function() { define(function() {
"use strict";
var hasOwn;
hasOwn = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);
return { return {
hasOwn: hasOwn,
isObject: isObject, isObject: isObject,
inherit: inherit inherit: inherit,
mixin: mixin
}; };
function isObject(it) { function isObject(it) {
...@@ -25,6 +30,24 @@ define(function() { ...@@ -25,6 +30,24 @@ define(function() {
return parent ? Object.create(parent) : {}; return parent ? Object.create(parent) : {};
} }
/**
* Brute force copy own properties from -> to. Effectively an
* ES6 Object.assign polyfill, usable with Array.prototype.reduce.
* @param {object} to
* @param {object} from
* @returns {object} to
*/
function mixin(to, from) {
if(!from) {
return to;
}
return Object.keys(from).reduce(function(to, key) {
to[key] = from[key];
return to;
}, to);
}
}); });
})(typeof define == 'function' })(typeof define == 'function'
// AMD // AMD
......
...@@ -8,10 +8,10 @@ ...@@ -8,10 +8,10 @@
* Licensed under the MIT License at: * Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
*/ */
define(['wire/domReady', 'when', '../dom/base'], function(domReady, when, base) { define(['wire/domReady', 'when', '../dom/base', '../object'], function(domReady, when, base, object) {
function getElementFactory (resolver, spec, wire) { function getElementFactory (resolver, componentDef, wire) {
when(wire(spec.element), function (element) { when(wire(componentDef.options), function (element) {
if (!element || !element.nodeType || !element.tagName) { if (!element || !element.nodeType || !element.tagName) {
throw new Error('dom: non-element reference provided to element factory'); throw new Error('dom: non-element reference provided to element factory');
...@@ -23,13 +23,12 @@ define(['wire/domReady', 'when', '../dom/base'], function(domReady, when, base) ...@@ -23,13 +23,12 @@ define(['wire/domReady', 'when', '../dom/base'], function(domReady, when, base)
return function createDomPlugin(options) { return function createDomPlugin(options) {
var getById, query, first, init, addClass, removeClass, placeAt, var getById, query, first, addClass, removeClass, placeAt,
doById, doPlaceAt, resolveQuery; doById, doPlaceAt, resolveQuery;
getById = options.byId || base.byId; getById = options.byId || base.byId;
query = options.query || base.querySelectorAll; query = options.query || base.querySelectorAll;
first = options.first || base.querySelector; first = options.first || base.querySelector;
init = options.init;
addClass = options.addClass; addClass = options.addClass;
placeAt = options.placeAt || base.placeAt; placeAt = options.placeAt || base.placeAt;
removeClass = options.removeClass; removeClass = options.removeClass;
...@@ -85,7 +84,7 @@ define(['wire/domReady', 'when', '../dom/base'], function(domReady, when, base) ...@@ -85,7 +84,7 @@ define(['wire/domReady', 'when', '../dom/base'], function(domReady, when, base)
// get first property and use it as the operation // get first property and use it as the operation
for (var p in options) { for (var p in options) {
if (options.hasOwnProperty(p)) { if (object.hasOwn(options, p)) {
operation = p; operation = p;
break; break;
} }
...@@ -194,73 +193,84 @@ define(['wire/domReady', 'when', '../dom/base'], function(domReady, when, base) ...@@ -194,73 +193,84 @@ define(['wire/domReady', 'when', '../dom/base'], function(domReady, when, base)
} }
} }
return { /**
wire$plugin: function(ready, destroyed, options) { * DOM plugin factory
var classes, resolvers, facets, factories; */
return function(options) {
options.at = makeQueryRoot(options.at); var classes, resolvers, facets, factories, context, htmlElement;
if (init) { options.at = makeQueryRoot(options.at);
init(ready, destroyed, options); classes = options.classes;
} context = {};
classes = options.classes; if(classes) {
domReady(function() {
htmlElement = document.getElementsByTagName('html')[0];
});
// Add/remove lifecycle classes if specified context.initialize = function (resolver) {
if (classes) {
domReady(function () { domReady(function () {
var node = document.getElementsByTagName('html')[0]; handleClasses(htmlElement, classes.init);
resolver.resolve();
// Add classes for wiring start
handleClasses(node, classes.init);
// Add/remove classes for context ready
ready.then(function () {
handleClasses(node, classes.ready, classes.init);
});
if (classes.ready) {
// Remove classes for context destroyed
destroyed.then(function () {
handleClasses(node, null, classes.ready);
});
}
}); });
}
resolvers = {
'dom': doById
};
facets = {
'insert': {
initialize: doPlaceAt
}
}; };
context.ready = function (resolver) {
factories = { domReady(function () {
'element': getElementFactory handleClasses(htmlElement, classes.ready, classes.init);
resolver.resolve();
});
}; };
if(classes.ready) {
context.destroy = function (resolver) {
domReady(function () {
handleClasses(htmlElement, null, classes.ready);
resolver.resolve();
});
};
}
}
if (query) { factories = {
resolvers['dom.first'] = createResolver(resolveFirst, options); element: getElementFactory
};
// dom.all and dom.query are synonyms facets = {
resolvers['dom.all'] insert: {
= resolvers['dom.query'] = createResolver(resolveQuery, options); initialize: doPlaceAt
} }
};
return { resolvers = {};
resolvers: resolvers, // id and dom are synonyms
facets: facets, // dom is deprecated and for backward compat only
factories: factories, resolvers.id = resolvers.dom = doById;
proxies: [
// this allows base.nodeProxy to be overridden if (query) {
function (node) { return base.nodeProxy(node); } // dom.first is deprecated
] resolvers.first = createResolver(resolveFirst, options);
resolvers['dom.first'] = function() {
// TODO: Deprecation warning
resolvers.first.apply(resolvers, arguments);
}; };
// all and query are synonyms
resolvers.all = resolvers.query = createResolver(resolveQuery, options);
resolvers['dom.all'] = resolvers['dom.query'] = function() {
// TODO: Deprecation warning
resolvers.query.apply(resolvers, arguments);
};
} }
return {
context: context,
resolvers: resolvers,
facets: facets,
factories: factories,
proxies: [
base.proxyNode
]
};
}; };
}; };
}); });
...@@ -30,141 +30,141 @@ function (when, apply, functional, connection) { ...@@ -30,141 +30,141 @@ function (when, apply, functional, connection) {
on = options.on; on = options.on;
return { return function eventsPlugin (options) {
wire$plugin: function eventsPlugin (ready, destroyed, options) {
var removers = []; var removers = [];
if (!options) { if (!options) {
options = {}; options = {};
} }
function createConnection(source, eventsString, handler) { function createConnection(source, eventsString, handler) {
var events, prevent, stop; var events, prevent, stop;
events = splitEventSelectorString(eventsString); events = splitEventSelectorString(eventsString);
prevent = options.preventDefault; prevent = options.preventDefault;
stop = options.stopPropagation; stop = options.stopPropagation;
removers = removers.concat( removers = removers.concat(
registerHandlers(events, source, handler, prevent, stop) registerHandlers(events, source, handler, prevent, stop)
); );
} }
function parseIncomingOn(source, targetProxy, connections, wire) { function parseIncomingOn(source, targetProxy, connections, wire) {
// NOTE: Custom parsing for incoming connections // NOTE: Custom parsing for incoming connections
// target is the node to which to connect, and // target is the node to which to connect, and
// right hand side is a specification of an event // right hand side is a specification of an event
// and a handler method on the current component // and a handler method on the current component
// //
// component: { // component: {
// on: { // on: {
// otherComponent: { // otherComponent: {
// selector: 'a.nav', // selector: 'a.nav',
// transform: { $ref: 'myTransformFunc' }, // optional // transform: { $ref: 'myTransformFunc' }, // optional
// click: 'handlerMethodOnComponent', // click: 'handlerMethodOnComponent',
// keypress: 'anotherHandlerOnComponent' // keypress: 'anotherHandlerOnComponent'
// } // }
// } // }
// } // }
var target, event, events, selector, prevent, stop, method, transform, promises; var target, event, events, selector, prevent, stop, method, transform, promises;
target = targetProxy.target; target = targetProxy.target;
promises = []; promises = [];
// Extract options // Extract options
selector = connections.selector; selector = connections.selector;
transform = connections.transform; transform = connections.transform;
prevent = connections.preventDefault || options.preventDefault; prevent = connections.preventDefault || options.preventDefault;
stop = connections.stopPropagation || options.stopPropagation; stop = connections.stopPropagation || options.stopPropagation;
/** /**
* Compose a transform pipeline and then pass it to addConnection * Compose a transform pipeline and then pass it to addConnection
*/ */
function createTransformedConnection(events, targetMethod, transformPromise) { function createTransformedConnection(events, targetMethod, transformPromise) {
return when(transformPromise, function(transform) { return when(transformPromise, function(transform) {
var composed = functional.compose([transform, targetMethod]).bind(targetProxy.target); var composed = functional.compose([transform, targetMethod]).bind(targetProxy.target);
removers = removers.concat( removers = removers.concat(
registerHandlers(events, source, function() { registerHandlers(events, source, function() {
return targetProxy.invoke(composed, arguments); return targetProxy.invoke(composed, arguments);
}, prevent, stop) }, prevent, stop)
); );
}); });
} }
for (event in connections) { for (event in connections) {
// Skip reserved names, such as 'selector' // Skip reserved names, such as 'selector'
if (!(event in theseAreNotEvents)) { if (!(event in theseAreNotEvents)) {
// If there's an explicit transform, compose a transform pipeline manually, // If there's an explicit transform, compose a transform pipeline manually,
// Otherwise, let the connection lib do it's thing // Otherwise, let the connection lib do it's thing
if(transform) { if(transform) {
// TODO: Remove this long form? It'd simplify the code a lot // TODO: Remove this long form? It'd simplify the code a lot
events = splitEventSelectorString(event, selector); events = splitEventSelectorString(event, selector);
method = connections[event]; method = connections[event];
promises.push(createTransformedConnection(events, target[method], wire(transform))); promises.push(createTransformedConnection(events, target[method], wire(transform)));
} else { } else {
promises.push(connection.parseIncoming(source, event, targetProxy, options, connections[event], wire, createConnection)); promises.push(connection.parseIncoming(source, event, targetProxy, options, connections[event], wire, createConnection));
}
} }
} }
return when.all(promises);
} }
function parseOn (proxy, refName, connections, wire) { return when.all(promises);
// First, figure out if the left-hand-side is a ref to }
// another component, or an event/delegation string
return when(wire.resolveRef(refName),
function (source) {
// It's an incoming connection, parse it as such
return parseIncomingOn(source, proxy, connections, wire);
},
function () {
// Failed to resolve refName as a reference, assume it
// is an outgoing event with the current component (which
// must be a Node) as the source
return connection.parseOutgoing(proxy, refName, connections, wire, createConnection);
}
);
} function parseOn (proxy, refName, connections, wire) {
// First, figure out if the left-hand-side is a ref to
// another component, or an event/delegation string
return when(wire.resolveRef(refName),
function (source) {
// It's an incoming connection, parse it as such
return parseIncomingOn(source, proxy, connections, wire);
},
function () {
// Failed to resolve refName as a reference, assume it
// is an outgoing event with the current component (which
// must be a Node) as the source
return connection.parseOutgoing(proxy, refName, connections, wire, createConnection);
}
);
function onFacet (wire, facet) { }
var promises, connections;
connections = facet.options; function onFacet (wire, facet) {
promises = []; var promises, connections;
for (var ref in connections) { connections = facet.options;
promises.push(parseOn(facet, ref, connections[ref], wire)); promises = [];
}
return when.all(promises); for (var ref in connections) {
promises.push(parseOn(facet, ref, connections[ref], wire));
} }
destroyed.then(function onContextDestroy () { return when.all(promises);
for (var i = removers.length - 1; i >= 0; i--) { }
removers[i]();
}
});
return { return {
facets: { context: {
on: { destroy: function(resolver) {
connect: function (resolver, facet, wire) { removers.forEach(function(remover) {
resolver.resolve(onFacet(wire, facet)); remover();
} });
} resolver.resolve();
}, }
resolvers: { },
on: function(resolver, name /*, refObj, wire*/) { facets: {
resolver.resolve(name ? createOnResolver(name) : on); on: {
connect: function (resolver, facet, wire) {
resolver.resolve(onFacet(wire, facet));
} }
} }
}; },
} resolvers: {
on: function(resolver, name /*, refObj, wire*/) {
resolver.resolve(name ? createOnResolver(name) : on);
}
}
};
}; };
function registerHandlers (events, node, callback, prevent, stop) { function registerHandlers (events, node, callback, prevent, stop) {
...@@ -256,7 +256,7 @@ function (when, apply, functional, connection) { ...@@ -256,7 +256,7 @@ function (when, apply, functional, connection) {
return function (e) { return function (e) {
preventer(e); preventer(e);
stopper(e); stopper(e);
return handler(e); return handler.apply(this, arguments);
}; };
} }
......
/** @license MIT License (c) copyright B Cavalier & J Hann */ /** @license MIT License (c) copyright B Cavalier & J Hann */
/** /**
* wire/base plugin
* Base wire plugin that provides properties, init, and destroy facets, and * Base wire plugin that provides properties, init, and destroy facets, and
* a proxy for plain JS objects. * a proxy for plain JS objects.
* *
...@@ -11,12 +10,20 @@ ...@@ -11,12 +10,20 @@
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
*/ */
(function(define) { (function(define) { 'use strict';
define(['when', './lib/object', './lib/functional', './lib/component', './lib/invoker'], function(when, object, functional, createComponent, createInvoker) { define(function(require) {
var whenAll, obj, undef; var when, object, functional, instantiate, createInvoker,
whenAll, obj, pluginInstance, undef;
when = require('when');
object = require('../object');
functional = require('../functional');
instantiate = require('../instantiate');
createInvoker = require('../invoker');
whenAll = when.all; whenAll = when.all;
obj = {}; obj = {};
function asArray(it) { function asArray(it) {
...@@ -75,7 +82,7 @@ define(['when', './lib/object', './lib/functional', './lib/component', './lib/in ...@@ -75,7 +82,7 @@ define(['when', './lib/object', './lib/functional', './lib/component', './lib/in
} }
function mixinFacet(resolver, facet, wire) { function mixinFacet(resolver, facet, wire) {
var target, intros, promise; var target, intros;
target = facet.target; target = facet.target;
intros = facet.options; intros = facet.options;
...@@ -84,11 +91,9 @@ define(['when', './lib/object', './lib/functional', './lib/component', './lib/in ...@@ -84,11 +91,9 @@ define(['when', './lib/object', './lib/functional', './lib/component', './lib/in
intros = [intros]; intros = [intros];
} }
promise = when.reduce(intros, function(target, intro) { resolver.resolve(when.reduce(intros, function(target, intro) {
return doMixin(target, intro, wire); return doMixin(target, intro, wire);
}, target); }, target));
resolver.resolve(promise);
} }
/** /**
...@@ -114,131 +119,63 @@ define(['when', './lib/object', './lib/functional', './lib/component', './lib/in ...@@ -114,131 +119,63 @@ define(['when', './lib/object', './lib/functional', './lib/component', './lib/in
* to create an instance of an AMD module whose id is "foo". * to create an instance of an AMD module whose id is "foo".
*/ */
function literalFactory(resolver, spec /*, wire */) { function literalFactory(resolver, spec /*, wire */) {
resolver.resolve(spec.literal); resolver.resolve(spec.options);
} }
/** /**
* @deprecated Use create (instanceFactory) instead * @deprecated Use create (instanceFactory) instead
* @param resolver * @param resolver
* @param spec * @param componentDef
* @param wire * @param wire
*/ */
function protoFactory(resolver, spec, wire) { function protoFactory(resolver, componentDef, wire) {
var parentRef, promise; var parentRef, promise;
parentRef = spec.prototype; parentRef = componentDef.options;
promise = typeof parentRef === 'string' promise = typeof parentRef === 'string'
? wire.resolveRef(parentRef) ? wire.resolveRef(parentRef)
: wire(parentRef); : wire(parentRef);
when(promise, Object.create) resolver.resolve(promise.then(Object.create));
.then(resolver.resolve, resolver.reject);
} }
function propertiesFacet(resolver, facet, wire) { function propertiesFacet(resolver, facet, wire) {
var properties, path, setProperty; var properties, path, setProperty, propertiesSet;
properties = facet.options; properties = facet.options;
path = facet.path; path = facet.path;
setProperty = facet.set.bind(facet); setProperty = facet.set.bind(facet);
when.map(Object.keys(facet.options), function(key) { propertiesSet = when.map(Object.keys(facet.options), function(key) {
return wire(properties[key], key, facet.path) return wire(properties[key], facet.path)
.then(function(wiredProperty) { .then(function(wiredProperty) {
setProperty(key, wiredProperty); setProperty(key, wiredProperty);
} }
); );
}).then(resolver.resolve, resolver.reject); });
resolver.resolve(propertiesSet);
} }
function invokerFactory(resolver, componentDef, wire) { function invokerFactory(resolver, componentDef, wire) {
wire(componentDef.invoker).then(function(invokerContext) { var invoker = wire(componentDef.options).then(function (invokerContext) {
// It'd be nice to use wire.getProxy() then proxy.invoke() // It'd be nice to use wire.getProxy() then proxy.invoke()
// here, but that means the invoker must always return // here, but that means the invoker must always return
// a promise. Not sure that's best, so for now, just // a promise. Not sure that's best, so for now, just
// call the method directly // call the method directly
return createInvoker(invokerContext.method, invokerContext.args); return createInvoker(invokerContext.method, invokerContext.args);
}).then(resolver.resolve, resolver.reject); });
resolver.resolve(invoker);
} }
function invokerFacet(resolver, facet, wire) { function invokerFacet(resolver, facet, wire) {
resolver.resolve(invokeAll(facet, wire)); resolver.resolve(invokeAll(facet, wire));
} }
function pojoProxy(object /*, spec */) {
return {
get: function(property) {
return object[property];
},
set: function(property, value) {
object[property] = value;
return value;
},
invoke: function(method, args) {
if(typeof method === 'string') {
method = object[method];
}
return method.apply(object, args);
},
destroy: function() {},
clone: function(options) {
// don't try to clone a primitive
if (typeof object != 'object') {
return object;
}
// cloneThing doesn't clone functions (methods), so clone here:
else if (typeof object == 'function') {
return object.bind();
}
if (!options) {
options = {};
}
return cloneThing(object, options);
}
};
}
function cloneThing (thing, options) {
var deep, inherited, clone, prop;
deep = options.deep;
inherited = options.inherited;
// Note: this filters out primitive properties and methods
if (typeof thing != 'object') {
return thing;
}
else if (thing instanceof Date) {
return new Date(thing.getTime());
}
else if (thing instanceof RegExp) {
return new RegExp(thing);
}
else if (Array.isArray(thing)) {
return deep
? thing.map(function (i) { return cloneThing(i, options); })
: thing.slice();
}
else {
clone = thing.constructor ? new thing.constructor() : {};
for (prop in thing) {
if (inherited || thing.hasOwnProperty(prop)) {
clone[prop] = deep
? cloneThing(thing[prop], options)
: thing[prop];
}
}
return clone;
}
}
//noinspection JSUnusedLocalSymbols //noinspection JSUnusedLocalSymbols
/** /**
* Wrapper for use with when.reduce that calls the supplied destroyFunc * Wrapper for use with when.reduce that calls the supplied destroyFunc
...@@ -249,89 +186,84 @@ define(['when', './lib/object', './lib/functional', './lib/component', './lib/in ...@@ -249,89 +186,84 @@ define(['when', './lib/object', './lib/functional', './lib/component', './lib/in
return destroyFunc(); return destroyFunc();
} }
function moduleFactory(resolver, spec, wire) { function cloneFactory(resolver, componentDef, wire) {
resolver.resolve(wire.loadModule(spec.module, spec)); var sourceRef, options, cloned;
}
function cloneFactory(resolver, spec, wire) {
var sourceRef, options;
if (wire.resolver.isRef(spec.clone.source)) { if (wire.resolver.isRef(componentDef.options.source)) {
sourceRef = spec.clone.source; sourceRef = componentDef.options.source;
options = spec.clone; options = componentDef.options;
} }
else { else {
sourceRef = spec.clone; sourceRef = componentDef.options;
options = {}; options = {};
} }
when(wire(sourceRef), function (ref) { cloned = wire(sourceRef).then(function (ref) {
return when(wire.getProxy(ref), function (proxy) { return when(wire.getProxy(ref), function (proxy) {
if (!proxy.clone) { if (!proxy.clone) {
throw new Error('No clone function found for ' + spec.id); throw new Error('No clone function found for ' + componentDef.id);
} }
return proxy.clone(options); return proxy.clone(options);
}); });
}).then(resolver.resolve, resolver.reject); });
resolver.resolve(cloned);
}
function moduleFactory(resolver, componentDef, wire) {
resolver.resolve(wire.loadModule(componentDef.options));
} }
/** /**
* Factory that uses an AMD module either directly, or as a * Factory that uses an AMD module either directly, or as a
* constructor or plain function to create the resulting item. * constructor or plain function to create the resulting item.
* *
* @param resolver {Resolver} resolver to resolve with the created component * @param {Object} resolver resolver to resolve with the created component
* @param spec {Object} portion of the spec for the component to be created * @param {Object} componentDef portion of the spec for the component to be created
* @param {function} wire
*/ */
function instanceFactory(resolver, spec, wire) { function instanceFactory(resolver, componentDef, wire) {
var create, args, isConstructor, name, promise; var create, args, isConstructor, module, instance;
name = spec.id; create = componentDef.options;
create = spec.create;
if (typeof create == 'string') { if (typeof create == 'string') {
promise = wire.loadModule(create, spec); module = wire.loadModule(create);
} else if(wire.resolver.isRef(create)) { } else if(wire.resolver.isRef(create)) {
promise = wire(create); module = wire(create);
} else { } else if(object.isObject(create) && create.module) {
promise = wire(create); module = wire.loadModule(create.module);
args = create.args; args = create.args ? wire(asArray(create.args)) : [];
isConstructor = create.isConstructor; isConstructor = create.isConstructor;
} else {
module = create;
} }
resolver.resolve(when(promise, handleModule)); instance = when.join(module, args).spread(createInstance);
// Load the module, and use it to create the object resolver.resolve(instance);
function handleModule(module) {
function resolve(resolvedArgs) {
return createComponent(module, resolvedArgs, isConstructor);
}
// Load the module, and use it to create the object
function createInstance(module, args) {
// We'll either use the module directly, or we need // We'll either use the module directly, or we need
// to instantiate/invoke it. // to instantiate/invoke it.
if (typeof module == 'function') { return typeof module == 'function'
// Instantiate or invoke it and use the result ? instantiate(module, args, isConstructor)
return args : Object.create(module);
? when(wire(asArray(args)), resolve)
: resolve([]);
} else {
// Simply use the module as is
return Object.create(module);
}
} }
} }
function composeFactory(resolver, spec, wire) { function composeFactory(resolver, componentDef, wire) {
var promise; var options, promise;
spec = spec.compose; options = componentDef.options;
if(typeof spec == 'string') { if(typeof options == 'string') {
promise = functional.compose.parse(undef, spec, wire); promise = functional.compose.parse(undef, options, wire);
} else { } else {
// Assume it's an array of things that will wire to functions // Assume it's an array of things that will wire to functions
promise = when(wire(spec), function(funcArray) { promise = when(wire(options), function(funcArray) {
return functional.compose(funcArray); return functional.compose(funcArray);
}); });
} }
...@@ -339,80 +271,51 @@ define(['when', './lib/object', './lib/functional', './lib/component', './lib/in ...@@ -339,80 +271,51 @@ define(['when', './lib/object', './lib/functional', './lib/component', './lib/in
resolver.resolve(promise); resolver.resolve(promise);
} }
return { pluginInstance = {
wire$plugin: function(ready, destroyed /*, options */) { factories: {
// Components in the current context that will be destroyed module: moduleFactory,
// when this context is destroyed create: instanceFactory,
var destroyFuncs, plugin; literal: literalFactory,
prototype: protoFactory,
destroyFuncs = []; clone: cloneFactory,
compose: composeFactory,
when(destroyed, function() { invoker: invokerFactory
return when.reduce(destroyFuncs, destroyReducer, 0); },
}); facets: {
// properties facet. Sets properties on components
function destroyFacet(resolver, facet, wire) { // after creation.
destroyFuncs.push(function destroyObject() { properties: {
return invokeAll(facet, wire); configure: propertiesFacet
}); },
mixin: {
// This resolver is just related to *collecting* the functions to configure: mixinFacet
// invoke when the component is destroyed. },
resolver.resolve(); // init facet. Invokes methods on components during
// the "init" stage.
init: {
initialize: invokerFacet
},
// ready facet. Invokes methods on components during
// the "ready" stage.
ready: {
ready: invokerFacet
},
// destroy facet. Registers methods to be invoked
// on components when the enclosing context is destroyed
destroy: {
destroy: invokerFacet
} }
plugin = {
factories: {
module: moduleFactory,
create: instanceFactory,
literal: literalFactory,
prototype: protoFactory,
clone: cloneFactory,
compose: composeFactory,
invoker: invokerFactory
},
facets: {
// properties facet. Sets properties on components
// after creation.
properties: {
configure: propertiesFacet
},
mixin: {
configure: mixinFacet
},
// init facet. Invokes methods on components during
// the "init" stage.
init: {
initialize: invokerFacet
},
// ready facet. Invokes methods on components during
// the "ready" stage.
ready: {
ready: invokerFacet
},
// destroy facet. Registers methods to be invoked
// on components when the enclosing context is destroyed
destroy: {
ready: destroyFacet
}
},
proxies: [
pojoProxy
]
};
// "introduce" is deprecated, but preserved here for now.
plugin.facets.introduce = plugin.facets.mixin;
return plugin;
} }
}; };
// "introduce" is deprecated, but preserved here for now.
pluginInstance.facets.introduce = pluginInstance.facets.mixin;
return function(/* options */) {
return pluginInstance;
};
}); });
})(typeof define == 'function' })(typeof define == 'function'
? define ? define
: function(deps, factory) { : function(factory) { module.exports = factory(require); }
module.exports = factory.apply(this, deps.map(function(x) {
return require(x);
}));
}
); );
/**
* defaultPlugins
* @author: brian
*/
(function(define) {
define(function(require) {
return [
require('./wirePlugin'),
require('./basePlugin')
];
});
}(typeof define === 'function' ? define : function(factory) { module.exports = factory(require); }));
/** @license MIT License (c) copyright 2010-2013 original author or authors */
/**
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*
* @author: Brian Cavalier
* @author: John Hann
*/
(function(define) { 'use strict';
define(function() {
var basePriority, defaultPriority;
basePriority = -99;
defaultPriority = 0;
return {
basePriority: basePriority,
sortReverse: prioritizeReverse
};
function prioritizeReverse(list) {
return list.sort(byReversePriority);
}
function byReversePriority(a, b) {
var aPriority, bPriority;
aPriority = a.priority || defaultPriority;
bPriority = b.priority || defaultPriority;
return aPriority < bPriority ? -1
: aPriority > bPriority ? 1 : 0;
}
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
/** @license MIT License (c) copyright B Cavalier & J Hann */
/**
* plugins
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
* @author: brian@hovercraftstudios.com
*/
(function(define) {
define(function(require) {
var when, array, object, priority, instantiate, nsKey, nsSeparator;
when = require('when');
array = require('../array');
object = require('../object');
priority = require('./priority');
instantiate = require('../instantiate');
nsKey = '$ns';
nsSeparator = ':';
function PluginRegistry() {
this.plugins = [];
this._namespaces = {};
this.contextListeners = [];
this.listeners = [];
this.proxiers = [];
this.resolvers = {};
this.factories = {};
this.facets = {};
}
PluginRegistry.prototype = {
scanModule: function (module, spec, namespace) {
var self, pluginFactory;
pluginFactory = discoverPlugin(module);
if (!allowPlugin(pluginFactory, this.plugins)) {
return when.resolve();
}
// Add to singleton plugins list to only allow one instance
// of this plugin in the current context.
this.plugins.push(pluginFactory);
// Initialize the plugin for this context
self = this;
return when(instantiate(pluginFactory, [spec]),
function (plugin) {
plugin && self.registerPlugin(plugin, namespace || getNamespace(spec));
}
).yield();
},
registerPlugin: function (plugin, namespace) {
addNamespace(namespace, this._namespaces);
addPlugin(plugin.resolvers, this.resolvers, namespace);
addPlugin(plugin.factories, this.factories, namespace);
addPlugin(plugin.facets, this.facets, namespace);
this.listeners.push(plugin);
if(plugin.context) {
this.contextListeners.push(plugin.context);
}
this._registerProxies(plugin.proxies);
},
_registerProxies: function (proxiesToAdd) {
if (!proxiesToAdd) {
return;
}
this.proxiers = priority.sortReverse(array.union(this.proxiers, proxiesToAdd));
}
};
return PluginRegistry;
function discoverPlugin(module) {
var plugin;
// Prefer deprecated legacy wire$plugin format over newer
// plain function format.
// TODO: Remove support for wire$plugin
if(typeof module.wire$plugin === 'function') {
plugin = module.wire$plugin;
} else if(typeof module === 'function') {
plugin = module;
}
return plugin;
}
function getNamespace(spec) {
var namespace;
if(typeof spec === 'object' && nsKey in spec) {
// A namespace was provided
namespace = spec[nsKey];
}
return namespace;
}
function addNamespace(namespace, namespaces) {
if(namespace && namespace in namespaces) {
throw new Error('plugin namespace already in use: ' + namespace);
} else {
namespaces[namespace] = 1;
}
}
function allowPlugin(plugin, existing) {
return typeof plugin === 'function' && existing.indexOf(plugin) === -1;
}
function addPlugin(src, registry, namespace) {
var newPluginName, namespacedName;
for (newPluginName in src) {
namespacedName = makeNamespace(newPluginName, namespace);
if (object.hasOwn(registry, namespacedName)) {
throw new Error("Two plugins for same type in scope: " + namespacedName);
}
registry[namespacedName] = src[newPluginName];
}
}
function makeNamespace(pluginName, namespace) {
return namespace ? (namespace + nsSeparator + pluginName) : pluginName;
}
});
}(typeof define === 'function' ? define : function(factory) { module.exports = factory(require); }));
/** @license MIT License (c) copyright B Cavalier & J Hann */
/**
* Plugin that allows wire to be used as a plugin within a wire spec
*
* wire is part of the cujo.js family of libraries (http://cujojs.com/)
*
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*/
(function(define) {
define(function(require) {
var when, object;
when = require('when');
object = require('../object');
return function(/* options */) {
var ready = when.defer();
return {
context: {
ready: function(resolver) {
ready.resolve();
resolver.resolve();
}
},
resolvers: {
wire: wireResolver
},
factories: {
wire: wireFactory
}
};
/**
* Factory that creates either a child context, or a *function* that will create
* that child context. In the case that a child is created, this factory returns
* a promise that will resolve when the child has completed wiring.
*
* @param {Object} resolver used to resolve with the created component
* @param {Object} componentDef component spec for the component to be created
* @param {function} wire scoped wire function
*/
function wireFactory(resolver, componentDef, wire) {
var options, module, provide, defer, waitParent, result;
options = componentDef.options;
// Get child spec and options
if(object.isObject(options) && 'spec' in options) {
module = options.spec;
waitParent = options.waitParent;
defer = options.defer;
provide = options.provide;
} else {
module = options;
}
function init(context) {
var initialized;
if(provide) {
initialized = when(wire(provide), function(provides) {
object.mixin(context.instances, provides);
});
}
return initialized;
}
function createChild(/** {Object|String}? */ mixin) {
var spec, config;
spec = mixin ? [].concat(module, mixin) : module;
config = { initializers: [init] };
var child = wire.createChild(spec, config);
return defer ? child
: when(child, function(child) {
return object.hasOwn(child, '$exports') ? child.$exports : child;
});
}
if (defer) {
// Resolve with the createChild *function* itself
// which can be used later to wire the spec
result = createChild;
} else if(waitParent) {
var childPromise = when(ready.promise, function() {
// ensure nothing is passed to createChild here
return createChild();
});
result = wrapChild(childPromise);
} else {
result = createChild(componentDef.spec);
}
resolver.resolve(result);
}
};
function wrapChild(promise) {
return { promise: promise };
}
/**
* Builtin reference resolver that resolves to the context-specific
* wire function.
*/
function wireResolver(resolver, _, __, wire) {
resolver.resolve(wire.createChild);
}
});
}(typeof define === 'function' ? define : function(factory) { module.exports = factory(require); }));
...@@ -5,26 +5,57 @@ ...@@ -5,26 +5,57 @@
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
*/ */
(function(define){ (function(define){ 'use strict';
define(['when'], function(when) { define(function(require) {
'use strict'; var when, timeout, object;
function Resolver(config) { when = require('when');
this._resolvers = config.resolvers; timeout = require('when/timeout');
this._pluginApi = config.pluginApi; object = require('./object');
/**
* Create a reference resolve that uses the supplied plugins and pluginApi
* @param {object} config
* @param {object} config.plugins plugin registry
* @param {object} config.pluginApi plugin Api to provide to resolver plugins
* when resolving references
* @constructor
*/
function Resolver(resolvers, pluginApi) {
this._resolvers = resolvers;
this._pluginApi = pluginApi;
} }
Resolver.prototype = { Resolver.prototype = {
/**
* Determine if it is a reference spec that can be resolved by this resolver
* @param {*} it
* @return {boolean} true iff it is a reference
*/
isRef: function(it) { isRef: function(it) {
return it && it.hasOwnProperty('$ref'); return it && object.hasOwn(it, '$ref');
}, },
/**
* Parse it, which must be a reference spec, into a reference object
* @param {object|string} it
* @param {string?} it.$ref
* @return {object} reference object
*/
parse: function(it) { parse: function(it) {
return this.create(it.$ref, it); return this.isRef(it)
? this.create(it.$ref, it)
: this.create(it, {});
}, },
/**
* Creates a reference object
* @param {string} name reference name
* @param {object} options
* @return {{resolver: String, name: String, options: object, resolve: Function}}
*/
create: function(name, options) { create: function(name, options) {
var self, split, resolver; var self, split, resolver;
...@@ -38,14 +69,26 @@ define(['when'], function(when) { ...@@ -38,14 +69,26 @@ define(['when'], function(when) {
resolver: resolver, resolver: resolver,
name: name, name: name,
options: options, options: options,
resolve: function() { resolve: function(fallback, onBehalfOf) {
return self._resolve(resolver, name, options); return this.resolver
? self._resolve(resolver, name, options, onBehalfOf)
: fallback(name, options);
} }
}; };
}, },
_resolve: function(resolverName, name, options) { /**
var deferred, resolver; * Do the work of resolving a reference using registered plugins
* @param {string} resolverName plugin resolver name (e.g. "dom"), the part before the "!"
* @param {string} name reference name, the part after the "!"
* @param {object} options additional options to pass thru to a resolver plugin
* @param {string|*} onBehalfOf some indication of another component on whose behalf this
* reference is being resolved. Used to build a reference graph and detect cycles
* @return {object} promise for the resolved reference
* @private
*/
_resolve: function(resolverName, name, options, onBehalfOf) {
var deferred, resolver, api;
deferred = when.defer(); deferred = when.defer();
...@@ -53,13 +96,14 @@ define(['when'], function(when) { ...@@ -53,13 +96,14 @@ define(['when'], function(when) {
resolver = this._resolvers[resolverName]; resolver = this._resolvers[resolverName];
if (resolver) { if (resolver) {
resolver(deferred.resolver, name, options||{}, this._pluginApi); api = this._pluginApi.contextualize(onBehalfOf);
resolver(deferred.resolver, name, options||{}, api);
} else { } else {
deferred.reject('No resolver plugin found: ' + resolverName); deferred.reject(new Error('No resolver plugin found: ' + resolverName));
} }
} else { } else {
deferred.reject('Cannot resolve ref: ' + name); deferred.reject(new Error('Cannot resolve ref: ' + name));
} }
return deferred.promise; return deferred.promise;
...@@ -73,9 +117,5 @@ define(['when'], function(when) { ...@@ -73,9 +117,5 @@ define(['when'], function(when) {
// AMD // AMD
? define ? define
// CommonJS // CommonJS
: function(deps, factory) { : function(factory) { module.exports = factory(require); }
module.exports = factory.apply(this, deps.map(function(x) {
return require(x);
}));
}
); );
\ No newline at end of file
/** @license MIT License (c) copyright B Cavalier & J Hann */
/**
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*
* @author brian@hovercraftstudios.com
*/
(function(define) { 'use strict';
define(function(require) {
var when, defer, sequence, array, object, loader,
ComponentFactory, Lifecycle, Resolver, WireProxy, PluginRegistry,
undef;
when = require('when');
sequence = require('when/sequence');
array = require('./array');
object = require('./object');
loader = require('./loader');
ComponentFactory = require('./ComponentFactory');
Lifecycle = require('./lifecycle');
Resolver = require('./resolver');
WireProxy = require('./WireProxy');
PluginRegistry = require('./plugin/registry');
defer = when.defer;
function Scope(parent, options) {
this.parent = parent||{};
object.mixin(this, options);
}
Scope.prototype = {
init: function(spec) {
this._inherit(this.parent);
this._init();
this._configure();
return this._startup(spec).yield(this);
},
_inherit: function(parent) {
this.instances = this._inheritInstances(parent);
this.components = object.inherit(parent.components);
this.path = this._createPath(this.name, parent.path);
this.plugins = parent.plugins;
this.initializers = array.delegate(this.initializers);
this.destroyers = array.delegate(this.destroyers);
this.moduleLoader = loader(this.require, parent.moduleLoader).load;
},
_inheritInstances: function(parent) {
return object.inherit(parent.instances);
},
_createChild: function(spec, options) {
// Create child and arrange for it to be destroyed just before
// this scope is destroyed
var destroyTasks = this.destroyers;
return this.createContext(spec, this, options).then(
function(child) {
destroyTasks.push(function() {
return child.destroy();
});
return child;
}
);
},
_init: function() {
this._pluginApi = this._initPluginApi();
},
_initPluginApi: function() {
// Plugin API
// wire() API that is passed to plugins.
var self, pluginApi;
self = this;
pluginApi = {};
pluginApi.contextualize = function(name) {
function contextualApi(spec, id) {
return self._resolveItem(self._createComponentDef(id, spec));
}
contextualApi.createChild = self._createChild.bind(self);
contextualApi.loadModule = self.getModule.bind(self);
contextualApi.resolver = self.resolver;
contextualApi.addComponent = addComponent;
contextualApi.addInstance = addInstance;
contextualApi.resolveRef = function(ref) {
var onBehalfOf = arguments.length > 1 ? arguments[2] : name;
return self._resolveRef(ref, onBehalfOf);
};
contextualApi.getProxy = function(nameOrComponent) {
var onBehalfOf = arguments.length > 1 ? arguments[2] : name;
return self.getProxy(nameOrComponent, onBehalfOf);
};
return contextualApi;
};
return pluginApi;
function addComponent(component, id) {
var def, instance;
def = self._createComponentDef(id);
instance = self.componentFactory.processComponent(def, component);
return self._makeResolvable(def, instance);
}
function addInstance(instance, id) {
self._makeResolvable(self._createComponentDef(id), instance);
return when.resolve(instance);
}
},
_configure: function() {
var plugins, pluginApi;
plugins = this.plugins;
pluginApi = this._pluginApi;
this.resolver = this._createResolver(plugins, pluginApi);
this.componentFactory = this._createComponentFactory(plugins, pluginApi);
this._destroy = function() {
this._destroy = noop;
return this._executeDestroyers()
.then(this._destroyComponents.bind(this))
.then(this._releaseResources.bind(this));
};
},
_startup: function(spec) {
var self = this;
return this._executeInitializers().then(function() {
var parsed = self._parseSpec(spec);
return self._createComponents(parsed).then(function() {
return self._awaitInstances(parsed);
});
});
},
destroy: function() {
return this._destroy();
},
_destroy: noop,
_destroyComponents: function() {
var instances = this.instances;
return this.componentFactory.destroy().then(function() {
for (var p in instances) {
delete instances[p];
}
});
},
_releaseResources: function() {
// Free Objects
this.instances = this.components = this.parent
= this.resolver = this.componentFactory
= this._pluginApi = this.plugins
= undef;
},
getModule: function(moduleId) {
return typeof moduleId == 'string'
? this.moduleLoader(moduleId)
: when.resolve(moduleId);
},
getProxy: function(nameOrComponent, onBehalfOf) {
var componentFactory = this.componentFactory;
return typeof nameOrComponent == 'string'
? when(this._resolveRefName(nameOrComponent, {}, onBehalfOf), function (component) {
return componentFactory.createProxy(component);
})
: componentFactory.createProxy(nameOrComponent);
},
_createResolver: function(plugins, pluginApi) {
return new Resolver(plugins.resolvers, pluginApi);
},
_createComponentFactory: function(plugins, pluginApi) {
var self, factory, init, lifecycle;
self = this;
lifecycle = new Lifecycle(plugins, pluginApi);
factory = new ComponentFactory(lifecycle, plugins, pluginApi);
init = factory.initInstance;
factory.initInstance = function() {
return when(init.apply(factory, arguments), function(proxy) {
return self._makeResolvable(proxy.metadata, proxy);
});
};
return factory;
},
_executeInitializers: function() {
return sequence(this.initializers, this);
},
_executeDestroyers: function() {
return sequence(this.destroyers, this);
},
_parseSpec: function(spec) {
var instances, components, plugins, id, d;
instances = this.instances;
components = {};
// Setup a promise for each item in this scope
for (id in spec) {
if(id === '$plugins' || id === 'plugins') {
plugins = spec[id];
} else if (!object.hasOwn(instances, id)) {
// An initializer may have inserted concrete components
// into the context. If so, they override components of the
// same name from the input spec
d = defer();
components[id] = this._createComponentDef(id, spec[id], d.resolver);
instances[id] = d.promise;
}
}
return {
plugins: plugins,
components: components,
instances: instances
};
},
_createComponentDef: function(id, spec, resolver) {
return {
id: id,
spec: spec,
path: this._createPath(id, this.path),
resolver: resolver
};
},
_createComponents: function(parsed) {
// Process/create each item in scope and resolve its
// promise when completed.
var self, components;
self = this;
components = parsed.components;
return when.map(Object.keys(components), function(name) {
return self._createScopeItem(components[name]);
});
},
_awaitInstances: function(parsed) {
var instances = parsed.instances;
return when.map(Object.keys(instances), function(id) {
return instances[id];
});
},
_createScopeItem: function(component) {
// NOTE: Order is important here.
// The object & local property assignment MUST happen before
// the chain resolves so that the concrete item is in place.
// Otherwise, the whole scope can be marked as resolved before
// the final item has been resolved.
var self, item;
self = this;
item = this._resolveItem(component).then(function (resolved) {
self._makeResolvable(component, resolved);
return resolved;
});
component.resolver.resolve(item);
return item;
},
_makeResolvable: function(component, instance) {
var id = component.id;
if(id != null) {
this.instances[id] = WireProxy.getTarget(instance);
}
return instance;
},
_resolveItem: function(component) {
var item, spec;
spec = component.spec;
if (this.resolver.isRef(spec)) {
// Reference
item = this._resolveRef(spec, component.id);
} else {
// Component
item = this._createItem(component);
}
return item;
},
_createItem: function(component) {
var created, spec;
spec = component.spec;
if (Array.isArray(spec)) {
// Array
created = this._createArray(component);
} else if (object.isObject(spec)) {
// component spec, create the component
created = this._createComponent(component);
} else {
// Plain value
created = when.resolve(spec);
}
return created;
},
_createArray: function(component) {
var self, id, i;
self = this;
id = component.id;
i = 0;
// Minor optimization, if it's an empty array spec, just return an empty array.
return when.map(component.spec, function(item) {
var componentDef = self._createComponentDef(id + '[' + (i++) + ']', item);
return self._resolveItem(componentDef);
});
},
_createComponent: function(component) {
var self = this;
return this.componentFactory.create(component)
.otherwise(function (reason) {
if(reason !== component) {
throw reason;
}
// No factory found, treat object spec as a nested scope
return new Scope(self)
.init(component.spec)
.then(function (childScope) {
// TODO: find a lighter weight solution
// We're paying the cost of creating a complete scope,
// then discarding everything except the instance map.
return object.mixin({}, childScope.instances);
}
);
}
);
},
_resolveRef: function(ref, onBehalfOf) {
var scope;
ref = this.resolver.parse(ref);
scope = onBehalfOf == ref.name && this.parent.instances ? this.parent : this;
return this._doResolveRef(ref, scope.instances, onBehalfOf);
},
_resolveRefName: function(refName, options, onBehalfOf) {
var ref = this.resolver.create(refName, options);
return this._doResolveRef(ref, this.instances, onBehalfOf);
},
_doResolveRef: function(ref, scope, onBehalfOf) {
return ref.resolve(function (name) {
return resolveDeepName(name, scope);
}, onBehalfOf);
},
_createPath: function(name, basePath) {
var path = basePath || this.path;
return (path && name) ? (path + '.' + name) : name;
}
};
return Scope;
function resolveDeepName(name, scope) {
var parts = name.split('.');
if(parts.length > 2) {
return when.reject('Only 1 "." is allowed in refs: ' + name);
}
return when.reduce(parts, function(scope, segment) {
return segment in scope
? scope[segment]
: when.reject('Cannot resolve ref: ' + name);
}, scope);
}
function noop() {}
});
})(typeof define == 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }
);
\ No newline at end of file
...@@ -44,7 +44,7 @@ define(['./lib/plugin-base/on', './lib/dom/base'], function (createOnPlugin, bas ...@@ -44,7 +44,7 @@ define(['./lib/plugin-base/on', './lib/dom/base'], function (createOnPlugin, bas
on.wire$plugin = createOnPlugin({ on.wire$plugin = createOnPlugin({
on: on on: on
}).wire$plugin; });
if (document && document.compareDocumentPosition) { if (document && document.compareDocumentPosition) {
contains = function w3cContains (refNode, testNode) { contains = function w3cContains (refNode, testNode) {
......
{
"name": "wire",
"version": "0.9.4",
"description": "A light, fast, flexible Javascript IOC container.",
"keywords": [
"ioc",
"aop",
"dependency injection",
"dependency inversion",
"application composition",
"cujo"
],
"licenses": [
{
"type": "MIT",
"url": "http://www.opensource.org/licenses/mit-license.php"
}
],
"repositories": [
{
"type": "git",
"url": "https://github.com/cujojs/wire"
}
],
"bugs": "https://github.com/cujojs/wire/issues",
"maintainers": [
{
"name": "Brian Cavalier",
"web": "http://hovercraftstudios.com"
}
],
"contributors": [
{
"name": "Brian Cavalier",
"web": "http://hovercraftstudios.com"
},
{
"name": "John Hann",
"web": "http://unscriptable.com"
}
],
"dependencies": {
"meld": "~1",
"when": ">=1.5.0 || ~2.0"
},
"devDependencies": {
"buster": "~0.6"
},
"main": "./wire",
"directories": {
"test": "test"
},
"scripts": {
"test": "buster test -e node"
}
}
\ No newline at end of file
/** @license MIT License (c) copyright B Cavalier & J Hann */ /** @license MIT License (c) copyright 2011-2013 original author or authors */
/*jshint sub:true*/ /*jshint sub:true*/
...@@ -6,33 +6,30 @@ ...@@ -6,33 +6,30 @@
* wire * wire
* Javascript IOC Container * Javascript IOC Container
* *
* wire is part of the cujo.js family of libraries (http://cujojs.com/) * wire is part of the cujoJS family of libraries (http://cujojs.com/)
* *
* Licensed under the MIT License at: * Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
* *
* @version 0.9.4 * @author Brian Cavalier
* @author John Hann
* @version 0.10.0
*/ */
(function(global, define){ (function(rootSpec, define){ 'use strict';
define(['require', 'when', './lib/context'], function(require, when, createContext) { define(function(require) {
"use strict"; var createContext, rootContext, rootOptions;
var rootSpec, rootContext, rootOptions; wire.version = '0.10.0';
wire.version = "0.9.4"; createContext = require('./lib/context');
rootSpec = global['wire'] || {};
rootOptions = { require: require }; rootOptions = { require: require };
//
// Module API
//
/** /**
* The top-level wire function that wires contexts as direct children * Main Programmtic API. The top-level wire function that wires contexts
* of the (possibly implicit) root context. It ensures that the root * as direct children of the (possibly implicit) root context. It ensures
* context has been wired before wiring children. * that the root context has been wired before wiring children.
* *
* @public * @public
* *
...@@ -58,11 +55,9 @@ define(['require', 'when', './lib/context'], function(require, when, createConte ...@@ -58,11 +55,9 @@ define(['require', 'when', './lib/context'], function(require, when, createConte
} }
// Use the rootContext to wire all new contexts. // Use the rootContext to wire all new contexts.
return when(rootContext, return rootContext.then(function (root) {
function (root) { return root.wire(spec, options);
return root.wire(spec, options); });
}
);
} }
/** /**
...@@ -82,27 +77,21 @@ define(['require', 'when', './lib/context'], function(require, when, createConte ...@@ -82,27 +77,21 @@ define(['require', 'when', './lib/context'], function(require, when, createConte
setTimeout(function() { throw e; }, 0); setTimeout(function() { throw e; }, 0);
}; };
when(wire(name.split(','), { require: require }), callback, errback); wire(name.split(','), { require: require }).then(callback, errback);
}; };
/** /**
* AMD Builder plugin API * AMD Builder plugin API
*/ */
// pluginBuilder: './build/amd/builder' // pluginBuilder: './builder/rjs'
// cram > v0.2 will support pluginBuilder property wire['pluginBuilder'] = './builder/rjs';
wire['pluginBuilder'] = './build/amd/builder'; wire['cramPlugin'] = './builder/cram';
return wire; return wire;
}); });
})(this, })(
typeof define == 'function' this['wire'] || {},
// AMD typeof define == 'function' && define.amd
? define ? define : function(factory) { module.exports = factory(require); }
// CommonJS
: function(deps, factory) {
module.exports = factory.apply(this, [require].concat(deps.slice(1).map(function(x) {
return require(x);
})));
}
); );
\ No newline at end of file
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
<body> <body>
<section id="todoapp"></section> <section id="todoapp"></section>
<script src="bower_components/todomvc-common/base.js"></script> <script src="bower_components/todomvc-common/base.js"></script>
<script src="bower_components/curl/src/curl.js"></script> <script data-curl-run="app/run" src="bower_components/curl/src/curl.js"></script>
<script src="app/run.js"></script>
</body> </body>
</html> </html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment