-
-
Save mikaelkaron/4508399 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require([], function(){ | |
var contextRequire = require.config({ | |
"paths" : { | |
"troopjs-bundle":"lib/troopjs-bundle/1.0.7/troopjs-bundle.min", | |
"jquery" : "//cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min" | |
}, | |
"map" : { | |
"*" : { | |
"template" : "troopjs-requirejs/template" | |
} | |
} | |
}); | |
var troopjsDep = ["jquery", "troopjs-bundle"]; | |
contextRequire(troopjsDep, function($) { | |
var troopjsJQueryDep = ["troopjs-jquery/weave", "troopjs-jquery/destroy", "troopjs-jquery/action", | |
"troopjs-jquery/resize", "troopjs-jquery/dimensions", "troopjs-jquery/hashchange"]; | |
contextRequire([ | |
"widget/application" | |
].concat(troopjsJQueryDep), function App(Application) { | |
$(function ready() { | |
Application($(this.documentElement), "app/demo").start(); | |
}); | |
}); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
define([ | |
"compose", | |
"jquery", | |
"troopjs-utils/deferred", | |
"troopjs-utils/when", | |
"troopjs-utils/tr", | |
"troopjs-utils/grep", | |
"troopjs-utils/uri", | |
"troopjs-core/widget/application", | |
"troopjs-core/route/router", | |
"troopjs-core/remote/ajax" | |
], function ApplicationModule(Compose, $, Deferred, when, tr, grep, URI, Application, Router, Ajax) { | |
"use strict"; | |
var SERVICES = "services"; | |
/** | |
* Forwards signals to services | |
* @param signal Signal | |
* @param deferred Deferred | |
* @returns me | |
*/ | |
function forward(signal, deferred) { | |
var me = this; | |
var services = tr.call(me[SERVICES], function (service, index) { | |
return Deferred(function (dfd) { | |
service.signal(signal, dfd); | |
}); | |
}); | |
if (deferred) { | |
when.apply($, services).then(deferred.resolve, deferred.reject, deferred.notify); | |
} | |
me.publish("application/signal/" + signal, deferred); | |
return me; | |
} | |
return Application.extend(function ($element, name) { | |
var $window = $(window); | |
this[SERVICES] = [ Router($window), Ajax()]; | |
}, { | |
"sig/initialize" : forward, | |
"sig/finalize" : forward, | |
"sig/start" : forward, | |
"sig/stop" : forward | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
define({}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
define([ | |
"troopjs-core/component/widget" | |
], function DemoModule(Widget) { | |
"use strict"; | |
return Widget.extend({ | |
"dom/click": function click() { | |
alert("click"); | |
} | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> | |
<script type="text/javascript"> | |
function add(){ | |
for (var i = 0; i < 500; i++) { | |
$('<div style="background-color: #A68585;margin: 2px 0;width: 100px;height: 30px;">' + i + '</div>').appendTo('.box'); | |
} | |
} | |
function remove(){ | |
$('.box').empty(); | |
} | |
</script> | |
<title></title> | |
</head> | |
<body> | |
<button onclick="add();">add div</button> | |
<button onclick="remove();">remove div</button> | |
<div class="box"> | |
</div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> | |
<script type="text/javascript"> | |
function add(){ | |
for (var i = 0; i < 500; i++) { | |
$('<div style="background-color: #A68585;margin: 2px 0;width: 100px;height: 30px;">' + i + '</div>').click(function () { | |
$(this).css('background-color','#A6EE85'); | |
}).appendTo('.box'); | |
} | |
} | |
function remove(){ | |
$('.box').empty(); | |
} | |
</script> | |
<title></title> | |
</head> | |
<body> | |
<button onclick="add();">add div</button> | |
<button onclick="remove();">remove div</button> | |
<div class="box"> | |
</div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<script type="text/javascript"> | |
function add(){ | |
for (var i = 0; i < 500; i++) { | |
$('<div data-weave="widget/demo" style="background-color: #A68585;margin: 2px 0;width: 100px;height: 30px;">' + i + '</div>').appendTo('.box'); | |
} | |
$('[data-weave]').weave(); | |
} | |
function remove(){ | |
$('.box').empty(); | |
} | |
</script> | |
<title>mejs memory leak demo</title> | |
</head> | |
<body> | |
<button onclick="add();">add</button> | |
<button onclick="remove();">remove</button> | |
<div class="box"></div> | |
<script type="text/javascript" data-main="js/app.js" src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.0.6/require.min.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<script type="text/javascript"> | |
function add(){ | |
for (var i = 0; i < 500; i++) { | |
$('<div data-weave="widget/demo" style="background-color: #A68585;margin: 2px 0;width: 100px;height: 30px;">' + i + '</div>').appendTo('.box'); | |
} | |
$('[data-weave]').weave(); | |
} | |
function remove(){ | |
$('.box').empty(); | |
} | |
var require = { | |
"baseUrl" : "js", | |
"packages" : [{ | |
"name" : "jquery", | |
"location" : "//cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3", | |
"main" : "jquery.min" | |
}, { | |
"name" : "troopjs-bundle", | |
"location" : "lib/troopjs-bundle/1.0.7-31", | |
"main" : "troopjs-bundle" | |
}], | |
"map" : { | |
"*" : { | |
"template" : "troopjs-requirejs/template", | |
"troopjs-core/component/widget" : "troopjs-browser/component/widget" | |
} | |
}, | |
"config" : { | |
"when" : { | |
"paranoid" : false | |
} | |
}, | |
"deps" : [ "require", "troopjs-bundle" ], | |
"callback" : function Boot(localRequire) { | |
localRequire([ "jquery", "troopjs-browser/application/widget" ], function Strap(jQuery, Application) { | |
jQuery(function ready($) { | |
var $HTML = $("html"); | |
Application($HTML, "bootstrap", []).start(); | |
}); | |
}); | |
} | |
}; | |
</script> | |
<title>mejs memory leak demo</title> | |
</head> | |
<body> | |
<button onclick="add();">add</button> | |
<button onclick="remove();">remove</button> | |
<div class="box"></div> | |
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.1/require.min.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<script type="text/javascript"> | |
function add(){ | |
for (var i = 0; i < 500; i++) { | |
$('<div data-weave="widget/demo" style="background-color: #A68585;margin: 2px 0;width: 100px;height: 30px;">' + i + '</div>').appendTo('.box'); | |
} | |
$('[data-weave]').weave(); | |
} | |
function remove(){ | |
$('.box').empty(); | |
} | |
var require = { | |
"baseUrl" : "js", | |
"packages" : [{ | |
"name" : "jquery", | |
"location" : "//cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3", | |
"main" : "jquery.min" | |
}, { | |
"name" : "troopjs-bundle", | |
"location" : "lib/troopjs-bundle/1.0.7-next", | |
"main" : "troopjs-bundle" | |
}], | |
"map" : { | |
"*" : { | |
"template" : "troopjs-requirejs/template", | |
"troopjs-core/component/widget" : "troopjs-browser/component/widget" | |
} | |
}, | |
"config" : { | |
"when" : { | |
"paranoid" : false | |
} | |
}, | |
"deps" : [ "require", "troopjs-bundle" ], | |
"callback" : function Boot(localRequire) { | |
localRequire([ "jquery", "troopjs-browser/application/widget" ], function Strap(jQuery, Application) { | |
jQuery(function ready($) { | |
var $HTML = $("html"); | |
Application($HTML, "bootstrap", []).start(); | |
}); | |
}); | |
} | |
}; | |
</script> | |
<title>mejs memory leak demo</title> | |
</head> | |
<body> | |
<button onclick="add();">add</button> | |
<button onclick="remove();">remove</button> | |
<div class="box"></div> | |
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.1/require.min.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* TroopJS Bundle - 1.0.7-31-g8d0ee03-dirty | |
* http://troopjs.com/ | |
* Copyright (c) 2013 Mikael Karon <[email protected]> | |
* Licensed MIT | |
*/ | |
/*! | |
* TroopJS RequireJS template plug-in | |
* | |
* parts of code from require-cs 0.4.0+ Copyright (c) 2010-2011, The Dojo Foundation | |
* | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false, require:false*/ | |
define('troopjs-requirejs/template',[],function TemplateModule() { | |
/*jshint strict:false, smarttabs:true, laxbreak:true, newcap:false, loopfunc:true */ | |
var FACTORIES = { | |
"node" : function () { | |
// Using special require.nodeRequire, something added by r.js. | |
var fs = require.nodeRequire("fs"); | |
return function fetchText(path, callback) { | |
callback(fs.readFileSync(path, 'utf8')); | |
}; | |
}, | |
"browser" : function () { | |
// Would love to dump the ActiveX crap in here. Need IE 6 to die first. | |
var progIds = [ "Msxml2.XMLHTTP", "Microsoft.XMLHTTP", "Msxml2.XMLHTTP.4.0"]; | |
var progId; | |
var XHR; | |
var i; | |
if (typeof XMLHttpRequest !== "undefined") { | |
XHR = XMLHttpRequest; | |
} | |
else { | |
for (i = 0; i < 3; i++) { | |
progId = progIds[i]; | |
try { | |
new ActiveXObject(progId); | |
XHR = function(){ | |
return new ActiveXObject(progId); | |
}; | |
break; | |
} | |
catch (e) { | |
} | |
} | |
if (!XHR){ | |
throw new Error("XHR: XMLHttpRequest not available"); | |
} | |
} | |
return function fetchText(url, callback) { | |
var xhr = new XHR(); | |
xhr.open('GET', url, true); | |
xhr.onreadystatechange = function (evt) { | |
// Do not explicitly handle errors, those should be | |
// visible via console output in the browser. | |
if (xhr.readyState === 4) { | |
callback(xhr.responseText); | |
} | |
}; | |
xhr.send(null); | |
}; | |
}, | |
"rhino" : function () { | |
var encoding = "utf-8"; | |
var lineSeparator = java.lang.System.getProperty("line.separator"); | |
// Why Java, why is this so awkward? | |
return function fetchText(path, callback) { | |
var file = new java.io.File(path); | |
var input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)); | |
var stringBuffer = new java.lang.StringBuffer(); | |
var line; | |
var content = ""; | |
try { | |
line = input.readLine(); | |
// Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 | |
// http://www.unicode.org/faq/utf_bom.html | |
// Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: | |
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 | |
if (line && line.length() && line.charAt(0) === 0xfeff) { | |
// Eat the BOM, since we've already found the encoding on this file, | |
// and we plan to concatenating this buffer with others; the BOM should | |
// only appear at the top of a file. | |
line = line.substring(1); | |
} | |
stringBuffer.append(line); | |
while ((line = input.readLine()) !== null) { | |
stringBuffer.append(lineSeparator); | |
stringBuffer.append(line); | |
} | |
// Make sure we return a JavaScript string and not a Java string. | |
content = String(stringBuffer.toString()); // String | |
} finally { | |
input.close(); | |
} | |
callback(content); | |
}; | |
}, | |
"borked" : function () { | |
return function fetchText() { | |
throw new Error("Environment unsupported."); | |
}; | |
} | |
}; | |
var RE_SANITIZE = /^[\n\t\r]+|[\n\t\r]+$/g; | |
var RE_BLOCK = /<%(=)?([\S\s]*?)%>/g; | |
var RE_TOKENS = /<%(\d+)%>/gm; | |
var RE_REPLACE = /(["\n\t\r])/gm; | |
var RE_CLEAN = /o \+= "";| \+ ""/gm; | |
var EMPTY = ""; | |
var REPLACE = { | |
"\"" : "\\\"", | |
"\n" : "\\n", | |
"\t" : "\\t", | |
"\r" : "\\r" | |
}; | |
/** | |
* Compiles template | |
* | |
* @param body Template body | |
* @returns {Function} | |
*/ | |
function compile(body) { | |
var blocks = []; | |
var length = 0; | |
function blocksTokens(original, prefix, block) { | |
blocks[length] = prefix | |
? "\" +" + block + "+ \"" | |
: "\";" + block + "o += \""; | |
return "<%" + String(length++) + "%>"; | |
} | |
function tokensBlocks(original, token) { | |
return blocks[token]; | |
} | |
function replace(original, token) { | |
return REPLACE[token] || token; | |
} | |
return ("function template(data) { var o = \"" | |
// Sanitize body before we start templating | |
+ body.replace(RE_SANITIZE, "") | |
// Replace script blocks with tokens | |
.replace(RE_BLOCK, blocksTokens) | |
// Replace unwanted tokens | |
.replace(RE_REPLACE, replace) | |
// Replace tokens with script blocks | |
.replace(RE_TOKENS, tokensBlocks) | |
+ "\"; return o; }") | |
// Clean | |
.replace(RE_CLEAN, EMPTY); | |
} | |
var buildMap = {}; | |
var fetchText = FACTORIES[ typeof process !== "undefined" && process.versions && !!process.versions.node | |
? "node" | |
: (typeof window !== "undefined" && window.navigator && window.document) || typeof importScripts !== "undefined" | |
? "browser" | |
: typeof Packages !== "undefined" | |
? "rhino" | |
: "borked" ](); | |
return { | |
load: function (name, parentRequire, load, config) { | |
var path = parentRequire.toUrl(name); | |
fetchText(path, function (text) { | |
try { | |
text = "define(function() { return " + compile(text, name, path, config.template) + "; })"; | |
} | |
catch (err) { | |
err.message = "In " + path + ", " + err.message; | |
throw(err); | |
} | |
if (config.isBuild) { | |
buildMap[name] = text; | |
} | |
// IE with conditional comments on cannot handle the | |
// sourceURL trick, so skip it if enabled | |
/*@if (@_jscript) @else @*/ | |
else { | |
text += "\n//@ sourceURL='" + path +"'"; | |
} | |
/*@end@*/ | |
load.fromText(name, text); | |
// Give result to load. Need to wait until the module | |
// is fully parse, which will happen after this | |
// execution. | |
parentRequire([name], function (value) { | |
load(value); | |
}); | |
}); | |
}, | |
write: function (pluginName, name, write) { | |
if (buildMap.hasOwnProperty(name)) { | |
write.asModule(pluginName + "!" + name, buildMap[name]); | |
} | |
} | |
}; | |
}); | |
/*! | |
* TroopJS jQuery hashchange plug-in | |
* | |
* Normalized hashchange event, ripped a _lot_ of code from | |
* https://github.com/millermedeiros/Hasher | |
* | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-jquery/hashchange',[ "jquery" ], function HashchangeModule($) { | |
/*jshint strict:false, smarttabs:true, laxbreak:true, evil:true */ | |
var INTERVAL = "interval"; | |
var HASHCHANGE = "hashchange"; | |
var ONHASHCHANGE = "on" + HASHCHANGE; | |
var RE_HASH = /#(.*)$/; | |
var RE_LOCAL = /\?/; | |
// hack based on this: http://code.google.com/p/closure-compiler/issues/detail?id=47#c13 | |
var _isIE = /**@preserve@cc_on !@*/0; | |
function getHash(window) { | |
// parsed full URL instead of getting location.hash because Firefox | |
// decode hash value (and all the other browsers don't) | |
// also because of IE8 bug with hash query in local file | |
var result = RE_HASH.exec(window.location.href); | |
return result && result[1] | |
? decodeURIComponent(result[1]) | |
: ""; | |
} | |
function Frame(document) { | |
var self = this; | |
var element; | |
self.element = element = document.createElement("iframe"); | |
element.src = "about:blank"; | |
element.style.display = "none"; | |
} | |
Frame.prototype = { | |
getElement : function () { | |
return this.element; | |
}, | |
getHash : function () { | |
return this.element.contentWindow.frameHash; | |
}, | |
update : function (hash) { | |
var self = this; | |
var document = self.element.contentWindow.document; | |
// Quick return if hash has not changed | |
if (self.getHash() === hash) { | |
return; | |
} | |
// update iframe content to force new history record. | |
// based on Really Simple History, SWFAddress and YUI.history. | |
document.open(); | |
document.write("<html><head><title>' + document.title + '</title><script type='text/javascript'>var frameHash='" + hash + "';</script></head><body> </body></html>"); | |
document.close(); | |
} | |
}; | |
$.event.special[HASHCHANGE] = { | |
/** | |
* @param data (Anything) Whatever eventData (optional) was passed in | |
* when binding the event. | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
* @param eventHandle (Function) The actual function that will be bound | |
* to the browser’s native event (this is used internally for the | |
* beforeunload event, you’ll never use it). | |
*/ | |
setup : function hashChangeSetup(data, namespaces, eventHandle) { | |
var window = this; | |
// Quick return if we support onHashChange natively | |
// FF3.6+, IE8+, Chrome 5+, Safari 5+ | |
if (ONHASHCHANGE in window) { | |
return false; | |
} | |
// Make sure we're always a window | |
if (!$.isWindow(window)) { | |
throw new Error("Unable to bind 'hashchange' to a non-window object"); | |
} | |
var $window = $(window); | |
var hash = getHash(window); | |
var location = window.location; | |
$window.data(INTERVAL, window.setInterval(_isIE | |
? (function hashChangeIntervalWrapper() { | |
var document = window.document; | |
var _isLocal = location.protocol === "file:"; | |
var frame = new Frame(document); | |
document.body.appendChild(frame.getElement()); | |
frame.update(hash); | |
return function hashChangeInterval() { | |
var oldHash = hash; | |
var newHash; | |
var windowHash = getHash(window); | |
var frameHash = frame.getHash(); | |
// Detect changes made pressing browser history buttons. | |
// Workaround since history.back() and history.forward() doesn't | |
// update hash value on IE6/7 but updates content of the iframe. | |
if (frameHash !== hash && frameHash !== windowHash) { | |
// Fix IE8 while offline | |
newHash = decodeURIComponent(frameHash); | |
if (hash !== newHash) { | |
hash = newHash; | |
frame.update(hash); | |
$window.trigger(HASHCHANGE, [ newHash, oldHash ]); | |
} | |
// Sync location.hash with frameHash | |
location.hash = "#" + encodeURI(_isLocal | |
? frameHash.replace(RE_LOCAL, "%3F") | |
: frameHash); | |
} | |
// detect if hash changed (manually or using setHash) | |
else if (windowHash !== hash) { | |
// Fix IE8 while offline | |
newHash = decodeURIComponent(windowHash); | |
if (hash !== newHash) { | |
hash = newHash; | |
$window.trigger(HASHCHANGE, [ newHash, oldHash ]); | |
} | |
} | |
}; | |
})() | |
: function hashChangeInterval() { | |
var oldHash = hash; | |
var newHash; | |
var windowHash = getHash(window); | |
if (windowHash !== hash) { | |
// Fix IE8 while offline | |
newHash = decodeURIComponent(windowHash); | |
if (hash !== newHash) { | |
hash = newHash; | |
$window.trigger(HASHCHANGE, [ newHash, oldHash ]); | |
} | |
} | |
}, 25)); | |
}, | |
/** | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
*/ | |
teardown : function hashChangeTeardown(namespaces) { | |
var window = this; | |
// Quick return if we support onHashChange natively | |
if (ONHASHCHANGE in window) { | |
return false; | |
} | |
window.clearInterval($.data(window, INTERVAL)); | |
} | |
}; | |
}); | |
/*! | |
* TroopJS Utils getargs module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-utils/getargs',[],function GetArgsModule() { | |
/*jshint strict:false */ | |
var PUSH = Array.prototype.push; | |
var SUBSTRING = String.prototype.substring; | |
var RE_BOOLEAN = /^(?:false|true)$/i; | |
var RE_BOOLEAN_TRUE = /^true$/i; | |
var RE_DIGIT = /^\d+$/; | |
return function getargs() { | |
var self = this; | |
var result = []; | |
var length; | |
var from; | |
var to; | |
var i; | |
var c; | |
var a; | |
var q = false; | |
// Iterate over string | |
for (from = to = i = 0, length = self.length; i < length; i++) { | |
// Get char | |
c = self.charAt(i); | |
switch(c) { | |
case "\"" : | |
case "'" : | |
// If we are currently quoted... | |
if (q === c) { | |
// Stop quote | |
q = false; | |
// Store result (no need to convert, we know this is a string) | |
PUSH.call(result, SUBSTRING.call(self, from, to)); | |
} | |
// Otherwise | |
else { | |
// Start quote | |
q = c; | |
} | |
// Update from/to | |
from = to = i + 1; | |
break; | |
case "," : | |
// Continue if we're quoted | |
if (q) { | |
to = i + 1; | |
break; | |
} | |
// If we captured something... | |
if (from !== to) { | |
a = SUBSTRING.call(self, from, to); | |
if (RE_BOOLEAN.test(a)) { | |
a = RE_BOOLEAN_TRUE.test(a); | |
} | |
else if (RE_DIGIT.test(a)) { | |
a = +a; | |
} | |
// Store result | |
PUSH.call(result, a); | |
} | |
// Update from/to | |
from = to = i + 1; | |
break; | |
case " " : | |
case "\t" : | |
// Continue if we're quoted | |
if (q) { | |
to = i + 1; | |
break; | |
} | |
// Update from/to | |
if (from === to) { | |
from = to = i + 1; | |
} | |
break; | |
default : | |
// Update to | |
to = i + 1; | |
} | |
} | |
// If we captured something... | |
if (from !== to) { | |
a = SUBSTRING.call(self, from, to); | |
if (RE_BOOLEAN.test(a)) { | |
a = RE_BOOLEAN_TRUE.test(a); | |
} | |
else if (RE_DIGIT.test(a)) { | |
a = +a; | |
} | |
// Store result | |
PUSH.call(result, a); | |
} | |
return result; | |
}; | |
}); | |
/*! | |
* TroopJS jQuery action plug-in | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-jquery/action',[ "jquery", "troopjs-utils/getargs" ], function ActionModule($, getargs) { | |
/*jshint strict:false, smarttabs:true, laxbreak:true */ | |
var UNDEFINED; | |
var FALSE = false; | |
var NULL = null; | |
var SLICE = Array.prototype.slice; | |
var ACTION = "action"; | |
var ORIGINALEVENT = "originalEvent"; | |
var RE_ACTION = /^([\w\d\s_\-\/]+)(?:\.([\w\.]+))?(?:\((.*)\))?$/; | |
var RE_DOT = /\.+/; | |
/** | |
* Namespace iterator | |
* @param namespace (string) namespace | |
* @param index (number) index | |
*/ | |
function namespaceIterator(namespace, index) { | |
return namespace ? namespace + "." + ACTION : NULL; | |
} | |
/** | |
* Action handler | |
* @param $event (jQuery.Event) event | |
*/ | |
function onAction($event) { | |
// Set $target | |
var $target = $(this); | |
// Get argv | |
var argv = SLICE.call(arguments, 1); | |
// Extract type | |
var type = ORIGINALEVENT in $event | |
? $event[ORIGINALEVENT].type | |
: ACTION; | |
// Extract name | |
var name = $event[ACTION]; | |
// Reset $event.type | |
$event.type = ACTION + "/" + name + "." + type; | |
// Trigger 'ACTION/{name}.{type}' | |
$target.trigger($event, argv); | |
// No handler, try without namespace, but exclusive | |
if ($event.result !== FALSE) { | |
// Reset $event.type | |
$event.type = ACTION + "/" + name + "!"; | |
// Trigger 'ACTION/{name}' | |
$target.trigger($event, argv); | |
// Still no handler, try generic action with namespace | |
if ($event.result !== FALSE) { | |
// Reset $event.type | |
$event.type = ACTION + "." + type; | |
// Trigger 'ACTION.{type}' | |
$target.trigger($event, argv); | |
} | |
} | |
} | |
/** | |
* Internal handler | |
* | |
* @param $event jQuery event | |
*/ | |
function handler($event) { | |
// Get closest element that has an action defined | |
var $target = $($event.target).closest("[data-action]"); | |
// Fail fast if there is no action available | |
if ($target.length === 0) { | |
return; | |
} | |
// Extract all data in one go | |
var $data = $target.data(); | |
// Extract matches from 'data-action' | |
var matches = RE_ACTION.exec($data[ACTION]); | |
// Return fast if action parameter was f*cked (no matches) | |
if (matches === NULL) { | |
return; | |
} | |
// Extract action name | |
var name = matches[1]; | |
// Extract action namespaces | |
var namespaces = matches[2]; | |
// Extract action args | |
var args = matches[3]; | |
// If there are action namespaces, make sure we're only triggering action on applicable types | |
if (namespaces !== UNDEFINED && !RegExp(namespaces.split(RE_DOT).join("|")).test($event.type)) { | |
return; | |
} | |
// Split args by separator (if there were args) | |
var argv = args !== UNDEFINED | |
? getargs.call(args) | |
: []; | |
// Iterate argv to determine arg type | |
$.each(argv, function argsIterator(i, value) { | |
if (value in $data) { | |
argv[i] = $data[value]; | |
} | |
}); | |
$target | |
// Trigger exclusive ACTION event | |
.trigger($.Event($event, { | |
type: ACTION + "!", | |
action: name | |
}), argv); | |
// Since we've translated the event, stop propagation | |
$event.stopPropagation(); | |
} | |
$.event.special[ACTION] = { | |
/** | |
* @param data (Anything) Whatever eventData (optional) was passed in | |
* when binding the event. | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
* @param eventHandle (Function) The actual function that will be bound | |
* to the browser’s native event (this is used internally for the | |
* beforeunload event, you’ll never use it). | |
*/ | |
setup : function onActionSetup(data, namespaces, eventHandle) { | |
$(this).bind(ACTION, data, onAction); | |
}, | |
/** | |
* Do something each time an event handler is bound to a particular element | |
* @param handleObj (Object) | |
*/ | |
add : function onActionAdd(handleObj) { | |
var events = $.map(handleObj.namespace.split(RE_DOT), namespaceIterator); | |
if (events.length !== 0) { | |
$(this).bind(events.join(" "), handler); | |
} | |
}, | |
/** | |
* Do something each time an event handler is unbound from a particular element | |
* @param handleObj (Object) | |
*/ | |
remove : function onActionRemove(handleObj) { | |
var events = $.map(handleObj.namespace.split(RE_DOT), namespaceIterator); | |
if (events.length !== 0) { | |
$(this).unbind(events.join(" "), handler); | |
} | |
}, | |
/** | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
*/ | |
teardown : function onActionTeardown(namespaces) { | |
$(this).unbind(ACTION, onAction); | |
} | |
}; | |
$.fn[ACTION] = function action(name) { | |
return $(this).trigger({ | |
type: ACTION + "!", | |
action: name | |
}, SLICE.call(arguments, 1)); | |
}; | |
}); | |
/*! | |
* TroopJS jQuery destroy plug-in | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-jquery/destroy',[ "jquery" ], function DestroyModule($) { | |
/*jshint strict:false, smarttabs:true */ | |
$.event.special.destroy = { | |
remove : function onDestroyRemove(handleObj) { | |
var self = this; | |
handleObj.handler.call(self, $.Event({ | |
"type" : handleObj.type, | |
"data" : handleObj.data, | |
"namespace" : handleObj.namespace, | |
"target" : self | |
})); | |
} | |
}; | |
}); | |
/*! | |
* TroopJS jQuery weave plug-in | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-jquery/weave',[ "require", "jquery", "troopjs-utils/getargs", "./destroy" ], function WeaveModule(parentRequire, $, getargs) { | |
/*jshint strict:false, smarttabs:true, laxbreak:true, loopfunc:true */ | |
var UNDEFINED; | |
var NULL = null; | |
var ARRAY = Array; | |
var ARRAY_PROTO = ARRAY.prototype; | |
var JOIN = ARRAY_PROTO.join; | |
var PUSH = ARRAY_PROTO.push; | |
var $WHEN = $.when; | |
var $DEFERRED = $.Deferred; | |
var WEAVE = "weave"; | |
var UNWEAVE = "unweave"; | |
var WOVEN = "woven"; | |
var WEAVING = "weaving"; | |
var PENDING = "pending"; | |
var DESTROY = "destroy"; | |
var DATA = "data-"; | |
var DATA_WEAVE = DATA + WEAVE; | |
var DATA_WOVEN = DATA + WOVEN; | |
var DATA_WEAVING = DATA + WEAVING; | |
var SELECTOR_WEAVE = "[" + DATA_WEAVE + "]"; | |
var SELECTOR_UNWEAVE = "[" + DATA_WEAVING + "],[" + DATA_WOVEN + "]"; | |
/** | |
* Generic destroy handler. | |
* Simply makes sure that unweave has been called | |
*/ | |
function onDestroy() { | |
$(this).unweave(); | |
} | |
$.expr[":"][WEAVE] = $.expr.createPseudo | |
? $.expr.createPseudo(function (widgets) { | |
if (widgets !== UNDEFINED) { | |
widgets = RegExp($.map(getargs.call(widgets), function (widget) { | |
return "^" + widget + "$"; | |
}).join("|"), "m"); | |
} | |
return function (element) { | |
var weave = $(element).attr(DATA_WEAVE); | |
return weave === UNDEFINED | |
? false | |
: widgets === UNDEFINED | |
? true | |
: widgets.test(weave.split(/[\s,]+/).join("\n")); | |
}; | |
}) | |
: function (element, index, match) { | |
var weave = $(element).attr(DATA_WEAVE); | |
return weave === UNDEFINED | |
? false | |
: match === UNDEFINED | |
? true | |
: RegExp($.map(getargs.call(match[3]), function (widget) { | |
return "^" + widget + "$"; | |
}).join("|"), "m").test(weave.split(/[\s,]+/).join("\n")); | |
}; | |
$.expr[":"][WOVEN] = $.expr.createPseudo | |
? $.expr.createPseudo(function (widgets) { | |
if (widgets !== UNDEFINED) { | |
widgets = RegExp($.map(getargs.call(widgets), function (widget) { | |
return "^" + widget + "@\\d+"; | |
}).join("|"), "m"); | |
} | |
return function (element) { | |
var woven = $(element).attr(DATA_WOVEN); | |
return woven === UNDEFINED | |
? false | |
: widgets === UNDEFINED | |
? true | |
: widgets.test(woven.split(/[\s,]+/).join("\n")); | |
}; | |
}) | |
: function (element, index, match) { | |
var woven = $(element).attr(DATA_WOVEN); | |
return woven === UNDEFINED | |
? false | |
: match === UNDEFINED | |
? true | |
: RegExp($.map(getargs.call(match[3]), function (widget) { | |
return "^" + widget + "@\\d+"; | |
}).join("|"), "m").test(woven.split(/[\s,]+/).join("\n")); | |
}; | |
$.fn[WEAVE] = function weave(/* arg, arg, arg */) { | |
var widgets = []; | |
var i = 0; | |
var $elements = $(this); | |
var arg = arguments; | |
$elements | |
// Reduce to only elements that can be woven | |
.filter(SELECTOR_WEAVE) | |
// Iterate | |
.each(function elementIterator(index, element) { | |
// Defer weave | |
$DEFERRED(function deferredWeave(dfdWeave) { | |
var $element = $(element); | |
var $data = $element.data(); | |
var weave = $data[WEAVE] = $element.attr(DATA_WEAVE) || ""; | |
var woven = $data[WOVEN] || ($data[WOVEN] = []); | |
var pending = $data[PENDING] || ($data[PENDING] = []); | |
// Link deferred | |
dfdWeave.done(function doneWeave() { | |
$element | |
// Remove DATA_WEAVING | |
.removeAttr(DATA_WEAVING) | |
// Set DATA_WOVEN with full names | |
.attr(DATA_WOVEN, JOIN.call(arguments, " ")); | |
}); | |
// Wait for all pending deferred | |
$WHEN.apply($, pending).then(function donePending() { | |
var re = /[\s,]*([\w_\-\/\.]+)(?:\(([^\)]+)\))?/g; | |
var mark = i; | |
var j = 0; | |
var matches; | |
// Push dfdWeave on pending to signify we're starting a new task | |
PUSH.call(pending, dfdWeave); | |
$element | |
// Make sure to remove DATA_WEAVE (so we don't try processing this again) | |
.removeAttr(DATA_WEAVE) | |
// Set DATA_WEAVING (so that unweave can pick this up) | |
.attr(DATA_WEAVING, weave) | |
// Bind destroy event | |
.bind(DESTROY, onDestroy); | |
// Iterate woven (while RE_WEAVE matches) | |
while ((matches = re.exec(weave)) !== NULL) { | |
// Defer widget | |
$DEFERRED(function deferredWidget(dfdWidget) { | |
var _j = j++; // store _j before we increment | |
var k; | |
var l; | |
var kMax; | |
var value; | |
// Add to widgets | |
widgets[i++] = dfdWidget; | |
// Link deferred | |
dfdWidget.then(function doneWidget(widget) { | |
woven[_j] = widget; | |
}, dfdWeave.reject, dfdWeave.notify); | |
// Get widget name | |
var name = matches[1]; | |
// Set initial argv | |
var argv = [ $element, name ]; | |
// Append values from arg to argv | |
for (k = 0, kMax = arg.length, l = argv.length; k < kMax; k++, l++) { | |
argv[l] = arg[k]; | |
} | |
// Get widget args | |
var args = matches[2]; | |
// Any widget arguments | |
if (args !== UNDEFINED) { | |
// Convert args using getargs | |
args = getargs.call(args); | |
// Append typed values from args to argv | |
for (k = 0, kMax = args.length, l = argv.length; k < kMax; k++, l++) { | |
// Get value | |
value = args[k]; | |
// Get value from $data or fall back to pure value | |
argv[l] = value in $data | |
? $data[value] | |
: value; | |
} | |
} | |
// Require module | |
parentRequire([ name ], function required(Widget) { | |
// Instantiate widget (with argv) | |
var widget = Widget.apply(Widget, argv); | |
// Start widget | |
widget.start().then(function resolve() { | |
dfdWidget.resolve(widget); | |
}, dfdWidget.reject, dfdWidget.notify); | |
}); | |
}); | |
} | |
// Slice out widgets woven for this element | |
$WHEN.apply($, widgets.slice(mark, i)).then(dfdWeave.resolve, dfdWeave.reject, dfdWeave.notify); | |
}, dfdWeave.reject, dfdWeave.notify); | |
}); | |
}); | |
// Return compacted combined promise | |
return $DEFERRED(function deferredWeave(dfdWeave) { | |
$WHEN.apply($, widgets).then(function resolve() { | |
dfdWeave.resolve(arguments); | |
}, dfdWeave.reject, dfdWeave.progress); | |
}).promise(); | |
}; | |
$.fn[UNWEAVE] = function unweave() { | |
var widgets = []; | |
var i = 0; | |
var $elements = $(this); | |
$elements | |
// Reduce to only elements that can be unwoven | |
.filter(SELECTOR_UNWEAVE) | |
// Iterate | |
.each(function elementIterator(index, element) { | |
// Defer unweave | |
$DEFERRED(function deferredUnweave(dfdUnweave) { | |
var $element = $(element); | |
var $data = $element.data(); | |
var pending = $data[PENDING] || ($data[PENDING] = []); | |
var woven = $data[WOVEN] || []; | |
// Link deferred | |
dfdUnweave.done(function doneUnweave() { | |
$element | |
// Copy weave data to data-weave attribute | |
.attr(DATA_WEAVE, $data[WEAVE]) | |
// Make sure to clean the destroy event handler | |
.unbind(DESTROY, onDestroy); | |
// Remove data fore WEAVE | |
delete $data[WEAVE]; | |
}); | |
// Wait for all pending deferred | |
$WHEN.apply($, pending).done(function donePending() { | |
var mark = i; | |
var widget; | |
// Push dfdUnweave on pending to signify we're starting a new task | |
PUSH.call(pending, dfdUnweave); | |
// Remove WOVEN data | |
delete $data[WOVEN]; | |
$element | |
// Remove DATA_WOVEN attribute | |
.removeAttr(DATA_WOVEN); | |
// Somewhat safe(r) iterator over woven | |
while ((widget = woven.shift()) !== UNDEFINED) { | |
// Defer widget | |
$DEFERRED(function deferredWidget(dfdWidget) { | |
// Add to unwoven and pending | |
widgets[i++] = widget.stop().then(function resolve() { | |
dfdWidget.resolve(widget); | |
}, dfdWidget.reject, dfdWidget.notify); | |
}); | |
} | |
// Slice out widgets unwoven for this element | |
$WHEN.apply($, widgets.slice(mark, i)).then(dfdUnweave.resolve, dfdUnweave.reject, dfdUnweave.notify); | |
}); | |
}); | |
}); | |
// Return compacted combined promise | |
return $DEFERRED(function deferredUnweave(dfdUnweave) { | |
$WHEN.apply($, widgets).then(function resolve() { | |
dfdUnweave.resolve(arguments); | |
}, dfdUnweave.reject, dfdUnweave.progress); | |
}).promise(); | |
}; | |
$.fn[WOVEN] = function woven(/* arg, arg */) { | |
var result = []; | |
var widgets = arguments.length > 0 | |
? RegExp($.map(arguments, function (widget) { | |
return "^" + widget + "$"; | |
}).join("|"), "m") | |
: UNDEFINED; | |
$(this).each(function elementIterator(index, element) { | |
if (!$.hasData(element)) { | |
return; | |
} | |
PUSH.apply(result, widgets === UNDEFINED | |
? $.data(element, WOVEN) | |
: $.map($.data(element, WOVEN), function (woven) { | |
return widgets.test(woven.displayName) | |
? woven | |
: UNDEFINED; | |
})); | |
}); | |
return result; | |
}; | |
}); | |
/*! | |
* TroopJS jQuery dimensions plug-in | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-jquery/dimensions',[ "jquery" ], function DimensionsModule($) { | |
/*jshint strict:false, smarttabs:true */ | |
var NULL = null; | |
var DIMENSIONS = "dimensions"; | |
var RESIZE = "resize." + DIMENSIONS; | |
var W = "w"; | |
var H = "h"; | |
var _W = "_" + W; | |
var _H = "_" + H; | |
/** | |
* Internal comparator used for reverse sorting arrays | |
*/ | |
function reverse(a, b) { | |
return b - a; | |
} | |
/** | |
* Internal onResize handler | |
* @param $event | |
*/ | |
function onResize($event) { | |
var self = this; | |
var $self = $(self); | |
var width = $self.width(); | |
var height = $self.height(); | |
// Iterate all dimensions | |
$.each($.data(self, DIMENSIONS), function dimensionIterator(namespace, dimension) { | |
var w = dimension[W]; | |
var h = dimension[H]; | |
var _w; | |
var _h; | |
var i; | |
i = w.length; | |
_w = w[i - 1]; | |
while(w[--i] < width) { | |
_w = w[i]; | |
} | |
i = h.length; | |
_h = h[i - 1]; | |
while(h[--i] < height) { | |
_h = h[i]; | |
} | |
// If _w or _h has changed, update and trigger | |
if (_w !== dimension[_W] || _h !== dimension[_H]) { | |
dimension[_W] = _w; | |
dimension[_H] = _h; | |
$self.trigger(DIMENSIONS + "." + namespace, [ _w, _h ]); | |
} | |
}); | |
} | |
$.event.special[DIMENSIONS] = { | |
/** | |
* @param data (Anything) Whatever eventData (optional) was passed in | |
* when binding the event. | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
* @param eventHandle (Function) The actual function that will be bound | |
* to the browser’s native event (this is used internally for the | |
* beforeunload event, you’ll never use it). | |
*/ | |
setup : function onDimensionsSetup(data, namespaces, eventHandle) { | |
$(this) | |
.bind(RESIZE, onResize) | |
.data(DIMENSIONS, {}); | |
}, | |
/** | |
* Do something each time an event handler is bound to a particular element | |
* @param handleObj (Object) | |
*/ | |
add : function onDimensionsAdd(handleObj) { | |
var self = this; | |
var namespace = handleObj.namespace; | |
var dimension = {}; | |
var w = dimension[W] = []; | |
var h = dimension[H] = []; | |
var re = /(w|h)(\d+)/g; | |
var matches; | |
while ((matches = re.exec(namespace)) !== NULL) { | |
dimension[matches[1]].push(parseInt(matches[2], 10)); | |
} | |
w.sort(reverse); | |
h.sort(reverse); | |
$.data(self, DIMENSIONS)[namespace] = dimension; | |
}, | |
/** | |
* Do something each time an event handler is unbound from a particular element | |
* @param handleObj (Object) | |
*/ | |
remove : function onDimensionsRemove(handleObj) { | |
delete $.data(this, DIMENSIONS)[handleObj.namespace]; | |
}, | |
/** | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
*/ | |
teardown : function onDimensionsTeardown(namespaces) { | |
$(this) | |
.removeData(DIMENSIONS) | |
.unbind(RESIZE, onResize); | |
} | |
}; | |
}); | |
/*! | |
* TroopJS jQuery resize plug-in | |
* | |
* Heavy inspiration from https://github.com/cowboy/jquery-resize.git | |
* | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-jquery/resize',[ "jquery" ], function ResizeModule($) { | |
/*jshint strict:false, smarttabs:true */ | |
var NULL = null; | |
var RESIZE = "resize"; | |
var W = "w"; | |
var H = "h"; | |
var $ELEMENTS = $([]); | |
var INTERVAL = NULL; | |
/** | |
* Iterator | |
* @param index | |
* @param self | |
*/ | |
function iterator(index, self) { | |
// Get data | |
var $data = $.data(self); | |
// Get reference to $self | |
var $self = $(self); | |
// Get previous width and height | |
var w = $self.width(); | |
var h = $self.height(); | |
// Check if width or height has changed since last check | |
if (w !== $data[W] || h !== $data[H]) { | |
$self.trigger(RESIZE, [$data[W] = w, $data[H] = h]); | |
} | |
} | |
/** | |
* Internal interval | |
*/ | |
function interval() { | |
$ELEMENTS.each(iterator); | |
} | |
$.event.special[RESIZE] = { | |
/** | |
* @param data (Anything) Whatever eventData (optional) was passed in | |
* when binding the event. | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
* @param eventHandle (Function) The actual function that will be bound | |
* to the browser’s native event (this is used internally for the | |
* beforeunload event, you’ll never use it). | |
*/ | |
setup : function onResizeSetup(data, namespaces, eventHandle) { | |
var self = this; | |
// window has a native resize event, exit fast | |
if ($.isWindow(self)) { | |
return false; | |
} | |
// Store data | |
var $data = $.data(self, RESIZE, {}); | |
// Get reference to $self | |
var $self = $(self); | |
// Initialize data | |
$data[W] = $self.width(); | |
$data[H] = $self.height(); | |
// Add to tracked collection | |
$ELEMENTS = $ELEMENTS.add(self); | |
// If this is the first element, start interval | |
if($ELEMENTS.length === 1) { | |
INTERVAL = setInterval(interval, 100); | |
} | |
}, | |
/** | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
*/ | |
teardown : function onResizeTeardown(namespaces) { | |
var self = this; | |
// window has a native resize event, exit fast | |
if ($.isWindow(self)) { | |
return false; | |
} | |
// Remove data | |
$.removeData(self, RESIZE); | |
// Remove from tracked collection | |
$ELEMENTS = $ELEMENTS.not(self); | |
// If this is the last element, stop interval | |
if($ELEMENTS.length === 0 && INTERVAL !== NULL) { | |
clearInterval(INTERVAL); | |
} | |
} | |
}; | |
}); | |
/*! | |
* TroopJS Utils merge module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-utils/merge',[],function MergeModule() { | |
/*jshint strict:false */ | |
var ARRAY = Array; | |
var OBJECT = Object; | |
return function merge(source) { | |
var target = this; | |
var key = null; | |
var i; | |
var iMax; | |
var value; | |
var constructor; | |
for (i = 0, iMax = arguments.length; i < iMax; i++) { | |
source = arguments[i]; | |
for (key in source) { | |
value = source[key]; | |
constructor = value.constructor; | |
if (!(key in target)) { | |
target[key] = value; | |
} | |
else if (constructor === ARRAY) { | |
target[key] = target[key].concat(value); | |
} | |
else if (constructor === OBJECT) { | |
merge.call(target[key], value); | |
} | |
else { | |
target[key] = value; | |
} | |
} | |
} | |
return target; | |
}; | |
}); | |
/*! | |
* TroopJS Utils tr component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-utils/tr',[],function TrModule() { | |
/*jshint strict:false */ | |
var TYPEOF_NUMBER = typeof Number(); | |
return function tr(callback) { | |
var self = this; | |
var result = []; | |
var i; | |
var length = self.length; | |
var key; | |
// Is this an array? Basically, is length a number, is it 0 or is it greater than 0 and that we have index 0 and index length-1 | |
if (typeof length === TYPEOF_NUMBER && length === 0 || length > 0 && 0 in self && length - 1 in self) { | |
for (i = 0; i < length; i++) { | |
result.push(callback.call(self, self[i], i)); | |
} | |
// Otherwise we'll iterate it as an object | |
} else if (self){ | |
for (key in self) { | |
result.push(callback.call(self, self[key], key)); | |
} | |
} | |
return result; | |
}; | |
}); | |
/* | |
* ComposeJS, object composition for JavaScript, featuring | |
* JavaScript-style prototype inheritance and composition, multiple inheritance, | |
* mixin and traits-inspired conflict resolution and composition | |
*/ | |
(function(define){ | |
define('compose/compose',[], function(){ | |
// function for creating instances from a prototype | |
function Create(){ | |
} | |
var delegate = Object.create ? | |
function(proto){ | |
return Object.create(typeof proto == "function" ? proto.prototype : proto || Object.prototype); | |
} : | |
function(proto){ | |
Create.prototype = typeof proto == "function" ? proto.prototype : proto; | |
var instance = new Create(); | |
Create.prototype = null; | |
return instance; | |
}; | |
function validArg(arg){ | |
if(!arg){ | |
throw new Error("Compose arguments must be functions or objects"); | |
} | |
return arg; | |
} | |
// this does the work of combining mixins/prototypes | |
function mixin(instance, args, i){ | |
// use prototype inheritance for first arg | |
var value, argsLength = args.length; | |
for(; i < argsLength; i++){ | |
var arg = args[i]; | |
if(typeof arg == "function"){ | |
// the arg is a function, use the prototype for the properties | |
var prototype = arg.prototype; | |
for(var key in prototype){ | |
value = prototype[key]; | |
var own = prototype.hasOwnProperty(key); | |
if(typeof value == "function" && key in instance && value !== instance[key]){ | |
var existing = instance[key]; | |
if(value == required){ | |
// it is a required value, and we have satisfied it | |
value = existing; | |
} | |
else if(!own){ | |
// if it is own property, it is considered an explicit override | |
// TODO: make faster calls on this, perhaps passing indices and caching | |
if(isInMethodChain(value, key, getBases([].slice.call(args, 0, i), true))){ | |
// this value is in the existing method's override chain, we can use the existing method | |
value = existing; | |
}else if(!isInMethodChain(existing, key, getBases([arg], true))){ | |
// the existing method is not in the current override chain, so we are left with a conflict | |
console.error("Conflicted method " + key + ", final composer must explicitly override with correct method."); | |
} | |
} | |
} | |
if(value && value.install && own && !isInMethodChain(existing, key, getBases([arg], true))){ | |
// apply modifier | |
value.install.call(instance, key); | |
}else{ | |
instance[key] = value; | |
} | |
} | |
}else{ | |
// it is an object, copy properties, looking for modifiers | |
for(var key in validArg(arg)){ | |
var value = arg[key]; | |
if(typeof value == "function"){ | |
if(value.install){ | |
// apply modifier | |
value.install.call(instance, key); | |
continue; | |
} | |
if(key in instance){ | |
if(value == required){ | |
// required requirement met | |
continue; | |
} | |
} | |
} | |
// add it to the instance | |
instance[key] = value; | |
} | |
} | |
} | |
return instance; | |
} | |
// allow for override (by es5 module) | |
Compose._setMixin = function(newMixin){ | |
mixin = newMixin; | |
}; | |
function isInMethodChain(method, name, prototypes){ | |
// searches for a method in the given prototype hierarchy | |
for(var i = 0; i < prototypes.length;i++){ | |
var prototype = prototypes[i]; | |
if(prototype[name] == method){ | |
// found it | |
return true; | |
} | |
} | |
} | |
// Decorator branding | |
function Decorator(install, direct){ | |
function Decorator(){ | |
if(direct){ | |
return direct.apply(this, arguments); | |
} | |
throw new Error("Decorator not applied"); | |
} | |
Decorator.install = install; | |
return Decorator; | |
} | |
Compose.Decorator = Decorator; | |
// aspect applier | |
function aspect(handler){ | |
return function(advice){ | |
return Decorator(function install(key){ | |
var baseMethod = this[key]; | |
(advice = this[key] = baseMethod ? handler(this, baseMethod, advice) : advice).install = install; | |
}, advice); | |
}; | |
}; | |
// around advice, useful for calling super methods too | |
Compose.around = aspect(function(target, base, advice){ | |
return advice.call(target, base); | |
}); | |
Compose.before = aspect(function(target, base, advice){ | |
return function(){ | |
var results = advice.apply(this, arguments); | |
if(results !== stop){ | |
return base.apply(this, results || arguments); | |
} | |
}; | |
}); | |
var stop = Compose.stop = {}; | |
var undefined; | |
Compose.after = aspect(function(target, base, advice){ | |
return function(){ | |
var results = base.apply(this, arguments); | |
var adviceResults = advice.apply(this, arguments); | |
return adviceResults === undefined ? results : adviceResults; | |
}; | |
}); | |
// rename Decorator for calling super methods | |
Compose.from = function(trait, fromKey){ | |
if(fromKey){ | |
return (typeof trait == "function" ? trait.prototype : trait)[fromKey]; | |
} | |
return Decorator(function(key){ | |
if(!(this[key] = (typeof trait == "string" ? this[trait] : | |
(typeof trait == "function" ? trait.prototype : trait)[fromKey || key]))){ | |
throw new Error("Source method " + fromKey + " was not available to be renamed to " + key); | |
} | |
}); | |
}; | |
// Composes an instance | |
Compose.create = function(base){ | |
// create the instance | |
var instance = mixin(delegate(base), arguments, 1); | |
var argsLength = arguments.length; | |
// for go through the arguments and call the constructors (with no args) | |
for(var i = 0; i < argsLength; i++){ | |
var arg = arguments[i]; | |
if(typeof arg == "function"){ | |
instance = arg.call(instance) || instance; | |
} | |
} | |
return instance; | |
} | |
// The required function, just throws an error if not overriden | |
function required(){ | |
throw new Error("This method is required and no implementation has been provided"); | |
}; | |
Compose.required = required; | |
// get the value of |this| for direct function calls for this mode (strict in ES5) | |
function extend(){ | |
var args = [this]; | |
args.push.apply(args, arguments); | |
return Compose.apply(0, args); | |
} | |
// Compose a constructor | |
function Compose(base){ | |
var args = arguments; | |
var prototype = (args.length < 2 && typeof args[0] != "function") ? | |
args[0] : // if there is just a single argument object, just use that as the prototype | |
mixin(delegate(validArg(base)), args, 1); // normally create a delegate to start with | |
function Constructor(){ | |
var instance; | |
if(this instanceof Constructor){ | |
// called with new operator, can proceed as is | |
instance = this; | |
}else{ | |
// we allow for direct calls without a new operator, in this case we need to | |
// create the instance ourself. | |
Create.prototype = prototype; | |
instance = new Create(); | |
} | |
// call all the constructors with the given arguments | |
for(var i = 0; i < constructorsLength; i++){ | |
var constructor = constructors[i]; | |
var result = constructor.apply(instance, arguments); | |
if(typeof result == "object"){ | |
if(result instanceof Constructor){ | |
instance = result; | |
}else{ | |
for(var j in result){ | |
if(result.hasOwnProperty(j)){ | |
instance[j] = result[j]; | |
} | |
} | |
} | |
} | |
} | |
return instance; | |
} | |
// create a function that can retrieve the bases (constructors or prototypes) | |
Constructor._getBases = function(prototype){ | |
return prototype ? prototypes : constructors; | |
}; | |
// now get the prototypes and the constructors | |
var constructors = getBases(args), | |
constructorsLength = constructors.length; | |
if(typeof args[args.length - 1] == "object"){ | |
args[args.length - 1] = prototype; | |
} | |
var prototypes = getBases(args, true); | |
Constructor.extend = extend; | |
if(!Compose.secure){ | |
prototype.constructor = Constructor; | |
} | |
Constructor.prototype = prototype; | |
return Constructor; | |
}; | |
Compose.apply = function(thisObject, args){ | |
// apply to the target | |
return thisObject ? | |
mixin(thisObject, args, 0) : // called with a target object, apply the supplied arguments as mixins to the target object | |
extend.apply.call(Compose, 0, args); // get the Function.prototype apply function, call() it to apply arguments to Compose (the extend doesn't matter, just a handle way to grab apply, since we can't get it off of Compose) | |
}; | |
Compose.call = function(thisObject){ | |
// call() should correspond with apply behavior | |
return mixin(thisObject, arguments, 1); | |
}; | |
function getBases(args, prototype){ | |
// this function registers a set of constructors for a class, eliminating duplicate | |
// constructors that may result from diamond construction for classes (B->A, C->A, D->B&C, then D() should only call A() once) | |
var bases = []; | |
function iterate(args, checkChildren){ | |
outer: | |
for(var i = 0; i < args.length; i++){ | |
var arg = args[i]; | |
var target = prototype && typeof arg == "function" ? | |
arg.prototype : arg; | |
if(prototype || typeof arg == "function"){ | |
var argGetBases = checkChildren && arg._getBases; | |
if(argGetBases){ | |
iterate(argGetBases(prototype)); // don't need to check children for these, this should be pre-flattened | |
}else{ | |
for(var j = 0; j < bases.length; j++){ | |
if(target == bases[j]){ | |
continue outer; | |
} | |
} | |
bases.push(target); | |
} | |
} | |
} | |
} | |
iterate(args, true); | |
return bases; | |
} | |
// returning the export of the module | |
return Compose; | |
}); | |
})(typeof define != "undefined" ? | |
define: // AMD/RequireJS format if available | |
function(deps, factory){ | |
if(typeof module !="undefined"){ | |
module.exports = factory(); // CommonJS environment, like NodeJS | |
// require("./configure"); | |
}else{ | |
Compose = factory(); // raw script, assign to Compose global | |
} | |
}); | |
define('compose', ['compose/compose'], function (main) { return main; }); | |
/*! | |
* TroopJS Utils URI module | |
* | |
* parts of code from parseUri 1.2.2 Copyright Steven Levithan <stevenlevithan.com> | |
* | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-utils/uri',[ "compose" ], function URIModule(Compose) { | |
/*jshint strict:false, smarttabs:true, laxbreak:true, newcap:false, forin:false, loopfunc:true */ | |
var NULL = null; | |
var ARRAY_PROTO = Array.prototype; | |
var OBJECT_PROTO = Object.prototype; | |
var PUSH = ARRAY_PROTO.push; | |
var SPLIT = String.prototype.split; | |
var TOSTRING = OBJECT_PROTO.toString; | |
var TOSTRING_OBJECT = TOSTRING.call(OBJECT_PROTO); | |
var TOSTRING_ARRAY = TOSTRING.call(ARRAY_PROTO); | |
var TOSTRING_FUNCTION = TOSTRING.call(Function.prototype); | |
var RE_URI = /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?(?:([^?#]*)(?:\?([^#]*))?(?:#(.*))?)/; | |
var PROTOCOL = "protocol"; | |
var AUTHORITY = "authority"; | |
var PATH = "path"; | |
var QUERY = "query"; | |
var ANCHOR = "anchor"; | |
var KEYS = [ "source", | |
PROTOCOL, | |
AUTHORITY, | |
"userInfo", | |
"user", | |
"password", | |
"host", | |
"port", | |
PATH, | |
QUERY, | |
ANCHOR ]; | |
// Store current Compose.secure setting | |
var SECURE = Compose.secure; | |
// Prevent Compose from creating constructor property | |
Compose.secure = true; | |
function Query(arg) { | |
var result = {}; | |
var matches; | |
var key = NULL; | |
var value; | |
var re = /(?:&|^)([^&=]*)=?([^&]*)/g; | |
result.toString = Query.toString; | |
if (TOSTRING.call(arg) === TOSTRING_OBJECT) { | |
for (key in arg) { | |
result[key] = arg[key]; | |
} | |
} else { | |
while ((matches = re.exec(arg)) !== NULL) { | |
key = matches[1]; | |
if (key in result) { | |
value = result[key]; | |
if (TOSTRING.call(value) === TOSTRING_ARRAY) { | |
value[value.length] = matches[2]; | |
} | |
else { | |
result[key] = [ value, matches[2] ]; | |
} | |
} | |
else { | |
result[key] = matches[2]; | |
} | |
} | |
} | |
return result; | |
} | |
Query.toString = function toString() { | |
var self = this; | |
var key; | |
var value; | |
var values; | |
var query = []; | |
var i = 0; | |
var j; | |
for (key in self) { | |
if (TOSTRING.call(self[key]) === TOSTRING_FUNCTION) { | |
continue; | |
} | |
query[i++] = key; | |
} | |
query.sort(); | |
while (i--) { | |
key = query[i]; | |
value = self[key]; | |
if (TOSTRING.call(value) === TOSTRING_ARRAY) { | |
values = value.slice(0); | |
values.sort(); | |
j = values.length; | |
while (j--) { | |
value = values[j]; | |
values[j] = value === "" | |
? key | |
: key + "=" + value; | |
} | |
query[i] = values.join("&"); | |
} | |
else { | |
query[i] = value === "" | |
? key | |
: key + "=" + value; | |
} | |
} | |
return query.join("&"); | |
}; | |
// Extend on the instance of array rather than subclass it | |
function Path(arg) { | |
var result = []; | |
result.toString = Path.toString; | |
PUSH.apply(result, TOSTRING.call(arg) === TOSTRING_ARRAY | |
? arg | |
: SPLIT.call(arg, "/")); | |
return result; | |
} | |
Path.toString = function() { | |
return this.join("/"); | |
}; | |
var URI = Compose(function URI(str) { | |
var self = this; | |
var value; | |
var matches; | |
var i; | |
if ((matches = RE_URI.exec(str)) !== NULL) { | |
i = matches.length; | |
while (i--) { | |
value = matches[i]; | |
if (value) { | |
self[KEYS[i]] = value; | |
} | |
} | |
} | |
if (QUERY in self) { | |
self[QUERY] = Query(self[QUERY]); | |
} | |
if (PATH in self) { | |
self[PATH] = Path(self[PATH]); | |
} | |
}); | |
URI.prototype.toString = function () { | |
var self = this; | |
var uri = [ PROTOCOL , "://", AUTHORITY, PATH, "?", QUERY, "#", ANCHOR ]; | |
var i; | |
var key; | |
if (!(PROTOCOL in self)) { | |
uri[0] = uri[1] = ""; | |
} | |
if (!(AUTHORITY in self)) { | |
uri[2] = ""; | |
} | |
if (!(PATH in self)) { | |
uri[3] = ""; | |
} | |
if (!(QUERY in self)) { | |
uri[4] = uri[5] = ""; | |
} | |
if (!(ANCHOR in self)) { | |
uri[6] = uri[7] = ""; | |
} | |
i = uri.length; | |
while (i--) { | |
key = uri[i]; | |
if (key in self) { | |
uri[i] = self[key]; | |
} | |
} | |
return uri.join(""); | |
}; | |
// Restore Compose.secure setting | |
Compose.secure = SECURE; | |
URI.Path = Path; | |
URI.Query = Query; | |
return URI; | |
}); | |
/*! | |
* TroopJS Utils unique component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-utils/unique',[],function UniqueModule() { | |
/*jshint strict:false */ | |
return function unique(callback) { | |
var self = this; | |
var length = self.length; | |
var result = []; | |
var value; | |
var i; | |
var j; | |
var k; | |
add: for (i = j = k = 0; i < length; i++, j = 0) { | |
value = self[i]; | |
while(j < k) { | |
if (callback.call(self, value, result[j++]) === true) { | |
continue add; | |
} | |
} | |
result[k++] = value; | |
} | |
return result; | |
}; | |
}); | |
/** @license MIT License (c) copyright B Cavalier & J Hann */ | |
/** | |
* A lightweight CommonJS Promises/A and when() implementation | |
* when 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 | |
* | |
* @version 1.7.1 | |
*/ | |
(function(define) { | |
define('when/when',[],function () { | |
var reduceArray, slice, undef; | |
// | |
// Public API | |
// | |
when.defer = defer; // Create a deferred | |
when.resolve = resolve; // Create a resolved promise | |
when.reject = reject; // Create a rejected promise | |
when.join = join; // Join 2 or more promises | |
when.all = all; // Resolve a list of promises | |
when.map = map; // Array.map() for promises | |
when.reduce = reduce; // Array.reduce() for promises | |
when.any = any; // One-winner race | |
when.some = some; // Multi-winner race | |
when.chain = chain; // Make a promise trigger another resolver | |
when.isPromise = isPromise; // Determine if a thing is a promise | |
/** | |
* Register an observer for a promise or immediate value. | |
* | |
* @param {*} promiseOrValue | |
* @param {function?} [onFulfilled] callback to be called when promiseOrValue is | |
* successfully fulfilled. If promiseOrValue is an immediate value, callback | |
* will be invoked immediately. | |
* @param {function?} [onRejected] callback to be called when promiseOrValue is | |
* rejected. | |
* @param {function?} [onProgress] callback to be called when progress updates | |
* are issued for promiseOrValue. | |
* @returns {Promise} a new {@link Promise} that will complete with the return | |
* value of callback or errback or the completion value of promiseOrValue if | |
* callback and/or errback is not supplied. | |
*/ | |
function when(promiseOrValue, onFulfilled, onRejected, onProgress) { | |
// Get a trusted promise for the input promiseOrValue, and then | |
// register promise handlers | |
return resolve(promiseOrValue).then(onFulfilled, onRejected, onProgress); | |
} | |
/** | |
* Returns promiseOrValue if promiseOrValue is a {@link Promise}, a new Promise if | |
* promiseOrValue is a foreign promise, or a new, already-fulfilled {@link Promise} | |
* whose value is promiseOrValue if promiseOrValue is an immediate value. | |
* | |
* @param {*} promiseOrValue | |
* @returns Guaranteed to return a trusted Promise. If promiseOrValue is a when.js {@link Promise} | |
* returns promiseOrValue, otherwise, returns a new, already-resolved, when.js {@link Promise} | |
* whose resolution value is: | |
* * the resolution value of promiseOrValue if it's a foreign promise, or | |
* * promiseOrValue if it's a value | |
*/ | |
function resolve(promiseOrValue) { | |
var promise, deferred; | |
if(promiseOrValue instanceof Promise) { | |
// It's a when.js promise, so we trust it | |
promise = promiseOrValue; | |
} else { | |
// It's not a when.js promise. See if it's a foreign promise or a value. | |
if(isPromise(promiseOrValue)) { | |
// It's a thenable, but we don't know where it came from, so don't trust | |
// its implementation entirely. Introduce a trusted middleman when.js promise | |
deferred = defer(); | |
// IMPORTANT: This is the only place when.js should ever call .then() on an | |
// untrusted promise. Don't expose the return value to the untrusted promise | |
promiseOrValue.then( | |
function(value) { deferred.resolve(value); }, | |
function(reason) { deferred.reject(reason); }, | |
function(update) { deferred.progress(update); } | |
); | |
promise = deferred.promise; | |
} else { | |
// It's a value, not a promise. Create a resolved promise for it. | |
promise = fulfilled(promiseOrValue); | |
} | |
} | |
return promise; | |
} | |
/** | |
* Returns a rejected promise for the supplied promiseOrValue. The returned | |
* promise will be rejected with: | |
* - promiseOrValue, if it is a value, or | |
* - if promiseOrValue is a promise | |
* - promiseOrValue's value after it is fulfilled | |
* - promiseOrValue's reason after it is rejected | |
* @param {*} promiseOrValue the rejected value of the returned {@link Promise} | |
* @return {Promise} rejected {@link Promise} | |
*/ | |
function reject(promiseOrValue) { | |
return when(promiseOrValue, rejected); | |
} | |
/** | |
* Trusted Promise constructor. A Promise created from this constructor is | |
* a trusted when.js promise. Any other duck-typed promise is considered | |
* untrusted. | |
* @constructor | |
* @name Promise | |
*/ | |
function Promise(then) { | |
this.then = then; | |
} | |
Promise.prototype = { | |
/** | |
* Register a callback that will be called when a promise is | |
* fulfilled or rejected. Optionally also register a progress handler. | |
* Shortcut for .then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress) | |
* @param {function?} [onFulfilledOrRejected] | |
* @param {function?} [onProgress] | |
* @return {Promise} | |
*/ | |
always: function(onFulfilledOrRejected, onProgress) { | |
return this.then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress); | |
}, | |
/** | |
* Register a rejection handler. Shortcut for .then(undefined, onRejected) | |
* @param {function?} onRejected | |
* @return {Promise} | |
*/ | |
otherwise: function(onRejected) { | |
return this.then(undef, onRejected); | |
}, | |
/** | |
* Shortcut for .then(function() { return value; }) | |
* @param {*} value | |
* @return {Promise} a promise that: | |
* - is fulfilled if value is not a promise, or | |
* - if value is a promise, will fulfill with its value, or reject | |
* with its reason. | |
*/ | |
yield: function(value) { | |
return this.then(function() { | |
return value; | |
}); | |
}, | |
/** | |
* Assumes that this promise will fulfill with an array, and arranges | |
* for the onFulfilled to be called with the array as its argument list | |
* i.e. onFulfilled.spread(undefined, array). | |
* @param {function} onFulfilled function to receive spread arguments | |
* @return {Promise} | |
*/ | |
spread: function(onFulfilled) { | |
return this.then(function(array) { | |
// array may contain promises, so resolve its contents. | |
return all(array, function(array) { | |
return onFulfilled.apply(undef, array); | |
}); | |
}); | |
} | |
}; | |
/** | |
* Create an already-resolved promise for the supplied value | |
* @private | |
* | |
* @param {*} value | |
* @return {Promise} fulfilled promise | |
*/ | |
function fulfilled(value) { | |
var p = new Promise(function(onFulfilled) { | |
// TODO: Promises/A+ check typeof onFulfilled | |
try { | |
return resolve(onFulfilled ? onFulfilled(value) : value); | |
} catch(e) { | |
return rejected(e); | |
} | |
}); | |
return p; | |
} | |
/** | |
* Create an already-rejected {@link Promise} with the supplied | |
* rejection reason. | |
* @private | |
* | |
* @param {*} reason | |
* @return {Promise} rejected promise | |
*/ | |
function rejected(reason) { | |
var p = new Promise(function(_, onRejected) { | |
// TODO: Promises/A+ check typeof onRejected | |
try { | |
return onRejected ? resolve(onRejected(reason)) : rejected(reason); | |
} catch(e) { | |
return rejected(e); | |
} | |
}); | |
return p; | |
} | |
/** | |
* Creates a new, Deferred with fully isolated resolver and promise parts, | |
* either or both of which may be given out safely to consumers. | |
* The Deferred itself has the full API: resolve, reject, progress, and | |
* then. The resolver has resolve, reject, and progress. The promise | |
* only has then. | |
* | |
* @return {Deferred} | |
*/ | |
function defer() { | |
var deferred, promise, handlers, progressHandlers, | |
_then, _progress, _resolve; | |
/** | |
* The promise for the new deferred | |
* @type {Promise} | |
*/ | |
promise = new Promise(then); | |
/** | |
* The full Deferred object, with {@link Promise} and {@link Resolver} parts | |
* @class Deferred | |
* @name Deferred | |
*/ | |
deferred = { | |
then: then, // DEPRECATED: use deferred.promise.then | |
resolve: promiseResolve, | |
reject: promiseReject, | |
// TODO: Consider renaming progress() to notify() | |
progress: promiseProgress, | |
promise: promise, | |
resolver: { | |
resolve: promiseResolve, | |
reject: promiseReject, | |
progress: promiseProgress | |
} | |
}; | |
handlers = []; | |
progressHandlers = []; | |
/** | |
* Pre-resolution then() that adds the supplied callback, errback, and progback | |
* functions to the registered listeners | |
* @private | |
* | |
* @param {function?} [onFulfilled] resolution handler | |
* @param {function?} [onRejected] rejection handler | |
* @param {function?} [onProgress] progress handler | |
*/ | |
_then = function(onFulfilled, onRejected, onProgress) { | |
// TODO: Promises/A+ check typeof onFulfilled, onRejected, onProgress | |
var deferred, progressHandler; | |
deferred = defer(); | |
progressHandler = typeof onProgress === 'function' | |
? function(update) { | |
try { | |
// Allow progress handler to transform progress event | |
deferred.progress(onProgress(update)); | |
} catch(e) { | |
// Use caught value as progress | |
deferred.progress(e); | |
} | |
} | |
: function(update) { deferred.progress(update); }; | |
handlers.push(function(promise) { | |
promise.then(onFulfilled, onRejected) | |
.then(deferred.resolve, deferred.reject, progressHandler); | |
}); | |
progressHandlers.push(progressHandler); | |
return deferred.promise; | |
}; | |
/** | |
* Issue a progress event, notifying all progress listeners | |
* @private | |
* @param {*} update progress event payload to pass to all listeners | |
*/ | |
_progress = function(update) { | |
processQueue(progressHandlers, update); | |
return update; | |
}; | |
/** | |
* Transition from pre-resolution state to post-resolution state, notifying | |
* all listeners of the resolution or rejection | |
* @private | |
* @param {*} value the value of this deferred | |
*/ | |
_resolve = function(value) { | |
value = resolve(value); | |
// Replace _then with one that directly notifies with the result. | |
_then = value.then; | |
// Replace _resolve so that this Deferred can only be resolved once | |
_resolve = resolve; | |
// Make _progress a noop, to disallow progress for the resolved promise. | |
_progress = noop; | |
// Notify handlers | |
processQueue(handlers, value); | |
// Free progressHandlers array since we'll never issue progress events | |
progressHandlers = handlers = undef; | |
return value; | |
}; | |
return deferred; | |
/** | |
* Wrapper to allow _then to be replaced safely | |
* @param {function?} [onFulfilled] resolution handler | |
* @param {function?} [onRejected] rejection handler | |
* @param {function?} [onProgress] progress handler | |
* @return {Promise} new promise | |
*/ | |
function then(onFulfilled, onRejected, onProgress) { | |
// TODO: Promises/A+ check typeof onFulfilled, onRejected, onProgress | |
return _then(onFulfilled, onRejected, onProgress); | |
} | |
/** | |
* Wrapper to allow _resolve to be replaced | |
*/ | |
function promiseResolve(val) { | |
return _resolve(val); | |
} | |
/** | |
* Wrapper to allow _reject to be replaced | |
*/ | |
function promiseReject(err) { | |
return _resolve(rejected(err)); | |
} | |
/** | |
* Wrapper to allow _progress to be replaced | |
*/ | |
function promiseProgress(update) { | |
return _progress(update); | |
} | |
} | |
/** | |
* Determines if promiseOrValue is a promise or not. Uses the feature | |
* test from http://wiki.commonjs.org/wiki/Promises/A to determine if | |
* promiseOrValue is a promise. | |
* | |
* @param {*} promiseOrValue anything | |
* @returns {boolean} true if promiseOrValue is a {@link Promise} | |
*/ | |
function isPromise(promiseOrValue) { | |
return promiseOrValue && typeof promiseOrValue.then === 'function'; | |
} | |
/** | |
* Initiates a competitive race, returning a promise that will resolve when | |
* howMany of the supplied promisesOrValues have resolved, or will reject when | |
* it becomes impossible for howMany to resolve, for example, when | |
* (promisesOrValues.length - howMany) + 1 input promises reject. | |
* | |
* @param {Array} promisesOrValues array of anything, may contain a mix | |
* of promises and values | |
* @param howMany {number} number of promisesOrValues to resolve | |
* @param {function?} [onFulfilled] resolution handler | |
* @param {function?} [onRejected] rejection handler | |
* @param {function?} [onProgress] progress handler | |
* @returns {Promise} promise that will resolve to an array of howMany values that | |
* resolved first, or will reject with an array of (promisesOrValues.length - howMany) + 1 | |
* rejection reasons. | |
*/ | |
function some(promisesOrValues, howMany, onFulfilled, onRejected, onProgress) { | |
checkCallbacks(2, arguments); | |
return when(promisesOrValues, function(promisesOrValues) { | |
var toResolve, toReject, values, reasons, deferred, fulfillOne, rejectOne, progress, len, i; | |
len = promisesOrValues.length >>> 0; | |
toResolve = Math.max(0, Math.min(howMany, len)); | |
values = []; | |
toReject = (len - toResolve) + 1; | |
reasons = []; | |
deferred = defer(); | |
// No items in the input, resolve immediately | |
if (!toResolve) { | |
deferred.resolve(values); | |
} else { | |
progress = deferred.progress; | |
rejectOne = function(reason) { | |
reasons.push(reason); | |
if(!--toReject) { | |
fulfillOne = rejectOne = noop; | |
deferred.reject(reasons); | |
} | |
}; | |
fulfillOne = function(val) { | |
// This orders the values based on promise resolution order | |
// Another strategy would be to use the original position of | |
// the corresponding promise. | |
values.push(val); | |
if (!--toResolve) { | |
fulfillOne = rejectOne = noop; | |
deferred.resolve(values); | |
} | |
}; | |
for(i = 0; i < len; ++i) { | |
if(i in promisesOrValues) { | |
when(promisesOrValues[i], fulfiller, rejecter, progress); | |
} | |
} | |
} | |
return deferred.then(onFulfilled, onRejected, onProgress); | |
function rejecter(reason) { | |
rejectOne(reason); | |
} | |
function fulfiller(val) { | |
fulfillOne(val); | |
} | |
}); | |
} | |
/** | |
* Initiates a competitive race, returning a promise that will resolve when | |
* any one of the supplied promisesOrValues has resolved or will reject when | |
* *all* promisesOrValues have rejected. | |
* | |
* @param {Array|Promise} promisesOrValues array of anything, may contain a mix | |
* of {@link Promise}s and values | |
* @param {function?} [onFulfilled] resolution handler | |
* @param {function?} [onRejected] rejection handler | |
* @param {function?} [onProgress] progress handler | |
* @returns {Promise} promise that will resolve to the value that resolved first, or | |
* will reject with an array of all rejected inputs. | |
*/ | |
function any(promisesOrValues, onFulfilled, onRejected, onProgress) { | |
function unwrapSingleResult(val) { | |
return onFulfilled ? onFulfilled(val[0]) : val[0]; | |
} | |
return some(promisesOrValues, 1, unwrapSingleResult, onRejected, onProgress); | |
} | |
/** | |
* Return a promise that will resolve only once all the supplied promisesOrValues | |
* have resolved. The resolution value of the returned promise will be an array | |
* containing the resolution values of each of the promisesOrValues. | |
* @memberOf when | |
* | |
* @param {Array|Promise} promisesOrValues array of anything, may contain a mix | |
* of {@link Promise}s and values | |
* @param {function?} [onFulfilled] resolution handler | |
* @param {function?} [onRejected] rejection handler | |
* @param {function?} [onProgress] progress handler | |
* @returns {Promise} | |
*/ | |
function all(promisesOrValues, onFulfilled, onRejected, onProgress) { | |
checkCallbacks(1, arguments); | |
return map(promisesOrValues, identity).then(onFulfilled, onRejected, onProgress); | |
} | |
/** | |
* Joins multiple promises into a single returned promise. | |
* @return {Promise} a promise that will fulfill when *all* the input promises | |
* have fulfilled, or will reject when *any one* of the input promises rejects. | |
*/ | |
function join(/* ...promises */) { | |
return map(arguments, identity); | |
} | |
/** | |
* Traditional map function, similar to `Array.prototype.map()`, but allows | |
* input to contain {@link Promise}s and/or values, and mapFunc may return | |
* either a value or a {@link Promise} | |
* | |
* @param {Array|Promise} promise array of anything, may contain a mix | |
* of {@link Promise}s and values | |
* @param {function} mapFunc mapping function mapFunc(value) which may return | |
* either a {@link Promise} or value | |
* @returns {Promise} a {@link Promise} that will resolve to an array containing | |
* the mapped output values. | |
*/ | |
function map(promise, mapFunc) { | |
return when(promise, function(array) { | |
var results, len, toResolve, resolve, i, d; | |
// Since we know the resulting length, we can preallocate the results | |
// array to avoid array expansions. | |
toResolve = len = array.length >>> 0; | |
results = []; | |
d = defer(); | |
if(!toResolve) { | |
d.resolve(results); | |
} else { | |
resolve = function resolveOne(item, i) { | |
when(item, mapFunc).then(function(mapped) { | |
results[i] = mapped; | |
if(!--toResolve) { | |
d.resolve(results); | |
} | |
}, d.reject); | |
}; | |
// Since mapFunc may be async, get all invocations of it into flight | |
for(i = 0; i < len; i++) { | |
if(i in array) { | |
resolve(array[i], i); | |
} else { | |
--toResolve; | |
} | |
} | |
} | |
return d.promise; | |
}); | |
} | |
/** | |
* Traditional reduce function, similar to `Array.prototype.reduce()`, but | |
* input may contain promises and/or values, and reduceFunc | |
* may return either a value or a promise, *and* initialValue may | |
* be a promise for the starting value. | |
* | |
* @param {Array|Promise} promise array or promise for an array of anything, | |
* may contain a mix of promises and values. | |
* @param {function} reduceFunc reduce function reduce(currentValue, nextValue, index, total), | |
* where total is the total number of items being reduced, and will be the same | |
* in each call to reduceFunc. | |
* @returns {Promise} that will resolve to the final reduced value | |
*/ | |
function reduce(promise, reduceFunc /*, initialValue */) { | |
var args = slice.call(arguments, 1); | |
return when(promise, function(array) { | |
var total; | |
total = array.length; | |
// Wrap the supplied reduceFunc with one that handles promises and then | |
// delegates to the supplied. | |
args[0] = function (current, val, i) { | |
return when(current, function (c) { | |
return when(val, function (value) { | |
return reduceFunc(c, value, i, total); | |
}); | |
}); | |
}; | |
return reduceArray.apply(array, args); | |
}); | |
} | |
/** | |
* Ensure that resolution of promiseOrValue will trigger resolver with the | |
* value or reason of promiseOrValue, or instead with resolveValue if it is provided. | |
* | |
* @param promiseOrValue | |
* @param {Object} resolver | |
* @param {function} resolver.resolve | |
* @param {function} resolver.reject | |
* @param {*} [resolveValue] | |
* @returns {Promise} | |
*/ | |
function chain(promiseOrValue, resolver, resolveValue) { | |
var useResolveValue = arguments.length > 2; | |
return when(promiseOrValue, | |
function(val) { | |
val = useResolveValue ? resolveValue : val; | |
resolver.resolve(val); | |
return val; | |
}, | |
function(reason) { | |
resolver.reject(reason); | |
return rejected(reason); | |
}, | |
resolver.progress | |
); | |
} | |
// | |
// Utility functions | |
// | |
/** | |
* Apply all functions in queue to value | |
* @param {Array} queue array of functions to execute | |
* @param {*} value argument passed to each function | |
*/ | |
function processQueue(queue, value) { | |
var handler, i = 0; | |
while (handler = queue[i++]) { | |
handler(value); | |
} | |
} | |
/** | |
* Helper that checks arrayOfCallbacks to ensure that each element is either | |
* a function, or null or undefined. | |
* @private | |
* @param {number} start index at which to start checking items in arrayOfCallbacks | |
* @param {Array} arrayOfCallbacks array to check | |
* @throws {Error} if any element of arrayOfCallbacks is something other than | |
* a functions, null, or undefined. | |
*/ | |
function checkCallbacks(start, arrayOfCallbacks) { | |
// TODO: Promises/A+ update type checking and docs | |
var arg, i = arrayOfCallbacks.length; | |
while(i > start) { | |
arg = arrayOfCallbacks[--i]; | |
if (arg != null && typeof arg != 'function') { | |
throw new Error('arg '+i+' must be a function'); | |
} | |
} | |
} | |
/** | |
* No-Op function used in method replacement | |
* @private | |
*/ | |
function noop() {} | |
slice = [].slice; | |
// ES5 reduce implementation if native not available | |
// See: http://es5.github.com/#x15.4.4.21 as there are many | |
// specifics and edge cases. | |
reduceArray = [].reduce || | |
function(reduceFunc /*, initialValue */) { | |
/*jshint maxcomplexity: 7*/ | |
// ES5 dictates that reduce.length === 1 | |
// This implementation deviates from ES5 spec in the following ways: | |
// 1. It does not check if reduceFunc is a Callable | |
var arr, args, reduced, len, i; | |
i = 0; | |
// This generates a jshint warning, despite being valid | |
// "Missing 'new' prefix when invoking a constructor." | |
// See https://github.com/jshint/jshint/issues/392 | |
arr = Object(this); | |
len = arr.length >>> 0; | |
args = arguments; | |
// If no initialValue, use first item of array (we know length !== 0 here) | |
// and adjust i to start at second item | |
if(args.length <= 1) { | |
// Skip to the first real element in the array | |
for(;;) { | |
if(i in arr) { | |
reduced = arr[i++]; | |
break; | |
} | |
// If we reached the end of the array without finding any real | |
// elements, it's a TypeError | |
if(++i >= len) { | |
throw new TypeError(); | |
} | |
} | |
} else { | |
// If initialValue provided, use it | |
reduced = args[1]; | |
} | |
// Do the actual reduce | |
for(;i < len; ++i) { | |
// Skip holes | |
if(i in arr) { | |
reduced = reduceFunc(reduced, arr[i], i, arr); | |
} | |
} | |
return reduced; | |
}; | |
function identity(x) { | |
return x; | |
} | |
return when; | |
}); | |
})(typeof define == 'function' && define.amd | |
? define | |
: function (factory) { typeof exports === 'object' | |
? (module.exports = factory()) | |
: (this.when = factory()); | |
} | |
// Boilerplate for AMD, Node, and browser global | |
); | |
define('when', ['when/when'], function (main) { return main; }); | |
/** | |
* TroopJS event/emitter module | |
* @license MIT http://troopjs.mit-license.org/ © Mikael Karon mailto:[email protected] | |
* @preserve | |
*/ | |
/*global define:false */ | |
define('troopjs-core/event/emitter',[ "compose", "when" ], function EventEmitterModule(Compose, when) { | |
/*jshint laxbreak:true */ | |
var UNDEFINED; | |
var FUNCTION = Function; | |
var MEMORY = "memory"; | |
var CONTEXT = "context"; | |
var CALLBACK = "callback"; | |
var LENGTH = "length"; | |
var HEAD = "head"; | |
var TAIL = "tail"; | |
var NEXT = "next"; | |
var HANDLED = "handled"; | |
var HANDLERS = "handlers"; | |
return Compose( | |
/** | |
* Creates a new EventEmitter | |
* @constructor | |
*/ | |
function EventEmitter() { | |
this[HANDLERS] = {}; | |
}, { | |
/** | |
* Adds a listener for the specified event. | |
* @param {String} event to subscribe to | |
* @param {Object} context to scope callbacks to | |
* @param {...Function} callback for this event | |
* @throws {Error} if no callbacks are provided | |
* @returns {Object} instance of this | |
*/ | |
on : function on(event, context, callback) { | |
var self = this; | |
var args = arguments; | |
var handlers = self[HANDLERS]; | |
var handler; | |
var head; | |
var tail; | |
var length = args[LENGTH]; | |
var offset = 2; | |
// Make sure we have at least one callback | |
if (!(callback instanceof FUNCTION)) { | |
throw new Error("no callback(s) supplied"); | |
} | |
// Have handlers | |
if (event in handlers) { | |
// Get handlers | |
handlers = handlers[event]; | |
// Create new handler | |
handler = {}; | |
// Set handler callback to next arg from offset | |
handler[CALLBACK] = args[offset++]; | |
// Set handler context | |
handler[CONTEXT] = context; | |
// Get tail handler | |
tail = TAIL in handlers | |
// Have tail, update handlers[TAIL][NEXT] to point to handler | |
? handlers[TAIL][NEXT] = handler | |
// Have no tail, update handlers[HEAD] to point to handler | |
: handlers[HEAD] = handler; | |
// Iterate handlers from offset | |
while (offset < length) { | |
// Set tail -> tail[NEXT] -> handler | |
tail = tail[NEXT] = handler = {}; | |
// Set handler callback to next arg from offset | |
handler[CALLBACK] = args[offset++]; | |
// Set handler context | |
handler[CONTEXT] = context; | |
} | |
// Set tail handler | |
handlers[TAIL] = tail; | |
} | |
// No handlers | |
else { | |
// Create head and tail | |
head = tail = handler = {}; | |
// Set handler callback to next arg from offset | |
handler[CALLBACK] = args[offset++]; | |
// Set handler context | |
handler[CONTEXT] = context; | |
// Iterate handlers from offset | |
while (offset < length) { | |
// Set tail -> tail[NEXT] -> handler | |
tail = tail[NEXT] = handler = {}; | |
// Set handler callback to next arg from offset | |
handler[CALLBACK] = args[offset++]; | |
// Set handler context | |
handler[CONTEXT] = context; | |
} | |
// Create event handlers | |
handlers = handlers[event] = {}; | |
// Initialize event handlers | |
handlers[HEAD] = head; | |
handlers[TAIL] = tail; | |
handlers[HANDLED] = 0; | |
} | |
return self; | |
}, | |
/** | |
* Remove a listener for the specified event. | |
* @param {String} event to unsubscribe from | |
* @param {Object} context to scope callbacks to (only applicable if callback is provided) | |
* @param {...Function} [callback] to unsubscribe, if none are provided all callbacks are unsubscribed | |
* @returns {Object} instance of this | |
*/ | |
off : function off(event, context, callback) { | |
var self = this; | |
var args = arguments; | |
var handlers = self[HANDLERS]; | |
var handler; | |
var head; | |
var previous; | |
var length = args[LENGTH]; | |
var offset = 2; | |
// Return fast if we don't have subscribers | |
if (!(event in handlers)) { | |
return self; | |
} | |
// Get handlers | |
handlers = handlers[event]; | |
// Return fast if there's no HEAD | |
if (!(HEAD in handlers)) { | |
return self; | |
} | |
// Get head | |
head = handlers[HEAD]; | |
// Loop callbacks | |
while (offset < length) { | |
// Store callback | |
callback = args[offset++]; | |
// Get first handler | |
handler = previous = head; | |
// Step through handlers | |
do { | |
// Check if this handler should be unlinked | |
if (handler[CALLBACK] === callback && (context === UNDEFINED || handler[CONTEXT] === context)) { | |
// Is this the first handler | |
if (handler === head) { | |
// Re-link head and previous, then continue | |
head = previous = handler[NEXT]; | |
continue; | |
} | |
// Unlink current handler, then continue | |
previous[NEXT] = handler[NEXT]; | |
continue; | |
} | |
// Update previous pointer | |
previous = handler; | |
} while ((handler = handler[NEXT]) !== UNDEFINED); | |
} | |
// Update head and tail | |
if (head && previous) { | |
handlers[HEAD] = head; | |
handlers[TAIL] = previous; | |
} | |
else { | |
delete handlers[HEAD]; | |
delete handlers[TAIL]; | |
} | |
return self; | |
}, | |
/** | |
* Reemit event from memory | |
* @param {String} event to reemit | |
* @param {Object} context to filter callbacks by | |
* @param {...Function} [callback] to reemit, if none are provided all callbacks will be reemited | |
* @returns {Object} instance of this | |
*/ | |
reemit : function reemit(event, context, callback) { | |
var self = this; | |
var args = arguments; | |
var handlers = self[HANDLERS]; | |
var handler; | |
var handled; | |
var head; | |
var length = args[LENGTH]; | |
var offset = 2; | |
// Have event in handlers | |
if (event in handlers) { | |
// Get handlers | |
handlers = handlers[event]; | |
// Have memory in handlers | |
if (MEMORY in handlers) { | |
// If we have no HEAD we can return a promise resolved with memory | |
if (!(HEAD in handlers)) { | |
return when.resolve(handlers[MEMORY]); | |
} | |
// Get first handler | |
head = handlers[HEAD]; | |
// Compute next handled | |
handled = handlers[HANDLED] + 1; | |
// Loop callbacks | |
while (offset < length) { | |
// Store callback | |
callback = args[offset++]; | |
// Get first handler | |
handler = head; | |
// Step through handlers | |
do { | |
// Check if this handler should be reemited | |
if (handler[CALLBACK] === callback && (context === UNDEFINED || handler[CONTEXT] === context)) { | |
continue; | |
} | |
// Mark this handler as already handled (to prevent reemit) | |
handler[HANDLED] = handled; | |
} while ((handler = handler[NEXT]) !== UNDEFINED); | |
} | |
// Return self.emit with memory | |
return self.emit.apply(self, handlers[MEMORY]); | |
} | |
} | |
// Return resolved promise | |
return when.resolve(); | |
}, | |
/** | |
* Execute each of the listeners in order with the supplied arguments | |
* @param {String} event to emit | |
* @returns {Promise} promise that resolves with results from all listeners | |
*/ | |
emit : function emit(event) { | |
var self = this; | |
var args = arguments; | |
var handlers = self[HANDLERS]; | |
var handler; | |
var handled; | |
/** | |
* Internal function for async execution of callbacks | |
* @private | |
* @param {Array} [_arg] result from previous callback | |
* @return {Promise} promise of next execution | |
*/ | |
function next(_arg) { | |
// Update arg | |
args = _arg || args; | |
// Step forward until we find a unhandled handler | |
while(handler[HANDLED] === handled) { | |
// No more handlers, escape! | |
if (!(handler = handler[NEXT])) { | |
// Remember arg | |
handlers[MEMORY] = args; | |
// Return promise resolved with arg | |
return when.resolve(args); | |
} | |
} | |
// Update handled | |
handler[HANDLED] = handled; | |
// Return promise of callback execution, chain next | |
return when(handler[CALLBACK].apply(handler[CONTEXT], args), next); | |
} | |
// Have event in handlers | |
if (event in handlers) { | |
// Get handlers | |
handlers = handlers[event]; | |
// Update handled | |
handled = ++handlers[HANDLED]; | |
// Have head in handlers | |
if (HEAD in handlers) { | |
// Get first handler | |
handler = handlers[HEAD]; | |
try { | |
// Return promise | |
return next(args); | |
} | |
catch (e) { | |
// Return promise rejected with exception | |
return when.reject(e); | |
} | |
} | |
} | |
// No event in handlers | |
else { | |
// Create handlers and store with event | |
handlers[event] = handlers = {}; | |
// Set handled | |
handlers[HANDLED] = 0; | |
} | |
// Remember arg | |
handlers[MEMORY] = args; | |
// Return promise resolved with arg | |
return when.resolve(args); | |
} | |
}); | |
}); | |
/*! | |
* TroopJS base component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-core/component/base',[ "../event/emitter" ], function ComponentModule(Emitter) { | |
/*jshint strict:false, smarttabs:true */ | |
var COUNT = 0; | |
var INSTANCE_COUNT = "instanceCount"; | |
var Component = Emitter.extend(function Component() { | |
this[INSTANCE_COUNT] = COUNT++; | |
}, { | |
instanceCount : COUNT, | |
displayName : "core/component" | |
}); | |
/** | |
* Generates string representation of this object | |
* @returns Combination displayName and instanceCount | |
*/ | |
Component.prototype.toString = function () { | |
var self = this; | |
return self.displayName + "@" + self[INSTANCE_COUNT]; | |
}; | |
return Component; | |
}); | |
/*! | |
* TroopJS pubsub/hub module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-core/pubsub/hub',[ "compose", "../component/base" ], function HubModule(Compose, Component) { | |
/*jshint strict:false, smarttabs:true */ | |
var from = Compose.from; | |
return Compose.create(Component, { | |
displayName: "core/pubsub/hub", | |
subscribe : from(Component, "on"), | |
unsubscribe : from(Component, "off"), | |
publish : from(Component, "emit"), | |
republish : from(Component, "reemit") | |
}); | |
}); | |
/*! | |
* TroopJS gadget component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-core/component/gadget',[ "./base", "when", "../pubsub/hub" ], function GadgetModule(Component, when, hub) { | |
/*jshint strict:false, smarttabs:true, newcap:false, forin:false, loopfunc:true laxbreak:true */ | |
var UNDEFINED; | |
var NULL = null; | |
var FUNCTION = Function; | |
var ARRAY_PROTO = Array.prototype; | |
var ARRAY_SLICE = ARRAY_PROTO.slice; | |
var ARRAY_SPLICE = ARRAY_PROTO.splice; | |
var ARRAY_UNSHIFT = ARRAY_PROTO.unshift; | |
var RE_HUB = /^hub(?::(\w+))?\/(.+)/; | |
var RE_SIG = /^sig(?::(\w+))?\/(.+)/; | |
var PUBLISH = hub.publish; | |
var REPUBLISH = hub.republish; | |
var SUBSCRIBE = hub.subscribe; | |
var UNSUBSCRIBE = hub.unsubscribe; | |
var FEATURES = "features"; | |
var SIGNALS = "signals"; | |
var SUBSCRIPTIONS = "subscriptions"; | |
return Component.extend(function Gadget() { | |
var self = this; | |
var bases = self.constructor._getBases(true); | |
var base; | |
var callbacks; | |
var callback; | |
var i = bases.length; | |
var j; | |
var jMax; | |
var signals = self[SIGNALS] = {}; | |
var signal; | |
var matches; | |
var key; | |
// Iterate base chain (backwards) | |
while((base = bases[--i])) { | |
add: for (key in base) { | |
// Get value | |
callback = base[key]; | |
// Continue if value is not a function | |
if (!(callback instanceof FUNCTION)) { | |
continue; | |
} | |
// Continue if we can't match | |
if ((matches = RE_SIG.exec(key)) === NULL) { | |
continue; | |
} | |
// Get signal | |
signal = matches[2]; | |
// Have we stored any callbacks for this signal? | |
if (signal in signals) { | |
// Get callbacks (for this signal) | |
callbacks = signals[signal]; | |
// Reset counters | |
j = jMax = callbacks.length; | |
// Loop callbacks, continue add if we've already added this callback | |
while (j--) { | |
if (callback === callbacks[j]) { | |
continue add; | |
} | |
} | |
// Add callback to callbacks chain | |
callbacks[jMax] = callback; | |
} | |
else { | |
// First callback | |
signals[signal] = [callback]; | |
} | |
} | |
} | |
}, { | |
displayName : "core/component/gadget", | |
/** | |
* Signal handler for 'initialize' | |
*/ | |
"sig/initialize" : function initialize() { | |
var self = this; | |
var subscription; | |
var subscriptions = self[SUBSCRIPTIONS] = []; | |
var key; | |
var value; | |
var matches; | |
var topic; | |
// Loop over each property in gadget | |
for (key in self) { | |
// Get value | |
value = self[key]; | |
// Continue if value is not a function | |
if (!(value instanceof FUNCTION)) { | |
continue; | |
} | |
// Continue if we can't match | |
if ((matches = RE_HUB.exec(key)) === NULL) { | |
continue; | |
} | |
// Get topic | |
topic = matches[2]; | |
// Subscribe | |
SUBSCRIBE.call(hub, topic, self, value); | |
// Create and store subscription | |
subscriptions[subscriptions.length] = subscription = [topic, self, value]; | |
// Store features | |
subscription[FEATURES] = matches[1]; | |
// NULL value | |
self[key] = NULL; | |
} | |
}, | |
"sig/start" : function start() { | |
var self = this; | |
var subscriptions = self[SUBSCRIPTIONS]; | |
var subscription; | |
var i = subscriptions.length; | |
var results = []; | |
while ((subscription = subscriptions[--i]) !== UNDEFINED) { | |
if (subscription[FEATURES] !== "memory") { | |
continue; | |
} | |
results.push(REPUBLISH.call(hub, subscription[0], subscription[1], subscription[2])); | |
} | |
return when.map(results, function (o) { return o; }); | |
}, | |
/** | |
* Signal handler for 'finalize' | |
*/ | |
"sig/finalize" : function finalize() { | |
var self = this; | |
var subscriptions = self[SUBSCRIPTIONS]; | |
var subscription; | |
// Loop over subscriptions | |
while ((subscription = subscriptions.shift()) !== UNDEFINED) { | |
UNSUBSCRIBE.call(hub, subscription[0], subscription[1], subscription[2]); | |
} | |
}, | |
/** | |
* Signals the component | |
* @param signal {String} Signal | |
* @return {*} | |
*/ | |
"signal" : function onSignal(signal) { | |
var self = this; | |
var args = ARRAY_SLICE.call(arguments); | |
var callbacks = self[SIGNALS][signal]; | |
var length = callbacks | |
? callbacks.length | |
: 0; | |
var index = 0; | |
function next(_args) { | |
// Update args | |
args = _args || args; | |
// Return a chained promise of next callback, or a promise resolved with args | |
return length > index | |
? when(callbacks[index++].apply(self, args), next) | |
: when.resolve(args); | |
} | |
try { | |
// Return promise | |
return next(); | |
} | |
catch (e) { | |
// Return rejected promise | |
return when.reject(e); | |
} | |
}, | |
/** | |
* Calls hub.publish in self context | |
*/ | |
"publish" : function publish() { | |
return PUBLISH.apply(hub, arguments); | |
}, | |
/** | |
* Calls hub.subscribe in self context | |
*/ | |
"subscribe" : function subscribe() { | |
var self = this; | |
var args = arguments; | |
// Add self as context | |
ARRAY_SPLICE.call(args, 1, 0, self); | |
// Subscribe | |
SUBSCRIBE.apply(hub, args); | |
return self; | |
}, | |
/** | |
* Calls hub.unsubscribe in self context | |
*/ | |
"unsubscribe" : function unsubscribe() { | |
var self = this; | |
var args = arguments; | |
// Add self as context | |
ARRAY_SPLICE.call(args, 1, 0, self); | |
// Unsubscribe | |
UNSUBSCRIBE.apply(hub, args); | |
return self; | |
}, | |
/** | |
* Start the component | |
* @return {*} | |
*/ | |
"start" : function start() { | |
var self = this; | |
var _signal = self.signal; | |
var args = arguments; | |
// Add signal to arguments | |
ARRAY_UNSHIFT.call(args, "initialize"); | |
return _signal.apply(self, args).then(function () { | |
// Modify args to change signal | |
args[0] = "start"; | |
return _signal.apply(self, args); | |
}); | |
}, | |
/** | |
* Stops the component | |
* @return {*} | |
*/ | |
"stop" : function stop() { | |
var self = this; | |
var _signal = self.signal; | |
var args = arguments; | |
// Add signal to arguments | |
ARRAY_UNSHIFT.call(args, "stop"); | |
return _signal.apply(self, args).then(function () { | |
// Modify args to change signal | |
args[0] = "finalize"; | |
return _signal.apply(self, args); | |
}); | |
} | |
}); | |
}); | |
/*! | |
* TroopJS service component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-core/component/service',[ "./gadget" ], function ServiceModule(Gadget) { | |
/*jshint strict:false */ | |
return Gadget.extend({ | |
"displayName" : "core/component/service" | |
}); | |
}); | |
/*! | |
* TroopJS Data query component | |
* @license TroopJS Copyright 2013, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-data/query/component', [ "troopjs-core/component/base" ], function QueryModule(Component) { | |
/*jshint laxbreak:true */ | |
var UNDEFINED; | |
var TRUE = true; | |
var FALSE = false; | |
var OBJECT = Object; | |
var ARRAY = Array; | |
var CONSTRUCTOR = "constructor"; | |
var LENGTH = "length"; | |
var OP = "op"; | |
var OP_ID = "!"; | |
var OP_PROPERTY = "."; | |
var OP_PATH = ","; | |
var OP_QUERY = "|"; | |
var TEXT = "text"; | |
var RAW = "raw"; | |
var RESOLVED = "resolved"; | |
var _ID = "id"; | |
var _EXPIRES = "expires"; | |
var _COLLAPSED = "collapsed"; | |
var _AST = "_ast"; | |
var _QUERY = "_query"; | |
var RE_TEXT = /("|')(.*?)\1/; | |
var TO_RAW = "$2"; | |
var RE_RAW = /!(.*[!,|.\s]+.*)/; | |
var TO_TEXT = "!'$1'"; | |
return Component.extend(function Query(query) { | |
var self = this; | |
if (query !== UNDEFINED) { | |
self[_QUERY] = query; | |
} | |
}, { | |
"displayName" : "data/query/component", | |
"parse" : function parse(query) { | |
var self = this; | |
// Reset _AST | |
delete self[_AST]; | |
// Set _QUERY | |
query = self[_QUERY] = (query || self[_QUERY] || ""); | |
var i; // Index | |
var l; // Length | |
var c; // Current character | |
var m; // Current mark | |
var q; // Current quote | |
var o; // Current operation | |
var ast = []; // _AST | |
// Step through the query | |
for (i = m = 0, l = query[LENGTH]; i < l; i++) { | |
c = query.charAt(i); | |
switch (c) { | |
case "\"" : // Double quote | |
case "'" : // Single quote | |
// Set / unset quote char | |
q = q === c | |
? UNDEFINED | |
: c; | |
break; | |
case OP_ID : | |
// Break fast if we're quoted | |
if (q !== UNDEFINED) { | |
break; | |
} | |
// Init new op | |
o = {}; | |
o[OP] = c; | |
break; | |
case OP_PROPERTY : | |
case OP_PATH : | |
// Break fast if we're quoted | |
if (q !== UNDEFINED) { | |
break; | |
} | |
// If there's an active op, store TEXT and push on _AST | |
if (o !== UNDEFINED) { | |
o[RAW] = (o[TEXT] = query.substring(m, i)).replace(RE_TEXT, TO_RAW); | |
ast.push(o); | |
} | |
// Init new op | |
o = {}; | |
o[OP] = c; | |
// Set mark | |
m = i + 1; | |
break; | |
case OP_QUERY : | |
case " " : // Space | |
case "\t" : // Horizontal tab | |
case "\r" : // Carriage return | |
case "\n" : // Newline | |
// Break fast if we're quoted | |
if (q !== UNDEFINED) { | |
break; | |
} | |
// If there's an active op, store TEXT and push on _AST | |
if (o !== UNDEFINED) { | |
o[RAW] = (o[TEXT] = query.substring(m, i)).replace(RE_TEXT, TO_RAW); | |
ast.push(o); | |
} | |
// Reset op | |
o = UNDEFINED; | |
// Set mark | |
m = i + 1; | |
break; | |
} | |
} | |
// If there's an active op, store TEXT and push on _AST | |
if (o !== UNDEFINED) { | |
o[RAW] = (o[TEXT] = query.substring(m, l)).replace(RE_TEXT, TO_RAW); | |
ast.push(o); | |
} | |
// Set _AST | |
self[_AST] = ast; | |
return self; | |
}, | |
"reduce" : function reduce(cache) { | |
var self = this; | |
var now = 0 | new Date().getTime() / 1000; | |
// If we're not parsed - parse | |
if (!(_AST in self)) { | |
self.parse(); | |
} | |
var ast = self[_AST]; // _AST | |
var result = []; // Result | |
var i; // Index | |
var j; | |
var c; | |
var l; // Length | |
var o; // Current operation | |
var x; // Current raw | |
var r; // Current root | |
var n; // Current node | |
var k = FALSE; // Keep flag | |
// First step is to resolve what we can from the _AST | |
for (i = 0, l = ast[LENGTH]; i < l; i++) { | |
o = ast[i]; | |
switch (o[OP]) { | |
case OP_ID : | |
// Set root | |
r = o; | |
// Get e from o | |
x = o[RAW]; | |
// Do we have this item in cache | |
if (x in cache) { | |
// Set current node | |
n = cache[x]; | |
// Set RESOLVED if we're not collapsed or expired | |
o[RESOLVED] = n[_COLLAPSED] !== TRUE && !(_EXPIRES in n) || n[_EXPIRES] > now; | |
} | |
else { | |
// Reset current root and node | |
n = UNDEFINED; | |
// Reset RESOLVED | |
o[RESOLVED] = FALSE; | |
} | |
break; | |
case OP_PROPERTY : | |
// Get e from o | |
x = o[RAW]; | |
// Do we have a node and this item in the node | |
if (n && x in n) { | |
// Set current node | |
n = n[x]; | |
// Get constructor | |
c = n[CONSTRUCTOR]; | |
// If the constructor is an array | |
if (c === ARRAY) { | |
// Set naive resolved | |
o[RESOLVED] = TRUE; | |
// Iterate backwards over n | |
for (j = n[LENGTH]; j-- > 0;) { | |
// Get item | |
c = n[j]; | |
// If the constructor is not an object | |
// or the object does not duck-type _ID | |
// or the object is not collapsed | |
// and the object does not duck-type _EXPIRES | |
// or the objects is not expired | |
if (c[CONSTRUCTOR] !== OBJECT | |
|| !(_ID in c) | |
|| c[_COLLAPSED] !== TRUE | |
&& !(_EXPIRES in c) | |
|| c[_EXPIRES] > now) { | |
continue; | |
} | |
// Change RESOLVED | |
o[RESOLVED] = FALSE; | |
break; | |
} | |
} | |
// If the constructor is _not_ an object or n does not duck-type _ID | |
else if (c !== OBJECT || !(_ID in n)) { | |
o[RESOLVED] = TRUE; | |
} | |
// We know c _is_ and object and n _does_ duck-type _ID | |
else { | |
// Change OP to OP_ID | |
o[OP] = OP_ID; | |
// Update RAW to _ID and TEXT to escaped version of RAW | |
o[TEXT] = (o[RAW] = n[_ID]).replace(RE_RAW, TO_TEXT); | |
// Set RESOLVED if we're not collapsed or expired | |
o[RESOLVED] = n[_COLLAPSED] !== TRUE && !(_EXPIRES in n) || n[_EXPIRES] > now; | |
} | |
} | |
else { | |
// Reset current node and RESOLVED | |
n = UNDEFINED; | |
o[RESOLVED] = FALSE; | |
} | |
break; | |
case OP_PATH : | |
// Get e from r | |
x = r[RAW]; | |
// Set current node | |
n = cache[x]; | |
// Change OP to OP_ID | |
o[OP] = OP_ID; | |
// Copy properties from r | |
o[TEXT] = r[TEXT]; | |
o[RAW] = x; | |
o[RESOLVED] = r[RESOLVED]; | |
break; | |
} | |
} | |
// After that we want to reduce 'dead' operations from the _AST | |
while (l-- > 0) { | |
o = ast[l]; | |
switch(o[OP]) { | |
case OP_ID : | |
// If the keep flag is set, or the op is not RESOLVED | |
if (k || o[RESOLVED] !== TRUE) { | |
result.unshift(o); | |
} | |
// Reset keep flag | |
k = FALSE; | |
break; | |
case OP_PROPERTY : | |
result.unshift(o); | |
// Set keep flag | |
k = TRUE; | |
break; | |
} | |
} | |
// Update _AST | |
self[_AST] = result; | |
return self; | |
}, | |
"ast" : function ast() { | |
var self = this; | |
// If we're not parsed - parse | |
if (!(_AST in self)) { | |
self.parse(); | |
} | |
return self[_AST]; | |
}, | |
"rewrite" : function rewrite() { | |
var self = this; | |
// If we're not parsed - parse | |
if (!(_AST in self)) { | |
self.parse(); | |
} | |
var ast = self[_AST]; // AST | |
var result = ""; // Result | |
var l; // Current length | |
var i; // Current index | |
var o; // Current operation | |
// Step through AST | |
for (i = 0, l = ast[LENGTH]; i < l; i++) { | |
o = ast[i]; | |
switch(o[OP]) { | |
case OP_ID : | |
// If this is the first OP_ID, there's no need to add OP_QUERY | |
result += i === 0 | |
? o[TEXT] | |
: OP_QUERY + o[TEXT]; | |
break; | |
case OP_PROPERTY : | |
result += OP_PROPERTY + o[TEXT]; | |
break; | |
} | |
} | |
return result; | |
} | |
}); | |
}); | |
/*! | |
* TroopJS pubsub/topic module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-core/pubsub/topic',[ "../component/base", "troopjs-utils/unique" ], function TopicModule(Component, unique) { | |
/*jshint strict:false, smarttabs:true, laxbreak:true */ | |
var TOSTRING = Object.prototype.toString; | |
var TOSTRING_ARRAY = TOSTRING.call(Array.prototype); | |
function comparator (a, b) { | |
return a.publisherInstanceCount === b.publisherInstanceCount; | |
} | |
var Topic = Component.extend(function Topic(topic, publisher, parent) { | |
var self = this; | |
self.topic = topic; | |
self.publisher = publisher; | |
self.parent = parent; | |
self.publisherInstanceCount = publisher.instanceCount; | |
}, { | |
displayName : "core/pubsub/topic", | |
/** | |
* Traces topic origin to root | |
* @returns String representation of all topics traced down to root | |
*/ | |
trace : function trace() { | |
var current = this; | |
var constructor = current.constructor; | |
var parent; | |
var item; | |
var stack = ""; | |
var i; | |
var u; | |
var iMax; | |
while (current) { | |
if (TOSTRING.call(current) === TOSTRING_ARRAY) { | |
u = unique.call(current, comparator); | |
for (i = 0, iMax = u.length; i < iMax; i++) { | |
item = u[i]; | |
u[i] = item.constructor === constructor | |
? item.trace() | |
: item.topic; | |
} | |
stack += u.join(","); | |
break; | |
} | |
parent = current.parent; | |
stack += parent | |
? current.publisher + ":" | |
: current.publisher; | |
current = parent; | |
} | |
return stack; | |
} | |
}); | |
/** | |
* Generates string representation of this object | |
* @returns Instance topic | |
*/ | |
Topic.prototype.toString = function () { | |
return this.topic; | |
}; | |
return Topic; | |
}); | |
/*! | |
* TroopJS Data query service | |
* @license TroopJS Copyright 2013, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-data/query/service',[ "module", "troopjs-core/component/service", "./component", "troopjs-core/pubsub/topic", "when", "troopjs-utils/merge" ], function QueryServiceModule(module, Service, Query, Topic, when, merge) { | |
/*jshint laxbreak:true */ | |
var ARRAY_PROTO = Array.prototype; | |
var SLICE = ARRAY_PROTO.slice; | |
var CONCAT = ARRAY_PROTO.concat; | |
var PUSH = ARRAY_PROTO.push; | |
var LENGTH = "length"; | |
var BATCHES = "batches"; | |
var INTERVAL = "interval"; | |
var CACHE = "cache"; | |
var TOPIC = "topic"; | |
var QUERIES = "queries"; | |
var RESOLVED = "resolved"; | |
var RAW = "raw"; | |
var ID = "id"; | |
var Q = "q"; | |
var CONFIG = module.config(); | |
var QueryService = Service.extend(function (cache) { | |
var self = this; | |
self[BATCHES] = []; | |
self[CACHE] = cache; | |
}, { | |
"displayName" : "data/query/service", | |
"sig/start" : function start() { | |
var self = this; | |
var cache = self[CACHE]; | |
// Set interval (if we don't have one) | |
self[INTERVAL] = INTERVAL in self | |
? self[INTERVAL] | |
: setInterval(function scan() { | |
var batches = self[BATCHES]; | |
// Return fast if there is nothing to do | |
if (batches[LENGTH] === 0) { | |
return; | |
} | |
// Reset batches | |
self[BATCHES] = []; | |
function request() { | |
var q = []; | |
var topics = []; | |
var batch; | |
var i; | |
// Iterate batches | |
for (i = batches[LENGTH]; i--;) { | |
batch = batches[i]; | |
// Add batch[TOPIC] to topics | |
PUSH.call(topics, batch[TOPIC]); | |
// Add batch[Q] to q | |
PUSH.apply(q, batch[Q]); | |
} | |
// Publish ajax | |
return self.publish(Topic("ajax", self, topics), merge.call({ | |
"data": { | |
"q": q.join("|") | |
} | |
}, CONFIG)); | |
} | |
function done(data) { | |
var batch; | |
var queries; | |
var id; | |
var i; | |
var j; | |
// Add all new data to cache | |
cache.put(data); | |
// Iterate batches | |
for (i = batches[LENGTH]; i--;) { | |
batch = batches[i]; | |
queries = batch[QUERIES]; | |
id = batch[ID]; | |
// Iterate queries | |
for (j = queries[LENGTH]; j--;) { | |
// If we have a corresponding ID, fetch from cache | |
if (j in id) { | |
queries[j] = cache[id[j]]; | |
} | |
} | |
// Resolve batch | |
batch.resolve(queries); | |
} | |
} | |
function fail() { | |
var batch; | |
var i; | |
// Iterate batches | |
for (i = batches[LENGTH]; i--;) { | |
batch = batches[i]; | |
// Reject (with original queries as argument) | |
batch.reject(batch[QUERIES]); | |
} | |
} | |
// Request and handle response | |
return request().then(done, fail); | |
}, 200); | |
}, | |
"sig/stop" : function stop() { | |
var self = this; | |
// Only do this if we have an interval | |
if (INTERVAL in self) { | |
// Clear interval | |
clearInterval(self[INTERVAL]); | |
// Reset interval | |
delete self[INTERVAL]; | |
} | |
}, | |
"hub/query" : function hubQuery(topic /* query, query, query, .., */) { | |
var self = this; | |
var batches = self[BATCHES]; | |
var cache = self[CACHE]; | |
var q = []; | |
var id = []; | |
var ast; | |
var i; | |
var j; | |
var iMax; | |
var queries; | |
var query; | |
// Create deferred batch | |
var batch = when.defer(); | |
try { | |
// Slice and flatten queries | |
queries = CONCAT.apply(ARRAY_PROTO, SLICE.call(arguments, 1)); | |
// Iterate queries | |
for (i = 0, iMax = queries[LENGTH]; i < iMax; i++) { | |
// Init Query | |
query = Query(queries[i]); | |
// Get AST | |
ast = query.ast(); | |
// If we have an ID | |
if (ast[LENGTH] > 0) { | |
// Store raw ID | |
id[i] = ast[0][RAW]; | |
} | |
// Get reduced AST | |
ast = query.reduce(cache).ast(); | |
// Step backwards through AST | |
for (j = ast[LENGTH]; j-- > 0;) { | |
// If this op is not resolved | |
if (!ast[j][RESOLVED]) { | |
// Add rewritten (and reduced) query to q | |
PUSH.call(q, query.rewrite()); | |
break; | |
} | |
} | |
} | |
// If all queries were fully reduced, we can quick resolve | |
if (q[LENGTH] === 0) { | |
// Iterate queries | |
for (i = 0; i < iMax; i++) { | |
// If we have a corresponding ID, fetch from cache | |
if (i in id) { | |
queries[i] = cache[id[i]]; | |
} | |
} | |
// Resolve batch | |
batch.resolve(queries); | |
} | |
else { | |
// Store properties on batch | |
batch[TOPIC] = topic; | |
batch[QUERIES] = queries; | |
batch[ID] = id; | |
batch[Q] = q; | |
// Add batch to batches | |
batches.push(batch); | |
} | |
} | |
catch (e) { | |
batch.reject(e); | |
} | |
// Return promise | |
return batch.promise; | |
} | |
}); | |
QueryService.config = function config(_config) { | |
return merge.call(CONFIG, _config); | |
}; | |
return QueryService; | |
}); | |
/*! | |
* TroopJS Data cache component | |
* @license TroopJS Copyright 2013, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-data/cache/component', [ "troopjs-core/component/gadget" ], function CacheModule(Gadget) { | |
/*jshint laxbreak:true */ | |
var UNDEFINED; | |
var FALSE = false; | |
var NULL = null; | |
var OBJECT = Object; | |
var ARRAY = Array; | |
var SECOND = 1000; | |
var INTERVAL = "interval"; | |
var GENERATIONS = "generations"; | |
var AGE = "age"; | |
var HEAD = "head"; | |
var NEXT = "next"; | |
var EXPIRES = "expires"; | |
var CONSTRUCTOR = "constructor"; | |
var LENGTH = "length"; | |
var _ID = "id"; | |
var _MAXAGE = "maxAge"; | |
var _EXPIRES = "expires"; | |
var _INDEXED = "indexed"; | |
var _COLLAPSED = "collapsed"; | |
/** | |
* Internal method to put a node in the cache | |
* @param node Node | |
* @param constructor Constructor of value | |
* @param now Current time (seconds) | |
* @returns Cached node | |
*/ | |
function _put(node, constructor, now) { | |
var self = this; | |
var result; | |
var id; | |
var i; | |
var iMax; | |
var expires; | |
var expired; | |
var head; | |
var current; | |
var next; | |
var generation; | |
var generations = self[GENERATIONS]; | |
var property; | |
var value; | |
// First add node to cache (or get the already cached instance) | |
cache : { | |
// Can't cache if there is no _ID | |
if (!(_ID in node)) { | |
result = node; // Reuse ref to node (avoids object creation) | |
break cache; | |
} | |
// Get _ID | |
id = node[_ID]; | |
// In cache, get it! | |
if (id in self) { | |
result = self[id]; | |
break cache; | |
} | |
// Not in cache, add it! | |
result = self[id] = node; // Reuse ref to node (avoids object creation) | |
// Update _INDEXED | |
result[_INDEXED] = now; | |
} | |
// We have to deep traverse the graph before we do any expiration (as more data for this object can be available) | |
// Check that this is an ARRAY | |
if (constructor === ARRAY) { | |
// Index all values | |
for (i = 0, iMax = node[LENGTH]; i < iMax; i++) { | |
// Keep value | |
value = node[i]; | |
// Get constructor of value (safely, falling back to UNDEFINED) | |
constructor = value === NULL || value === UNDEFINED | |
? UNDEFINED | |
: value[CONSTRUCTOR]; | |
// Do magic comparison to see if we recursively put this in the cache, or plain put | |
result[i] = (constructor === OBJECT || constructor === ARRAY && value[LENGTH] !== 0) | |
? _put.call(self, value, constructor, now) | |
: value; | |
} | |
} | |
// Check that this is an OBJECT | |
else if (constructor === OBJECT) { | |
// Index all properties | |
for (property in node) { | |
// Except the _ID property | |
// or the _COLLAPSED property, if it's false | |
if (property === _ID | |
|| (property === _COLLAPSED && result[_COLLAPSED] === FALSE)) { | |
continue; | |
} | |
// Keep value | |
value = node[property]; | |
// Get constructor of value (safely, falling back to UNDEFINED) | |
constructor = value === NULL || value === UNDEFINED | |
? UNDEFINED | |
: value[CONSTRUCTOR]; | |
// Do magic comparison to see if we recursively put this in the cache, or plain put | |
result[property] = (constructor === OBJECT || constructor === ARRAY && value[LENGTH] !== 0) | |
? _put.call(self, value, constructor, now) | |
: value; | |
} | |
} | |
// Check if we need to move result between generations | |
move : { | |
// Break fast if id is NULL | |
if (id === NULL) { | |
break move; | |
} | |
// Calculate expiration and floor | |
// '>>>' means convert anything other than posiitive integer into 0 | |
expires = 0 | now + (result[_MAXAGE] >>> 0); | |
remove : { | |
// Fail fast if there is no old expiration | |
if (!(_EXPIRES in result)) { | |
break remove; | |
} | |
// Get current expiration | |
expired = result[_EXPIRES]; | |
// If expiration has not changed, we can continue | |
if (expired === expires) { | |
break move; | |
} | |
// Remove ref from generation (if that generation exists) | |
if (expired in generations) { | |
delete generations[expired][id]; | |
} | |
} | |
add : { | |
// Update expiration time | |
result[_EXPIRES] = expires; | |
// Existing generation | |
if (expires in generations) { | |
// Add result to generation | |
generations[expires][id] = result; | |
break add; | |
} | |
// Create generation with expiration set | |
(generation = generations[expires] = {})[EXPIRES] = expires; | |
// Add result to generation | |
generation[id] = result; | |
// Short circuit if there is no head | |
if (generations[HEAD] === UNDEFINED) { | |
generations[HEAD] = generation; | |
break add; | |
} | |
// Step through list as long as there is a next, and expiration is "older" than the next expiration | |
for (current = head = generations[HEAD]; (next = current[NEXT]) !== UNDEFINED && next[EXPIRES] < expires; current = next); | |
// Check if we're still on the head and if we're younger | |
if (current === head && current[EXPIRES] > expires) { | |
// Next generation is the current one (head) | |
generation[NEXT] = current; | |
// Reset head to new generation | |
generations[HEAD] = generation; | |
break add; | |
} | |
// Insert new generation between current and current.next | |
generation[NEXT] = current[NEXT]; | |
current[NEXT] = generation; | |
} | |
} | |
return result; | |
} | |
return Gadget.extend(function (age) { | |
var me = this; | |
me[AGE] = age || (60 * SECOND); | |
me[GENERATIONS] = {}; | |
}, { | |
"displayName" : "data/cache/component", | |
"sig/start" : function start() { | |
var self = this; | |
var generations = self[GENERATIONS]; | |
// Create new sweep interval | |
self[INTERVAL] = INTERVAL in self | |
? self[INTERVAL] | |
: setInterval(function sweep() { | |
// Calculate expiration of this generation | |
var expires = 0 | new Date().getTime() / SECOND; | |
var property; | |
var current; | |
// Get head | |
current = generations[HEAD]; | |
// Fail fast if there's no head | |
if (current === UNDEFINED) { | |
return; | |
} | |
do { | |
// Exit if this generation is to young | |
if (current[EXPIRES] > expires) { | |
break; | |
} | |
// Iterate all properties on current | |
for (property in current) { | |
// And is it not a reserved property | |
if (property === EXPIRES || property === NEXT || property === GENERATIONS) { | |
continue; | |
} | |
// Delete from self (cache) | |
delete self[property]; | |
} | |
// Delete generation | |
delete generations[current[EXPIRES]]; | |
} | |
// While there's a next | |
while ((current = current[NEXT])); | |
// Reset head | |
generations[HEAD] = current; | |
}, self[AGE]); | |
}, | |
"sig/stop" : function stop() { | |
var self = this; | |
// Only do this if we have an interval | |
if (INTERVAL in self) { | |
// Clear interval | |
clearInterval(self[INTERVAL]); | |
// Reset interval | |
delete self[INTERVAL]; | |
} | |
}, | |
"sig/finalize" : function finalize() { | |
var self = this; | |
var property; | |
// Iterate all properties on self | |
for (property in self) { | |
// Don't delete non-objects or objects that don't ducktype cachable | |
if (self[property][CONSTRUCTOR] !== OBJECT || !(_ID in self[property])) { | |
continue; | |
} | |
// Delete from self (cache) | |
delete self[property]; | |
} | |
}, | |
/** | |
* Puts a node into the cache | |
* @param node Node to add (object || array) | |
* @returns Cached node (if it existed in the cache before), otherwise the node sent in | |
*/ | |
"put" : function put(node) { | |
var self = this; | |
// Get constructor of node (safely, falling back to UNDEFINED) | |
var constructor = node === NULL || node === UNDEFINED | |
? UNDEFINED | |
: node[CONSTRUCTOR]; | |
// Do magic comparison to see if we should cache this object | |
return constructor === OBJECT || constructor === ARRAY && node[LENGTH] !== 0 | |
? _put.call(self, node, constructor, 0 | new Date().getTime() / SECOND) | |
: node; | |
} | |
}); | |
}); | |
/*! | |
* TroopJS ajax/service module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-browser/ajax/service',[ "troopjs-core/component/service", "jquery", "troopjs-utils/merge" ], function AjaxModule(Service, $, merge) { | |
/*jshint strict:false */ | |
var TRACE = "trace"; | |
return Service.extend({ | |
displayName : "browser/ajax/service", | |
"hub/ajax" : function ajax(topic, settings) { | |
// Request | |
return $.ajax(merge.call({ | |
"headers": { | |
"x-request-id": new Date().getTime(), | |
"x-components": topic[TRACE] instanceof Function ? topic[TRACE]() : topic | |
} | |
}, settings)); | |
} | |
}); | |
}); | |
/*! | |
* TroopJS widget component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-browser/component/widget',[ "troopjs-core/component/gadget", "jquery", "when", "troopjs-jquery/weave", "troopjs-jquery/action" ], function WidgetModule(Gadget, $, when) { | |
/*jshint strict:false, smarttabs:true, newcap:false */ | |
var UNDEFINED; | |
var NULL = null; | |
var FUNCTION = Function; | |
var ARRAY_PROTO = Array.prototype; | |
var SHIFT = ARRAY_PROTO.shift; | |
var UNSHIFT = ARRAY_PROTO.unshift; | |
var $TRIGGER = $.fn.trigger; | |
var $ONE = $.fn.one; | |
var $BIND = $.fn.bind; | |
var $UNBIND = $.fn.unbind; | |
var RE = /^dom(?::(\w+))?\/([^\.]+(?:\.(.+))?)/; | |
var REFRESH = "widget/refresh"; | |
var $ELEMENT = "$element"; | |
var $PROXIES = "$proxies"; | |
var ONE = "one"; | |
var FEATURES = "features"; | |
var ATTR_WEAVE = "[data-weave]"; | |
var ATTR_WOVEN = "[data-woven]"; | |
/** | |
* Creates a proxy of the inner method 'handlerProxy' with the 'topic', 'widget' and handler parameters set | |
* @param topic event topic | |
* @param widget target widget | |
* @param handler target handler | |
* @returns {Function} proxied handler | |
*/ | |
function eventProxy(topic, widget, handler) { | |
/** | |
* Creates a proxy of the outer method 'handler' that first adds 'topic' to the arguments passed | |
* @returns result of proxied hanlder invocation | |
*/ | |
return function handlerProxy() { | |
// Add topic to front of arguments | |
UNSHIFT.call(arguments, topic); | |
// Apply with shifted arguments to handler | |
return handler.apply(widget, arguments); | |
}; | |
} | |
/** | |
* Creates a proxy of the inner method 'render' with the '$fn' parameter set | |
* @param $fn jQuery method | |
* @returns {Function} proxied render | |
*/ | |
function renderProxy($fn) { | |
/** | |
* Renders contents into element | |
* @param contents (Function | String) Template/String to render | |
* @param data (Object) If contents is a template - template data (optional) | |
* @returns self | |
*/ | |
function render(/* contents, data, ... */) { | |
var self = this; | |
var arg = arguments; | |
// Shift contents from first argument | |
var contents = SHIFT.call(arg); | |
// Call render with contents (or result of contents if it's a function) | |
$fn.call(self[$ELEMENT], contents instanceof FUNCTION ? contents.apply(self, arg) : contents); | |
return self.weave().then(function resolve(widgets) { | |
self.trigger(REFRESH, widgets); | |
return widgets; | |
}); | |
} | |
return render; | |
} | |
return Gadget.extend(function Widget($element, displayName) { | |
var self = this; | |
self[$ELEMENT] = $element; | |
if (displayName) { | |
self.displayName = displayName; | |
} | |
}, { | |
"displayName" : "browser/component/widget", | |
"sig/initialize" : function initialize() { | |
var self = this; | |
var $element = self[$ELEMENT]; | |
var $proxies = self[$PROXIES] = []; | |
var $proxy; | |
var key; | |
var value; | |
var matches; | |
var topic; | |
// Loop over each property in widget | |
for (key in self) { | |
// Get value | |
value = self[key]; | |
// Continue if value is not a function | |
if (!(value instanceof FUNCTION)) { | |
continue; | |
} | |
// Match signature in key | |
matches = RE.exec(key); | |
if (matches !== NULL) { | |
// Get topic | |
topic = matches[2]; | |
// Replace value with a scoped proxy | |
value = eventProxy(topic, self, value); | |
// Either ONE or BIND element | |
(matches[2] === ONE ? $ONE : $BIND).call($element, topic, self, value); | |
// Create and store $proxy | |
$proxies[$proxies.length] = $proxy = [topic, value]; | |
// Store features | |
$proxy[FEATURES] = matches[1]; | |
// NULL value | |
self[key] = NULL; | |
} | |
} | |
}, | |
"sig/finalize" : function finalize() { | |
var self = this; | |
var $element = self[$ELEMENT]; | |
var $proxies = self[$PROXIES]; | |
var $proxy; | |
// Loop over subscriptions | |
while (($proxy = $proxies.shift()) !== UNDEFINED) { | |
$element.unbind($proxy[0], $proxy[1]); | |
} | |
delete self[$ELEMENT]; | |
}, | |
/** | |
* Weaves all children of $element | |
* @returns self | |
*/ | |
"weave" : function weave() { | |
return this[$ELEMENT].find(ATTR_WEAVE).weave(); | |
}, | |
/** | |
* Unweaves all children of $element _and_ self | |
* @returns self | |
*/ | |
"unweave" : function unweave() { | |
return this[$ELEMENT].find(ATTR_WOVEN).addBack().unweave(); | |
}, | |
/** | |
* Binds event from $element, exactly once | |
* @returns self | |
*/ | |
"one" : function one() { | |
var self = this; | |
$ONE.apply(self[$ELEMENT], arguments); | |
return self; | |
}, | |
/** | |
* Binds event to $element | |
* @returns self | |
*/ | |
"bind" : function bind() { | |
var self = this; | |
$BIND.apply(self[$ELEMENT], arguments); | |
return self; | |
}, | |
/** | |
* Unbinds event from $element | |
* @returns self | |
*/ | |
"unbind" : function unbind() { | |
var self = this; | |
$UNBIND.apply(self[$ELEMENT], arguments); | |
return self; | |
}, | |
/** | |
* Triggers event on $element | |
* @returns self | |
*/ | |
"trigger" : function trigger() { | |
var self = this; | |
$TRIGGER.apply(self[$ELEMENT], arguments); | |
return self; | |
}, | |
/** | |
* Renders content and inserts it before $element | |
*/ | |
"before" : renderProxy($.fn.before), | |
/** | |
* Renders content and inserts it after $element | |
*/ | |
"after" : renderProxy($.fn.after), | |
/** | |
* Renders content and replaces $element contents | |
*/ | |
"html" : renderProxy($.fn.html), | |
/** | |
* Renders content and replaces $element contents | |
*/ | |
"text" : renderProxy($.fn.text), | |
/** | |
* Renders content and appends it to $element | |
*/ | |
"append" : renderProxy($.fn.append), | |
/** | |
* Renders content and prepends it to $element | |
*/ | |
"prepend" : renderProxy($.fn.prepend), | |
/** | |
* Empties widget | |
* @returns self | |
*/ | |
"empty" : function empty() { | |
var self = this; | |
// Create deferred | |
var deferred = when.defer(); | |
// Get element | |
var $element = self[$ELEMENT]; | |
// Detach contents | |
var $contents = $element.contents().detach(); | |
// Trigger refresh | |
self.trigger(REFRESH, self); | |
// Use timeout in order to yield | |
setTimeout(function emptyTimeout() { | |
// Get DOM elements | |
var contents = $contents.get(); | |
// Remove elements from DOM | |
$contents.remove(); | |
// Resolve deferred | |
deferred.resolve(contents); | |
}, 0); | |
return deferred.promise; | |
} | |
}); | |
}); | |
/*! | |
* TroopJS dimensions/widget module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-browser/dimensions/widget',[ "../component/widget", "troopjs-jquery/dimensions", "troopjs-jquery/resize" ], function DimensionsModule(Widget) { | |
/*jshint strict:false */ | |
var DIMENSIONS = "dimensions"; | |
function onDimensions($event, w, h) { | |
var self = $event.data; | |
self.publish(self.displayName, w, h, $event); | |
} | |
return Widget.extend(function DimensionsWidget($element, displayName, dimensions) { | |
this[DIMENSIONS] = dimensions; | |
}, { | |
"displayName" : "browser/dimensions/widget", | |
"sig/initialize" : function initialize(signal) { | |
var self = this; | |
self.bind(DIMENSIONS + "." + self[DIMENSIONS], self, onDimensions); | |
}, | |
"sig/start" : function start() { | |
this.trigger("resize." + DIMENSIONS); | |
}, | |
"sig/finalize" : function finalize() { | |
var self = this; | |
self.unbind(DIMENSIONS + "." + self[DIMENSIONS], onDimensions); | |
} | |
}); | |
}); | |
/*! | |
* TroopJS store/base module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-browser/store/base',[ "compose", "troopjs-core/component/gadget", "when" ], function StoreModule(Compose, Gadget, when) { | |
/*jshint strict:false */ | |
var STORAGE = "storage"; | |
return Gadget.extend({ | |
storage : Compose.required, | |
set : function set(key, value) { | |
// JSON encoded 'value' then store as 'key' | |
return when(this[STORAGE].setItem(key, JSON.stringify(value))); | |
}, | |
get : function get(key) { | |
// Get value from 'key', parse JSON | |
return when(JSON.parse(this[STORAGE].getItem(key))); | |
}, | |
remove : function remove(key) { | |
// Remove key | |
return when(this[STORAGE].removeItem(key)); | |
}, | |
clear : function clear() { | |
// Clear | |
return when(this[STORAGE].clear()); | |
} | |
}); | |
}); | |
/*! | |
* TroopJS store/session module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-browser/store/session',[ "compose", "./base" ], function StoreSessionModule(Compose, Store) { | |
/*jshint strict:false */ | |
return Compose.create(Store, { | |
displayName : "browser/store/session", | |
storage: window.sessionStorage | |
}); | |
}); | |
/*! | |
* TroopJS store/local module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-browser/store/local',[ "compose", "./base" ], function StoreLocalModule(Compose, Store) { | |
/*jshint strict:false */ | |
return Compose.create(Store, { | |
displayName : "browser/store/local", | |
storage : window.localStorage | |
}); | |
}); | |
/*! | |
* TroopJS route/widget module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-browser/route/widget',[ "../component/widget", "troopjs-utils/uri", "troopjs-jquery/hashchange" ], function RouteWidgetModule(Widget, URI) { | |
/*jshint strict:false */ | |
var HASHCHANGE = "hashchange"; | |
var ROUTE = "route"; | |
var RE = /^#/; | |
function onHashChange($event) { | |
var self = $event.data; | |
// Create URI | |
var uri = URI($event.target.location.hash.replace(RE, "")); | |
// Convert to string | |
var route = uri.toString(); | |
// Did anything change? | |
if (route !== self[ROUTE]) { | |
// Store new value | |
self[ROUTE] = route; | |
// Publish route | |
self.publish(self.displayName, uri, $event); | |
} | |
} | |
return Widget.extend({ | |
"sig/initialize" : function initialize() { | |
var self = this; | |
self.bind(HASHCHANGE, self, onHashChange); | |
}, | |
"sig/start" : function start() { | |
this.trigger(HASHCHANGE); | |
}, | |
"sig/finalize" : function finalize() { | |
this.unbind(HASHCHANGE, onHashChange); | |
} | |
}); | |
}); | |
/*! | |
* TroopJS widget/application component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-browser/application/widget',[ "module", "../component/widget", "when" ], function ApplicationWidgetModule(module, Widget, when) { | |
/*jshint strict:false, laxbreak:true */ | |
var CHILDREN = "children"; | |
var ARRAY_SLICE = Array.prototype.slice; | |
function forward(signal) { | |
var self = this; | |
var args = arguments; | |
var children = self[CHILDREN]; | |
var length = children ? children.length : 0; | |
var index = 0; | |
function next(_args) { | |
args = _args || args; | |
return length > index | |
? when(children[index++].signal(signal), next) | |
: when.resolve(args); | |
} | |
return next(); | |
} | |
return Widget.extend(function ApplicationWidget($element, name, children) { | |
this[CHILDREN] = children; | |
}, { | |
displayName : "browser/application/widget", | |
"sig/initialize" : forward, | |
"sig/start" : function start() { | |
var self = this; | |
var _weave = self.weave; | |
var args = arguments; | |
return forward.apply(self, args).then(function started() { | |
return _weave.apply(self, ARRAY_SLICE.call(args, 1)); | |
}); | |
}, | |
"sig/stop" : function stop() { | |
var self = this; | |
var _unweave = self.unweave; | |
var args = arguments; | |
return _unweave.apply(self, ARRAY_SLICE.call(args, 1)).then(function stopped() { | |
return forward.apply(self, args); | |
}); | |
}, | |
"sig/finalize" : forward | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* TroopJS Bundle - 1.0.7-42-g27b1122-dirty | |
* http://troopjs.com/ | |
* Copyright (c) 2013 Mikael Karon <[email protected]> | |
* Licensed MIT | |
*/ | |
/*! | |
* TroopJS RequireJS template plug-in | |
* | |
* parts of code from require-cs 0.4.0+ Copyright (c) 2010-2011, The Dojo Foundation | |
* | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false, require:false*/ | |
define('troopjs-requirejs/template',[],function TemplateModule() { | |
/*jshint strict:false, smarttabs:true, laxbreak:true, newcap:false, loopfunc:true */ | |
var FACTORIES = { | |
"node" : function () { | |
// Using special require.nodeRequire, something added by r.js. | |
var fs = require.nodeRequire("fs"); | |
return function fetchText(path, callback) { | |
var file = fs.readFileSync(path, 'utf8'); | |
//Remove BOM (Byte Mark Order) from utf8 files if it is there. | |
if (file.indexOf('\uFEFF') === 0) { | |
file = file.substring(1); | |
} | |
callback(file); | |
}; | |
}, | |
"browser" : function () { | |
// Would love to dump the ActiveX crap in here. Need IE 6 to die first. | |
var progIds = [ "Msxml2.XMLHTTP", "Microsoft.XMLHTTP", "Msxml2.XMLHTTP.4.0"]; | |
var progId; | |
var XHR; | |
var i; | |
if (typeof XMLHttpRequest !== "undefined") { | |
XHR = XMLHttpRequest; | |
} | |
else { | |
for (i = 0; i < 3; i++) { | |
progId = progIds[i]; | |
try { | |
new ActiveXObject(progId); | |
XHR = function(){ | |
return new ActiveXObject(progId); | |
}; | |
break; | |
} | |
catch (e) { | |
} | |
} | |
if (!XHR){ | |
throw new Error("XHR: XMLHttpRequest not available"); | |
} | |
} | |
return function fetchText(url, callback) { | |
var xhr = new XHR(); | |
xhr.open('GET', url, true); | |
xhr.onreadystatechange = function (evt) { | |
// Do not explicitly handle errors, those should be | |
// visible via console output in the browser. | |
if (xhr.readyState === 4) { | |
callback(xhr.responseText); | |
} | |
}; | |
xhr.send(null); | |
}; | |
}, | |
"rhino" : function () { | |
var encoding = "utf-8"; | |
var lineSeparator = java.lang.System.getProperty("line.separator"); | |
// Why Java, why is this so awkward? | |
return function fetchText(path, callback) { | |
var file = new java.io.File(path); | |
var input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)); | |
var stringBuffer = new java.lang.StringBuffer(); | |
var line; | |
var content = ""; | |
try { | |
line = input.readLine(); | |
// Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 | |
// http://www.unicode.org/faq/utf_bom.html | |
// Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: | |
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 | |
if (line && line.length() && line.charAt(0) === 0xfeff) { | |
// Eat the BOM, since we've already found the encoding on this file, | |
// and we plan to concatenating this buffer with others; the BOM should | |
// only appear at the top of a file. | |
line = line.substring(1); | |
} | |
stringBuffer.append(line); | |
while ((line = input.readLine()) !== null) { | |
stringBuffer.append(lineSeparator); | |
stringBuffer.append(line); | |
} | |
// Make sure we return a JavaScript string and not a Java string. | |
content = String(stringBuffer.toString()); // String | |
} finally { | |
input.close(); | |
} | |
callback(content); | |
}; | |
}, | |
"borked" : function () { | |
return function fetchText() { | |
throw new Error("Environment unsupported."); | |
}; | |
} | |
}; | |
var RE_SANITIZE = /^[\n\t\r]+|[\n\t\r]+$/g; | |
var RE_BLOCK = /<%(=)?([\S\s]*?)%>/g; | |
var RE_TOKENS = /<%(\d+)%>/gm; | |
var RE_REPLACE = /(["\n\t\r])/gm; | |
var RE_CLEAN = /o \+= "";| \+ ""/gm; | |
var EMPTY = ""; | |
var REPLACE = { | |
"\"" : "\\\"", | |
"\n" : "\\n", | |
"\t" : "\\t", | |
"\r" : "\\r" | |
}; | |
/** | |
* Compiles template | |
* | |
* @param body Template body | |
* @returns {Function} | |
*/ | |
function compile(body) { | |
var blocks = []; | |
var length = 0; | |
function blocksTokens(original, prefix, block) { | |
blocks[length] = prefix | |
? "\" +" + block + "+ \"" | |
: "\";" + block + "o += \""; | |
return "<%" + String(length++) + "%>"; | |
} | |
function tokensBlocks(original, token) { | |
return blocks[token]; | |
} | |
function replace(original, token) { | |
return REPLACE[token] || token; | |
} | |
return ("function template(data) { var o = \"" | |
// Sanitize body before we start templating | |
+ body.replace(RE_SANITIZE, "") | |
// Replace script blocks with tokens | |
.replace(RE_BLOCK, blocksTokens) | |
// Replace unwanted tokens | |
.replace(RE_REPLACE, replace) | |
// Replace tokens with script blocks | |
.replace(RE_TOKENS, tokensBlocks) | |
+ "\"; return o; }") | |
// Clean | |
.replace(RE_CLEAN, EMPTY); | |
} | |
var buildMap = {}; | |
var fetchText = FACTORIES[ typeof process !== "undefined" && process.versions && !!process.versions.node | |
? "node" | |
: (typeof window !== "undefined" && window.navigator && window.document) || typeof importScripts !== "undefined" | |
? "browser" | |
: typeof Packages !== "undefined" | |
? "rhino" | |
: "borked" ](); | |
return { | |
load: function (name, parentRequire, load, config) { | |
var path = parentRequire.toUrl(name); | |
fetchText(path, function (text) { | |
try { | |
text = "define(function() { return " + compile(text, name, path, config.template) + "; })"; | |
} | |
catch (err) { | |
err.message = "In " + path + ", " + err.message; | |
throw(err); | |
} | |
if (config.isBuild) { | |
buildMap[name] = text; | |
} | |
// IE with conditional comments on cannot handle the | |
// sourceURL trick, so skip it if enabled | |
/*@if (@_jscript) @else @*/ | |
else { | |
text += "\n//@ sourceURL='" + path +"'"; | |
} | |
/*@end@*/ | |
load.fromText(name, text); | |
// Give result to load. Need to wait until the module | |
// is fully parse, which will happen after this | |
// execution. | |
parentRequire([name], function (value) { | |
load(value); | |
}); | |
}); | |
}, | |
write: function (pluginName, name, write) { | |
if (buildMap.hasOwnProperty(name)) { | |
write.asModule(pluginName + "!" + name, buildMap[name]); | |
} | |
} | |
}; | |
}); | |
/*! | |
* TroopJS jQuery hashchange plug-in | |
* | |
* Normalized hashchange event, ripped a _lot_ of code from | |
* https://github.com/millermedeiros/Hasher | |
* | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-jquery/hashchange',[ "jquery" ], function HashchangeModule($) { | |
/*jshint strict:false, smarttabs:true, laxbreak:true, evil:true */ | |
var INTERVAL = "interval"; | |
var HASHCHANGE = "hashchange"; | |
var ONHASHCHANGE = "on" + HASHCHANGE; | |
var RE_HASH = /#(.*)$/; | |
var RE_LOCAL = /\?/; | |
// hack based on this: http://code.google.com/p/closure-compiler/issues/detail?id=47#c13 | |
var _isIE = /**@preserve@cc_on !@*/0; | |
function getHash(window) { | |
// parsed full URL instead of getting location.hash because Firefox | |
// decode hash value (and all the other browsers don't) | |
// also because of IE8 bug with hash query in local file | |
var result = RE_HASH.exec(window.location.href); | |
return result && result[1] | |
? decodeURIComponent(result[1]) | |
: ""; | |
} | |
function Frame(document) { | |
var self = this; | |
var element; | |
self.element = element = document.createElement("iframe"); | |
element.src = "about:blank"; | |
element.style.display = "none"; | |
} | |
Frame.prototype = { | |
getElement : function () { | |
return this.element; | |
}, | |
getHash : function () { | |
return this.element.contentWindow.frameHash; | |
}, | |
update : function (hash) { | |
var self = this; | |
var document = self.element.contentWindow.document; | |
// Quick return if hash has not changed | |
if (self.getHash() === hash) { | |
return; | |
} | |
// update iframe content to force new history record. | |
// based on Really Simple History, SWFAddress and YUI.history. | |
document.open(); | |
document.write("<html><head><title>' + document.title + '</title><script type='text/javascript'>var frameHash='" + hash + "';</script></head><body> </body></html>"); | |
document.close(); | |
} | |
}; | |
$.event.special[HASHCHANGE] = { | |
/** | |
* @param data (Anything) Whatever eventData (optional) was passed in | |
* when binding the event. | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
* @param eventHandle (Function) The actual function that will be bound | |
* to the browser’s native event (this is used internally for the | |
* beforeunload event, you’ll never use it). | |
*/ | |
setup : function hashChangeSetup(data, namespaces, eventHandle) { | |
var window = this; | |
// Quick return if we support onHashChange natively | |
// FF3.6+, IE8+, Chrome 5+, Safari 5+ | |
if (ONHASHCHANGE in window) { | |
return false; | |
} | |
// Make sure we're always a window | |
if (!$.isWindow(window)) { | |
throw new Error("Unable to bind 'hashchange' to a non-window object"); | |
} | |
var $window = $(window); | |
var hash = getHash(window); | |
var location = window.location; | |
$window.data(INTERVAL, window.setInterval(_isIE | |
? (function hashChangeIntervalWrapper() { | |
var document = window.document; | |
var _isLocal = location.protocol === "file:"; | |
var frame = new Frame(document); | |
document.body.appendChild(frame.getElement()); | |
frame.update(hash); | |
return function hashChangeInterval() { | |
var oldHash = hash; | |
var newHash; | |
var windowHash = getHash(window); | |
var frameHash = frame.getHash(); | |
// Detect changes made pressing browser history buttons. | |
// Workaround since history.back() and history.forward() doesn't | |
// update hash value on IE6/7 but updates content of the iframe. | |
if (frameHash !== hash && frameHash !== windowHash) { | |
// Fix IE8 while offline | |
newHash = decodeURIComponent(frameHash); | |
if (hash !== newHash) { | |
hash = newHash; | |
frame.update(hash); | |
$window.trigger(HASHCHANGE, [ newHash, oldHash ]); | |
} | |
// Sync location.hash with frameHash | |
location.hash = "#" + encodeURI(_isLocal | |
? frameHash.replace(RE_LOCAL, "%3F") | |
: frameHash); | |
} | |
// detect if hash changed (manually or using setHash) | |
else if (windowHash !== hash) { | |
// Fix IE8 while offline | |
newHash = decodeURIComponent(windowHash); | |
if (hash !== newHash) { | |
hash = newHash; | |
$window.trigger(HASHCHANGE, [ newHash, oldHash ]); | |
} | |
} | |
}; | |
})() | |
: function hashChangeInterval() { | |
var oldHash = hash; | |
var newHash; | |
var windowHash = getHash(window); | |
if (windowHash !== hash) { | |
// Fix IE8 while offline | |
newHash = decodeURIComponent(windowHash); | |
if (hash !== newHash) { | |
hash = newHash; | |
$window.trigger(HASHCHANGE, [ newHash, oldHash ]); | |
} | |
} | |
}, 25)); | |
}, | |
/** | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
*/ | |
teardown : function hashChangeTeardown(namespaces) { | |
var window = this; | |
// Quick return if we support onHashChange natively | |
if (ONHASHCHANGE in window) { | |
return false; | |
} | |
window.clearInterval($.data(window, INTERVAL)); | |
} | |
}; | |
}); | |
/*! | |
* TroopJS Utils getargs module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-utils/getargs',[],function GetArgsModule() { | |
/*jshint strict:false */ | |
var PUSH = Array.prototype.push; | |
var SUBSTRING = String.prototype.substring; | |
var RE_BOOLEAN = /^(?:false|true)$/i; | |
var RE_BOOLEAN_TRUE = /^true$/i; | |
var RE_DIGIT = /^\d+$/; | |
return function getargs() { | |
var self = this; | |
var result = []; | |
var length; | |
var from; | |
var to; | |
var i; | |
var c; | |
var a; | |
var q = false; | |
// Iterate over string | |
for (from = to = i = 0, length = self.length; i < length; i++) { | |
// Get char | |
c = self.charAt(i); | |
switch(c) { | |
case "\"" : | |
case "'" : | |
// If we are currently quoted... | |
if (q === c) { | |
// Stop quote | |
q = false; | |
// Store result (no need to convert, we know this is a string) | |
PUSH.call(result, SUBSTRING.call(self, from, to)); | |
} | |
// Otherwise | |
else { | |
// Start quote | |
q = c; | |
} | |
// Update from/to | |
from = to = i + 1; | |
break; | |
case "," : | |
// Continue if we're quoted | |
if (q) { | |
to = i + 1; | |
break; | |
} | |
// If we captured something... | |
if (from !== to) { | |
a = SUBSTRING.call(self, from, to); | |
if (RE_BOOLEAN.test(a)) { | |
a = RE_BOOLEAN_TRUE.test(a); | |
} | |
else if (RE_DIGIT.test(a)) { | |
a = +a; | |
} | |
// Store result | |
PUSH.call(result, a); | |
} | |
// Update from/to | |
from = to = i + 1; | |
break; | |
case " " : | |
case "\t" : | |
// Continue if we're quoted | |
if (q) { | |
to = i + 1; | |
break; | |
} | |
// Update from/to | |
if (from === to) { | |
from = to = i + 1; | |
} | |
break; | |
default : | |
// Update to | |
to = i + 1; | |
} | |
} | |
// If we captured something... | |
if (from !== to) { | |
a = SUBSTRING.call(self, from, to); | |
if (RE_BOOLEAN.test(a)) { | |
a = RE_BOOLEAN_TRUE.test(a); | |
} | |
else if (RE_DIGIT.test(a)) { | |
a = +a; | |
} | |
// Store result | |
PUSH.call(result, a); | |
} | |
return result; | |
}; | |
}); | |
/*! | |
* TroopJS jQuery action plug-in | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-jquery/action',[ "jquery", "troopjs-utils/getargs" ], function ActionModule($, getargs) { | |
/*jshint strict:false, smarttabs:true, laxbreak:true */ | |
var UNDEFINED; | |
var FALSE = false; | |
var NULL = null; | |
var SLICE = Array.prototype.slice; | |
var ACTION = "action"; | |
var ORIGINALEVENT = "originalEvent"; | |
var RE_ACTION = /^([\w\d\s_\-\/]+)(?:\.([\w\.]+))?(?:\((.*)\))?$/; | |
var RE_DOT = /\.+/; | |
/** | |
* Namespace iterator | |
* @param namespace (string) namespace | |
* @param index (number) index | |
*/ | |
function namespaceIterator(namespace, index) { | |
return namespace ? namespace + "." + ACTION : NULL; | |
} | |
/** | |
* Action handler | |
* @param $event (jQuery.Event) event | |
*/ | |
function onAction($event) { | |
// Set $target | |
var $target = $(this); | |
// Get argv | |
var argv = SLICE.call(arguments, 1); | |
// Extract type | |
var type = ORIGINALEVENT in $event | |
? $event[ORIGINALEVENT].type | |
: ACTION; | |
// Extract name | |
var name = $event[ACTION]; | |
// Reset $event.type | |
$event.type = ACTION + "/" + name + "." + type; | |
// Trigger 'ACTION/{name}.{type}' | |
$target.trigger($event, argv); | |
// No handler, try without namespace, but exclusive | |
if ($event.result !== FALSE) { | |
// Reset $event.type | |
$event.type = ACTION + "/" + name + "!"; | |
// Trigger 'ACTION/{name}' | |
$target.trigger($event, argv); | |
// Still no handler, try generic action with namespace | |
if ($event.result !== FALSE) { | |
// Reset $event.type | |
$event.type = ACTION + "." + type; | |
// Trigger 'ACTION.{type}' | |
$target.trigger($event, argv); | |
} | |
} | |
} | |
/** | |
* Internal handler | |
* | |
* @param $event jQuery event | |
*/ | |
function handler($event) { | |
// Get closest element that has an action defined | |
var $target = $($event.target).closest("[data-action]"); | |
// Fail fast if there is no action available | |
if ($target.length === 0) { | |
return; | |
} | |
// Extract all data in one go | |
var $data = $target.data(); | |
// Extract matches from 'data-action' | |
var matches = RE_ACTION.exec($data[ACTION]); | |
// Return fast if action parameter was f*cked (no matches) | |
if (matches === NULL) { | |
return; | |
} | |
// Extract action name | |
var name = matches[1]; | |
// Extract action namespaces | |
var namespaces = matches[2]; | |
// Extract action args | |
var args = matches[3]; | |
// If there are action namespaces, make sure we're only triggering action on applicable types | |
if (namespaces !== UNDEFINED && !RegExp(namespaces.split(RE_DOT).join("|")).test($event.type)) { | |
return; | |
} | |
// Split args by separator (if there were args) | |
var argv = args !== UNDEFINED | |
? getargs.call(args) | |
: []; | |
// Iterate argv to determine arg type | |
$.each(argv, function argsIterator(i, value) { | |
if (value in $data) { | |
argv[i] = $data[value]; | |
} | |
}); | |
$target | |
// Trigger exclusive ACTION event | |
.trigger($.Event($event, { | |
type: ACTION + "!", | |
action: name | |
}), argv); | |
// Since we've translated the event, stop propagation | |
$event.stopPropagation(); | |
} | |
$.event.special[ACTION] = { | |
/** | |
* @param data (Anything) Whatever eventData (optional) was passed in | |
* when binding the event. | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
* @param eventHandle (Function) The actual function that will be bound | |
* to the browser’s native event (this is used internally for the | |
* beforeunload event, you’ll never use it). | |
*/ | |
setup : function onActionSetup(data, namespaces, eventHandle) { | |
$(this).bind(ACTION, data, onAction); | |
}, | |
/** | |
* Do something each time an event handler is bound to a particular element | |
* @param handleObj (Object) | |
*/ | |
add : function onActionAdd(handleObj) { | |
var events = $.map(handleObj.namespace.split(RE_DOT), namespaceIterator); | |
if (events.length !== 0) { | |
$(this).bind(events.join(" "), handler); | |
} | |
}, | |
/** | |
* Do something each time an event handler is unbound from a particular element | |
* @param handleObj (Object) | |
*/ | |
remove : function onActionRemove(handleObj) { | |
var events = $.map(handleObj.namespace.split(RE_DOT), namespaceIterator); | |
if (events.length !== 0) { | |
$(this).unbind(events.join(" "), handler); | |
} | |
}, | |
/** | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
*/ | |
teardown : function onActionTeardown(namespaces) { | |
$(this).unbind(ACTION, onAction); | |
} | |
}; | |
$.fn[ACTION] = function action(name) { | |
return $(this).trigger({ | |
type: ACTION + "!", | |
action: name | |
}, SLICE.call(arguments, 1)); | |
}; | |
}); | |
/*! | |
* TroopJS jQuery destroy plug-in | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-jquery/destroy',[ "jquery" ], function DestroyModule($) { | |
/*jshint strict:false, smarttabs:true */ | |
$.event.special.destroy = { | |
remove : function onDestroyRemove(handleObj) { | |
var self = this; | |
handleObj.handler.call(self, $.Event({ | |
"type" : handleObj.type, | |
"data" : handleObj.data, | |
"namespace" : handleObj.namespace, | |
"target" : self | |
})); | |
} | |
}; | |
}); | |
/*! | |
* TroopJS jQuery weave plug-in | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-jquery/weave',[ "require", "jquery", "troopjs-utils/getargs", "./destroy" ], function WeaveModule(parentRequire, $, getargs) { | |
/*jshint strict:false, smarttabs:true, laxbreak:true, loopfunc:true */ | |
var UNDEFINED; | |
var NULL = null; | |
var ARRAY = Array; | |
var ARRAY_PROTO = ARRAY.prototype; | |
var JOIN = ARRAY_PROTO.join; | |
var PUSH = ARRAY_PROTO.push; | |
var $WHEN = $.when; | |
var $DEFERRED = $.Deferred; | |
var WEAVE = "weave"; | |
var UNWEAVE = "unweave"; | |
var WOVEN = "woven"; | |
var WEAVING = "weaving"; | |
var PENDING = "pending"; | |
var DESTROY = "destroy"; | |
var DATA = "data-"; | |
var DATA_WEAVE = DATA + WEAVE; | |
var DATA_WOVEN = DATA + WOVEN; | |
var DATA_WEAVING = DATA + WEAVING; | |
var SELECTOR_WEAVE = "[" + DATA_WEAVE + "]"; | |
var SELECTOR_UNWEAVE = "[" + DATA_WEAVING + "],[" + DATA_WOVEN + "]"; | |
/** | |
* Generic destroy handler. | |
* Simply makes sure that unweave has been called | |
*/ | |
function onDestroy() { | |
$(this).unweave(); | |
} | |
$.expr[":"][WEAVE] = $.expr.createPseudo | |
? $.expr.createPseudo(function (widgets) { | |
if (widgets !== UNDEFINED) { | |
widgets = RegExp($.map(getargs.call(widgets), function (widget) { | |
return "^" + widget + "$"; | |
}).join("|"), "m"); | |
} | |
return function (element) { | |
var weave = $(element).attr(DATA_WEAVE); | |
return weave === UNDEFINED | |
? false | |
: widgets === UNDEFINED | |
? true | |
: widgets.test(weave.split(/[\s,]+/).join("\n")); | |
}; | |
}) | |
: function (element, index, match) { | |
var weave = $(element).attr(DATA_WEAVE); | |
return weave === UNDEFINED | |
? false | |
: match === UNDEFINED | |
? true | |
: RegExp($.map(getargs.call(match[3]), function (widget) { | |
return "^" + widget + "$"; | |
}).join("|"), "m").test(weave.split(/[\s,]+/).join("\n")); | |
}; | |
$.expr[":"][WOVEN] = $.expr.createPseudo | |
? $.expr.createPseudo(function (widgets) { | |
if (widgets !== UNDEFINED) { | |
widgets = RegExp($.map(getargs.call(widgets), function (widget) { | |
return "^" + widget + "@\\d+"; | |
}).join("|"), "m"); | |
} | |
return function (element) { | |
var woven = $(element).attr(DATA_WOVEN); | |
return woven === UNDEFINED | |
? false | |
: widgets === UNDEFINED | |
? true | |
: widgets.test(woven.split(/[\s,]+/).join("\n")); | |
}; | |
}) | |
: function (element, index, match) { | |
var woven = $(element).attr(DATA_WOVEN); | |
return woven === UNDEFINED | |
? false | |
: match === UNDEFINED | |
? true | |
: RegExp($.map(getargs.call(match[3]), function (widget) { | |
return "^" + widget + "@\\d+"; | |
}).join("|"), "m").test(woven.split(/[\s,]+/).join("\n")); | |
}; | |
$.fn[WEAVE] = function weave(/* arg, arg, arg */) { | |
var widgets = []; | |
var i = 0; | |
var $elements = $(this); | |
var arg = arguments; | |
$elements | |
// Reduce to only elements that can be woven | |
.filter(SELECTOR_WEAVE) | |
// Iterate | |
.each(function elementIterator(index, element) { | |
// Defer weave | |
$DEFERRED(function deferredWeave(dfdWeave) { | |
var $element = $(element); | |
var $data = $element.data(); | |
var weave = $data[WEAVE] = $element.attr(DATA_WEAVE) || ""; | |
var woven = $data[WOVEN] || ($data[WOVEN] = []); | |
var pending = $data[PENDING] || ($data[PENDING] = []); | |
// Link deferred | |
dfdWeave.done(function doneWeave() { | |
$element | |
// Remove DATA_WEAVING | |
.removeAttr(DATA_WEAVING) | |
// Set DATA_WOVEN with full names | |
.attr(DATA_WOVEN, JOIN.call(arguments, " ")); | |
}); | |
// Wait for all pending deferred | |
$WHEN.apply($, pending).then(function donePending() { | |
var re = /[\s,]*([\w_\-\/\.]+)(?:\(([^\)]+)\))?/g; | |
var mark = i; | |
var j = 0; | |
var matches; | |
// Push dfdWeave on pending to signify we're starting a new task | |
PUSH.call(pending, dfdWeave); | |
$element | |
// Make sure to remove DATA_WEAVE (so we don't try processing this again) | |
.removeAttr(DATA_WEAVE) | |
// Set DATA_WEAVING (so that unweave can pick this up) | |
.attr(DATA_WEAVING, weave) | |
// Bind destroy event | |
.bind(DESTROY, onDestroy); | |
// Iterate woven (while RE_WEAVE matches) | |
while ((matches = re.exec(weave)) !== NULL) { | |
// Defer widget | |
$DEFERRED(function deferredWidget(dfdWidget) { | |
var _j = j++; // store _j before we increment | |
var k; | |
var l; | |
var kMax; | |
var value; | |
// Add to widgets | |
widgets[i++] = dfdWidget; | |
// Link deferred | |
dfdWidget.then(function doneWidget(widget) { | |
woven[_j] = widget; | |
}, dfdWeave.reject, dfdWeave.notify); | |
// Get widget name | |
var name = matches[1]; | |
// Set initial argv | |
var argv = [ $element, name ]; | |
// Append values from arg to argv | |
for (k = 0, kMax = arg.length, l = argv.length; k < kMax; k++, l++) { | |
argv[l] = arg[k]; | |
} | |
// Get widget args | |
var args = matches[2]; | |
// Any widget arguments | |
if (args !== UNDEFINED) { | |
// Convert args using getargs | |
args = getargs.call(args); | |
// Append typed values from args to argv | |
for (k = 0, kMax = args.length, l = argv.length; k < kMax; k++, l++) { | |
// Get value | |
value = args[k]; | |
// Get value from $data or fall back to pure value | |
argv[l] = value in $data | |
? $data[value] | |
: value; | |
} | |
} | |
// Require module | |
parentRequire([ name ], function required(Widget) { | |
// Instantiate widget (with argv) | |
var widget = Widget.apply(Widget, argv); | |
// Start widget | |
widget.start().then(function resolve() { | |
dfdWidget.resolve(widget); | |
}, dfdWidget.reject, dfdWidget.notify); | |
}); | |
}); | |
} | |
// Slice out widgets woven for this element | |
$WHEN.apply($, widgets.slice(mark, i)).then(dfdWeave.resolve, dfdWeave.reject, dfdWeave.notify); | |
}, dfdWeave.reject, dfdWeave.notify); | |
}); | |
}); | |
// Return compacted combined promise | |
return $DEFERRED(function deferredWeave(dfdWeave) { | |
$WHEN.apply($, widgets).then(function resolve() { | |
dfdWeave.resolve(arguments); | |
}, dfdWeave.reject, dfdWeave.progress); | |
}).promise(); | |
}; | |
$.fn[UNWEAVE] = function unweave() { | |
var widgets = []; | |
var i = 0; | |
var $elements = $(this); | |
$elements | |
// Reduce to only elements that can be unwoven | |
.filter(SELECTOR_UNWEAVE) | |
// Iterate | |
.each(function elementIterator(index, element) { | |
// Defer unweave | |
$DEFERRED(function deferredUnweave(dfdUnweave) { | |
var $element = $(element); | |
var $data = $element.data(); | |
var pending = $data[PENDING] || ($data[PENDING] = []); | |
var woven = $data[WOVEN] || []; | |
// Link deferred | |
dfdUnweave.done(function doneUnweave() { | |
$element | |
// Copy weave data to data-weave attribute | |
.attr(DATA_WEAVE, $data[WEAVE]) | |
// Make sure to clean the destroy event handler | |
.unbind(DESTROY, onDestroy); | |
// Remove data fore WEAVE | |
delete $data[WEAVE]; | |
}); | |
// Wait for all pending deferred | |
$WHEN.apply($, pending).done(function donePending() { | |
var mark = i; | |
var widget; | |
// Push dfdUnweave on pending to signify we're starting a new task | |
PUSH.call(pending, dfdUnweave); | |
// Remove WOVEN data | |
delete $data[WOVEN]; | |
$element | |
// Remove DATA_WOVEN attribute | |
.removeAttr(DATA_WOVEN); | |
// Somewhat safe(r) iterator over woven | |
while ((widget = woven.shift()) !== UNDEFINED) { | |
// Defer widget | |
$DEFERRED(function deferredWidget(dfdWidget) { | |
// Add to unwoven and pending | |
widgets[i++] = widget.stop().then(function resolve() { | |
dfdWidget.resolve(widget); | |
}, dfdWidget.reject, dfdWidget.notify); | |
}); | |
} | |
// Slice out widgets unwoven for this element | |
$WHEN.apply($, widgets.slice(mark, i)).then(dfdUnweave.resolve, dfdUnweave.reject, dfdUnweave.notify); | |
}); | |
}); | |
}); | |
// Return compacted combined promise | |
return $DEFERRED(function deferredUnweave(dfdUnweave) { | |
$WHEN.apply($, widgets).then(function resolve() { | |
dfdUnweave.resolve(arguments); | |
}, dfdUnweave.reject, dfdUnweave.progress); | |
}).promise(); | |
}; | |
$.fn[WOVEN] = function woven(/* arg, arg */) { | |
var result = []; | |
var widgets = arguments.length > 0 | |
? RegExp($.map(arguments, function (widget) { | |
return "^" + widget + "$"; | |
}).join("|"), "m") | |
: UNDEFINED; | |
$(this).each(function elementIterator(index, element) { | |
if (!$.hasData(element)) { | |
return; | |
} | |
PUSH.apply(result, widgets === UNDEFINED | |
? $.data(element, WOVEN) | |
: $.map($.data(element, WOVEN), function (woven) { | |
return widgets.test(woven.displayName) | |
? woven | |
: UNDEFINED; | |
})); | |
}); | |
return result; | |
}; | |
}); | |
/*! | |
* TroopJS jQuery dimensions plug-in | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-jquery/dimensions',[ "jquery" ], function DimensionsModule($) { | |
/*jshint strict:false, smarttabs:true */ | |
var NULL = null; | |
var DIMENSIONS = "dimensions"; | |
var RESIZE = "resize." + DIMENSIONS; | |
var W = "w"; | |
var H = "h"; | |
var _W = "_" + W; | |
var _H = "_" + H; | |
/** | |
* Internal comparator used for reverse sorting arrays | |
*/ | |
function reverse(a, b) { | |
return b - a; | |
} | |
/** | |
* Internal onResize handler | |
* @param $event | |
*/ | |
function onResize($event) { | |
var self = this; | |
var $self = $(self); | |
var width = $self.width(); | |
var height = $self.height(); | |
// Iterate all dimensions | |
$.each($.data(self, DIMENSIONS), function dimensionIterator(namespace, dimension) { | |
var w = dimension[W]; | |
var h = dimension[H]; | |
var _w; | |
var _h; | |
var i; | |
i = w.length; | |
_w = w[i - 1]; | |
while(w[--i] < width) { | |
_w = w[i]; | |
} | |
i = h.length; | |
_h = h[i - 1]; | |
while(h[--i] < height) { | |
_h = h[i]; | |
} | |
// If _w or _h has changed, update and trigger | |
if (_w !== dimension[_W] || _h !== dimension[_H]) { | |
dimension[_W] = _w; | |
dimension[_H] = _h; | |
$self.trigger(DIMENSIONS + "." + namespace, [ _w, _h ]); | |
} | |
}); | |
} | |
$.event.special[DIMENSIONS] = { | |
/** | |
* @param data (Anything) Whatever eventData (optional) was passed in | |
* when binding the event. | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
* @param eventHandle (Function) The actual function that will be bound | |
* to the browser’s native event (this is used internally for the | |
* beforeunload event, you’ll never use it). | |
*/ | |
setup : function onDimensionsSetup(data, namespaces, eventHandle) { | |
$(this) | |
.bind(RESIZE, onResize) | |
.data(DIMENSIONS, {}); | |
}, | |
/** | |
* Do something each time an event handler is bound to a particular element | |
* @param handleObj (Object) | |
*/ | |
add : function onDimensionsAdd(handleObj) { | |
var self = this; | |
var namespace = handleObj.namespace; | |
var dimension = {}; | |
var w = dimension[W] = []; | |
var h = dimension[H] = []; | |
var re = /(w|h)(\d+)/g; | |
var matches; | |
while ((matches = re.exec(namespace)) !== NULL) { | |
dimension[matches[1]].push(parseInt(matches[2], 10)); | |
} | |
w.sort(reverse); | |
h.sort(reverse); | |
$.data(self, DIMENSIONS)[namespace] = dimension; | |
}, | |
/** | |
* Do something each time an event handler is unbound from a particular element | |
* @param handleObj (Object) | |
*/ | |
remove : function onDimensionsRemove(handleObj) { | |
delete $.data(this, DIMENSIONS)[handleObj.namespace]; | |
}, | |
/** | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
*/ | |
teardown : function onDimensionsTeardown(namespaces) { | |
$(this) | |
.removeData(DIMENSIONS) | |
.unbind(RESIZE, onResize); | |
} | |
}; | |
}); | |
/*! | |
* TroopJS jQuery resize plug-in | |
* | |
* Heavy inspiration from https://github.com/cowboy/jquery-resize.git | |
* | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-jquery/resize',[ "jquery" ], function ResizeModule($) { | |
/*jshint strict:false, smarttabs:true */ | |
var NULL = null; | |
var RESIZE = "resize"; | |
var W = "w"; | |
var H = "h"; | |
var $ELEMENTS = $([]); | |
var INTERVAL = NULL; | |
/** | |
* Iterator | |
* @param index | |
* @param self | |
*/ | |
function iterator(index, self) { | |
// Get data | |
var $data = $.data(self); | |
// Get reference to $self | |
var $self = $(self); | |
// Get previous width and height | |
var w = $self.width(); | |
var h = $self.height(); | |
// Check if width or height has changed since last check | |
if (w !== $data[W] || h !== $data[H]) { | |
$self.trigger(RESIZE, [$data[W] = w, $data[H] = h]); | |
} | |
} | |
/** | |
* Internal interval | |
*/ | |
function interval() { | |
$ELEMENTS.each(iterator); | |
} | |
$.event.special[RESIZE] = { | |
/** | |
* @param data (Anything) Whatever eventData (optional) was passed in | |
* when binding the event. | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
* @param eventHandle (Function) The actual function that will be bound | |
* to the browser’s native event (this is used internally for the | |
* beforeunload event, you’ll never use it). | |
*/ | |
setup : function onResizeSetup(data, namespaces, eventHandle) { | |
var self = this; | |
// window has a native resize event, exit fast | |
if ($.isWindow(self)) { | |
return false; | |
} | |
// Store data | |
var $data = $.data(self, RESIZE, {}); | |
// Get reference to $self | |
var $self = $(self); | |
// Initialize data | |
$data[W] = $self.width(); | |
$data[H] = $self.height(); | |
// Add to tracked collection | |
$ELEMENTS = $ELEMENTS.add(self); | |
// If this is the first element, start interval | |
if($ELEMENTS.length === 1) { | |
INTERVAL = setInterval(interval, 100); | |
} | |
}, | |
/** | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
*/ | |
teardown : function onResizeTeardown(namespaces) { | |
var self = this; | |
// window has a native resize event, exit fast | |
if ($.isWindow(self)) { | |
return false; | |
} | |
// Remove data | |
$.removeData(self, RESIZE); | |
// Remove from tracked collection | |
$ELEMENTS = $ELEMENTS.not(self); | |
// If this is the last element, stop interval | |
if($ELEMENTS.length === 0 && INTERVAL !== NULL) { | |
clearInterval(INTERVAL); | |
} | |
} | |
}; | |
}); | |
/*! | |
* TroopJS Utils merge module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-utils/merge',[],function MergeModule() { | |
/*jshint strict:false */ | |
var ARRAY = Array; | |
var OBJECT = Object; | |
return function merge(source) { | |
var target = this; | |
var key = null; | |
var i; | |
var iMax; | |
var value; | |
var constructor; | |
for (i = 0, iMax = arguments.length; i < iMax; i++) { | |
source = arguments[i]; | |
for (key in source) { | |
value = source[key]; | |
constructor = value.constructor; | |
if (!(key in target)) { | |
target[key] = value; | |
} | |
else if (constructor === ARRAY) { | |
target[key] = target[key].concat(value); | |
} | |
else if (constructor === OBJECT) { | |
merge.call(target[key], value); | |
} | |
else { | |
target[key] = value; | |
} | |
} | |
} | |
return target; | |
}; | |
}); | |
/*! | |
* TroopJS Utils tr component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-utils/tr',[],function TrModule() { | |
/*jshint strict:false */ | |
var TYPEOF_NUMBER = typeof Number(); | |
return function tr(callback) { | |
var self = this; | |
var result = []; | |
var i; | |
var length = self.length; | |
var key; | |
// Is this an array? Basically, is length a number, is it 0 or is it greater than 0 and that we have index 0 and index length-1 | |
if (typeof length === TYPEOF_NUMBER && length === 0 || length > 0 && 0 in self && length - 1 in self) { | |
for (i = 0; i < length; i++) { | |
result.push(callback.call(self, self[i], i)); | |
} | |
// Otherwise we'll iterate it as an object | |
} else if (self){ | |
for (key in self) { | |
result.push(callback.call(self, self[key], key)); | |
} | |
} | |
return result; | |
}; | |
}); | |
/* | |
* ComposeJS, object composition for JavaScript, featuring | |
* JavaScript-style prototype inheritance and composition, multiple inheritance, | |
* mixin and traits-inspired conflict resolution and composition | |
*/ | |
(function(define){ | |
define('compose/compose',[], function(){ | |
// function for creating instances from a prototype | |
function Create(){ | |
} | |
var delegate = Object.create ? | |
function(proto){ | |
return Object.create(typeof proto == "function" ? proto.prototype : proto || Object.prototype); | |
} : | |
function(proto){ | |
Create.prototype = typeof proto == "function" ? proto.prototype : proto; | |
var instance = new Create(); | |
Create.prototype = null; | |
return instance; | |
}; | |
function validArg(arg){ | |
if(!arg){ | |
throw new Error("Compose arguments must be functions or objects"); | |
} | |
return arg; | |
} | |
// this does the work of combining mixins/prototypes | |
function mixin(instance, args, i){ | |
// use prototype inheritance for first arg | |
var value, argsLength = args.length; | |
for(; i < argsLength; i++){ | |
var arg = args[i]; | |
if(typeof arg == "function"){ | |
// the arg is a function, use the prototype for the properties | |
var prototype = arg.prototype; | |
for(var key in prototype){ | |
value = prototype[key]; | |
var own = prototype.hasOwnProperty(key); | |
if(typeof value == "function" && key in instance && value !== instance[key]){ | |
var existing = instance[key]; | |
if(value == required){ | |
// it is a required value, and we have satisfied it | |
value = existing; | |
} | |
else if(!own){ | |
// if it is own property, it is considered an explicit override | |
// TODO: make faster calls on this, perhaps passing indices and caching | |
if(isInMethodChain(value, key, getBases([].slice.call(args, 0, i), true))){ | |
// this value is in the existing method's override chain, we can use the existing method | |
value = existing; | |
}else if(!isInMethodChain(existing, key, getBases([arg], true))){ | |
// the existing method is not in the current override chain, so we are left with a conflict | |
console.error("Conflicted method " + key + ", final composer must explicitly override with correct method."); | |
} | |
} | |
} | |
if(value && value.install && own && !isInMethodChain(existing, key, getBases([arg], true))){ | |
// apply modifier | |
value.install.call(instance, key); | |
}else{ | |
instance[key] = value; | |
} | |
} | |
}else{ | |
// it is an object, copy properties, looking for modifiers | |
for(var key in validArg(arg)){ | |
var value = arg[key]; | |
if(typeof value == "function"){ | |
if(value.install){ | |
// apply modifier | |
value.install.call(instance, key); | |
continue; | |
} | |
if(key in instance){ | |
if(value == required){ | |
// required requirement met | |
continue; | |
} | |
} | |
} | |
// add it to the instance | |
instance[key] = value; | |
} | |
} | |
} | |
return instance; | |
} | |
// allow for override (by es5 module) | |
Compose._setMixin = function(newMixin){ | |
mixin = newMixin; | |
}; | |
function isInMethodChain(method, name, prototypes){ | |
// searches for a method in the given prototype hierarchy | |
for(var i = 0; i < prototypes.length;i++){ | |
var prototype = prototypes[i]; | |
if(prototype[name] == method){ | |
// found it | |
return true; | |
} | |
} | |
} | |
// Decorator branding | |
function Decorator(install, direct){ | |
function Decorator(){ | |
if(direct){ | |
return direct.apply(this, arguments); | |
} | |
throw new Error("Decorator not applied"); | |
} | |
Decorator.install = install; | |
return Decorator; | |
} | |
Compose.Decorator = Decorator; | |
// aspect applier | |
function aspect(handler){ | |
return function(advice){ | |
return Decorator(function install(key){ | |
var baseMethod = this[key]; | |
(advice = this[key] = baseMethod ? handler(this, baseMethod, advice) : advice).install = install; | |
}, advice); | |
}; | |
}; | |
// around advice, useful for calling super methods too | |
Compose.around = aspect(function(target, base, advice){ | |
return advice.call(target, base); | |
}); | |
Compose.before = aspect(function(target, base, advice){ | |
return function(){ | |
var results = advice.apply(this, arguments); | |
if(results !== stop){ | |
return base.apply(this, results || arguments); | |
} | |
}; | |
}); | |
var stop = Compose.stop = {}; | |
var undefined; | |
Compose.after = aspect(function(target, base, advice){ | |
return function(){ | |
var results = base.apply(this, arguments); | |
var adviceResults = advice.apply(this, arguments); | |
return adviceResults === undefined ? results : adviceResults; | |
}; | |
}); | |
// rename Decorator for calling super methods | |
Compose.from = function(trait, fromKey){ | |
if(fromKey){ | |
return (typeof trait == "function" ? trait.prototype : trait)[fromKey]; | |
} | |
return Decorator(function(key){ | |
if(!(this[key] = (typeof trait == "string" ? this[trait] : | |
(typeof trait == "function" ? trait.prototype : trait)[fromKey || key]))){ | |
throw new Error("Source method " + fromKey + " was not available to be renamed to " + key); | |
} | |
}); | |
}; | |
// Composes an instance | |
Compose.create = function(base){ | |
// create the instance | |
var instance = mixin(delegate(base), arguments, 1); | |
var argsLength = arguments.length; | |
// for go through the arguments and call the constructors (with no args) | |
for(var i = 0; i < argsLength; i++){ | |
var arg = arguments[i]; | |
if(typeof arg == "function"){ | |
instance = arg.call(instance) || instance; | |
} | |
} | |
return instance; | |
} | |
// The required function, just throws an error if not overriden | |
function required(){ | |
throw new Error("This method is required and no implementation has been provided"); | |
}; | |
Compose.required = required; | |
// get the value of |this| for direct function calls for this mode (strict in ES5) | |
function extend(){ | |
var args = [this]; | |
args.push.apply(args, arguments); | |
return Compose.apply(0, args); | |
} | |
// Compose a constructor | |
function Compose(base){ | |
var args = arguments; | |
var prototype = (args.length < 2 && typeof args[0] != "function") ? | |
args[0] : // if there is just a single argument object, just use that as the prototype | |
mixin(delegate(validArg(base)), args, 1); // normally create a delegate to start with | |
function Constructor(){ | |
var instance; | |
if(this instanceof Constructor){ | |
// called with new operator, can proceed as is | |
instance = this; | |
}else{ | |
// we allow for direct calls without a new operator, in this case we need to | |
// create the instance ourself. | |
Create.prototype = prototype; | |
instance = new Create(); | |
} | |
// call all the constructors with the given arguments | |
for(var i = 0; i < constructorsLength; i++){ | |
var constructor = constructors[i]; | |
var result = constructor.apply(instance, arguments); | |
if(typeof result == "object"){ | |
if(result instanceof Constructor){ | |
instance = result; | |
}else{ | |
for(var j in result){ | |
if(result.hasOwnProperty(j)){ | |
instance[j] = result[j]; | |
} | |
} | |
} | |
} | |
} | |
return instance; | |
} | |
// create a function that can retrieve the bases (constructors or prototypes) | |
Constructor._getBases = function(prototype){ | |
return prototype ? prototypes : constructors; | |
}; | |
// now get the prototypes and the constructors | |
var constructors = getBases(args), | |
constructorsLength = constructors.length; | |
if(typeof args[args.length - 1] == "object"){ | |
args[args.length - 1] = prototype; | |
} | |
var prototypes = getBases(args, true); | |
Constructor.extend = extend; | |
if(!Compose.secure){ | |
prototype.constructor = Constructor; | |
} | |
Constructor.prototype = prototype; | |
return Constructor; | |
}; | |
Compose.apply = function(thisObject, args){ | |
// apply to the target | |
return thisObject ? | |
mixin(thisObject, args, 0) : // called with a target object, apply the supplied arguments as mixins to the target object | |
extend.apply.call(Compose, 0, args); // get the Function.prototype apply function, call() it to apply arguments to Compose (the extend doesn't matter, just a handle way to grab apply, since we can't get it off of Compose) | |
}; | |
Compose.call = function(thisObject){ | |
// call() should correspond with apply behavior | |
return mixin(thisObject, arguments, 1); | |
}; | |
function getBases(args, prototype){ | |
// this function registers a set of constructors for a class, eliminating duplicate | |
// constructors that may result from diamond construction for classes (B->A, C->A, D->B&C, then D() should only call A() once) | |
var bases = []; | |
function iterate(args, checkChildren){ | |
outer: | |
for(var i = 0; i < args.length; i++){ | |
var arg = args[i]; | |
var target = prototype && typeof arg == "function" ? | |
arg.prototype : arg; | |
if(prototype || typeof arg == "function"){ | |
var argGetBases = checkChildren && arg._getBases; | |
if(argGetBases){ | |
iterate(argGetBases(prototype)); // don't need to check children for these, this should be pre-flattened | |
}else{ | |
for(var j = 0; j < bases.length; j++){ | |
if(target == bases[j]){ | |
continue outer; | |
} | |
} | |
bases.push(target); | |
} | |
} | |
} | |
} | |
iterate(args, true); | |
return bases; | |
} | |
// returning the export of the module | |
return Compose; | |
}); | |
})(typeof define != "undefined" ? | |
define: // AMD/RequireJS format if available | |
function(deps, factory){ | |
if(typeof module !="undefined"){ | |
module.exports = factory(); // CommonJS environment, like NodeJS | |
// require("./configure"); | |
}else{ | |
Compose = factory(); // raw script, assign to Compose global | |
} | |
}); | |
define('compose', ['compose/compose'], function (main) { return main; }); | |
/*! | |
* TroopJS Utils URI module | |
* | |
* parts of code from parseUri 1.2.2 Copyright Steven Levithan <stevenlevithan.com> | |
* | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-utils/uri',[ "compose" ], function URIModule(Compose) { | |
/*jshint strict:false, smarttabs:true, laxbreak:true, newcap:false, forin:false, loopfunc:true */ | |
var NULL = null; | |
var ARRAY_PROTO = Array.prototype; | |
var OBJECT_PROTO = Object.prototype; | |
var PUSH = ARRAY_PROTO.push; | |
var SPLIT = String.prototype.split; | |
var TOSTRING = OBJECT_PROTO.toString; | |
var TOSTRING_OBJECT = TOSTRING.call(OBJECT_PROTO); | |
var TOSTRING_ARRAY = TOSTRING.call(ARRAY_PROTO); | |
var TOSTRING_FUNCTION = TOSTRING.call(Function.prototype); | |
var RE_URI = /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?(?:([^?#]*)(?:\?([^#]*))?(?:#(.*))?)/; | |
var PROTOCOL = "protocol"; | |
var AUTHORITY = "authority"; | |
var PATH = "path"; | |
var QUERY = "query"; | |
var ANCHOR = "anchor"; | |
var KEYS = [ "source", | |
PROTOCOL, | |
AUTHORITY, | |
"userInfo", | |
"user", | |
"password", | |
"host", | |
"port", | |
PATH, | |
QUERY, | |
ANCHOR ]; | |
// Store current Compose.secure setting | |
var SECURE = Compose.secure; | |
// Prevent Compose from creating constructor property | |
Compose.secure = true; | |
function Query(arg) { | |
var result = {}; | |
var matches; | |
var key = NULL; | |
var value; | |
var re = /(?:&|^)([^&=]*)=?([^&]*)/g; | |
result.toString = Query.toString; | |
if (TOSTRING.call(arg) === TOSTRING_OBJECT) { | |
for (key in arg) { | |
result[key] = arg[key]; | |
} | |
} else { | |
while ((matches = re.exec(arg)) !== NULL) { | |
key = matches[1]; | |
if (key in result) { | |
value = result[key]; | |
if (TOSTRING.call(value) === TOSTRING_ARRAY) { | |
value[value.length] = matches[2]; | |
} | |
else { | |
result[key] = [ value, matches[2] ]; | |
} | |
} | |
else { | |
result[key] = matches[2]; | |
} | |
} | |
} | |
return result; | |
} | |
Query.toString = function toString() { | |
var self = this; | |
var key; | |
var value; | |
var values; | |
var query = []; | |
var i = 0; | |
var j; | |
for (key in self) { | |
if (TOSTRING.call(self[key]) === TOSTRING_FUNCTION) { | |
continue; | |
} | |
query[i++] = key; | |
} | |
query.sort(); | |
while (i--) { | |
key = query[i]; | |
value = self[key]; | |
if (TOSTRING.call(value) === TOSTRING_ARRAY) { | |
values = value.slice(0); | |
values.sort(); | |
j = values.length; | |
while (j--) { | |
value = values[j]; | |
values[j] = value === "" | |
? key | |
: key + "=" + value; | |
} | |
query[i] = values.join("&"); | |
} | |
else { | |
query[i] = value === "" | |
? key | |
: key + "=" + value; | |
} | |
} | |
return query.join("&"); | |
}; | |
// Extend on the instance of array rather than subclass it | |
function Path(arg) { | |
var result = []; | |
result.toString = Path.toString; | |
PUSH.apply(result, TOSTRING.call(arg) === TOSTRING_ARRAY | |
? arg | |
: SPLIT.call(arg, "/")); | |
return result; | |
} | |
Path.toString = function() { | |
return this.join("/"); | |
}; | |
var URI = Compose(function URI(str) { | |
var self = this; | |
var value; | |
var matches; | |
var i; | |
if ((matches = RE_URI.exec(str)) !== NULL) { | |
i = matches.length; | |
while (i--) { | |
value = matches[i]; | |
if (value) { | |
self[KEYS[i]] = value; | |
} | |
} | |
} | |
if (QUERY in self) { | |
self[QUERY] = Query(self[QUERY]); | |
} | |
if (PATH in self) { | |
self[PATH] = Path(self[PATH]); | |
} | |
}); | |
URI.prototype.toString = function () { | |
var self = this; | |
var uri = [ PROTOCOL , "://", AUTHORITY, PATH, "?", QUERY, "#", ANCHOR ]; | |
var i; | |
var key; | |
if (!(PROTOCOL in self)) { | |
uri[0] = uri[1] = ""; | |
} | |
if (!(AUTHORITY in self)) { | |
uri[2] = ""; | |
} | |
if (!(PATH in self)) { | |
uri[3] = ""; | |
} | |
if (!(QUERY in self)) { | |
uri[4] = uri[5] = ""; | |
} | |
if (!(ANCHOR in self)) { | |
uri[6] = uri[7] = ""; | |
} | |
i = uri.length; | |
while (i--) { | |
key = uri[i]; | |
if (key in self) { | |
uri[i] = self[key]; | |
} | |
} | |
return uri.join(""); | |
}; | |
// Restore Compose.secure setting | |
Compose.secure = SECURE; | |
URI.Path = Path; | |
URI.Query = Query; | |
return URI; | |
}); | |
/*! | |
* TroopJS Utils unique component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-utils/unique',[],function UniqueModule() { | |
/*jshint strict:false */ | |
return function unique(callback) { | |
var self = this; | |
var length = self.length; | |
var result = []; | |
var value; | |
var i; | |
var j; | |
var k; | |
add: for (i = j = k = 0; i < length; i++, j = 0) { | |
value = self[i]; | |
while(j < k) { | |
if (callback.call(self, value, result[j++]) === true) { | |
continue add; | |
} | |
} | |
result[k++] = value; | |
} | |
return result; | |
}; | |
}); | |
/** @license MIT License (c) copyright B Cavalier & J Hann */ | |
/** | |
* A lightweight CommonJS Promises/A and when() implementation | |
* when 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 | |
* | |
* @version 1.7.1 | |
*/ | |
(function(define) { | |
define('when/when',[],function () { | |
var reduceArray, slice, undef; | |
// | |
// Public API | |
// | |
when.defer = defer; // Create a deferred | |
when.resolve = resolve; // Create a resolved promise | |
when.reject = reject; // Create a rejected promise | |
when.join = join; // Join 2 or more promises | |
when.all = all; // Resolve a list of promises | |
when.map = map; // Array.map() for promises | |
when.reduce = reduce; // Array.reduce() for promises | |
when.any = any; // One-winner race | |
when.some = some; // Multi-winner race | |
when.chain = chain; // Make a promise trigger another resolver | |
when.isPromise = isPromise; // Determine if a thing is a promise | |
/** | |
* Register an observer for a promise or immediate value. | |
* | |
* @param {*} promiseOrValue | |
* @param {function?} [onFulfilled] callback to be called when promiseOrValue is | |
* successfully fulfilled. If promiseOrValue is an immediate value, callback | |
* will be invoked immediately. | |
* @param {function?} [onRejected] callback to be called when promiseOrValue is | |
* rejected. | |
* @param {function?} [onProgress] callback to be called when progress updates | |
* are issued for promiseOrValue. | |
* @returns {Promise} a new {@link Promise} that will complete with the return | |
* value of callback or errback or the completion value of promiseOrValue if | |
* callback and/or errback is not supplied. | |
*/ | |
function when(promiseOrValue, onFulfilled, onRejected, onProgress) { | |
// Get a trusted promise for the input promiseOrValue, and then | |
// register promise handlers | |
return resolve(promiseOrValue).then(onFulfilled, onRejected, onProgress); | |
} | |
/** | |
* Returns promiseOrValue if promiseOrValue is a {@link Promise}, a new Promise if | |
* promiseOrValue is a foreign promise, or a new, already-fulfilled {@link Promise} | |
* whose value is promiseOrValue if promiseOrValue is an immediate value. | |
* | |
* @param {*} promiseOrValue | |
* @returns Guaranteed to return a trusted Promise. If promiseOrValue is a when.js {@link Promise} | |
* returns promiseOrValue, otherwise, returns a new, already-resolved, when.js {@link Promise} | |
* whose resolution value is: | |
* * the resolution value of promiseOrValue if it's a foreign promise, or | |
* * promiseOrValue if it's a value | |
*/ | |
function resolve(promiseOrValue) { | |
var promise, deferred; | |
if(promiseOrValue instanceof Promise) { | |
// It's a when.js promise, so we trust it | |
promise = promiseOrValue; | |
} else { | |
// It's not a when.js promise. See if it's a foreign promise or a value. | |
if(isPromise(promiseOrValue)) { | |
// It's a thenable, but we don't know where it came from, so don't trust | |
// its implementation entirely. Introduce a trusted middleman when.js promise | |
deferred = defer(); | |
// IMPORTANT: This is the only place when.js should ever call .then() on an | |
// untrusted promise. Don't expose the return value to the untrusted promise | |
promiseOrValue.then( | |
function(value) { deferred.resolve(value); }, | |
function(reason) { deferred.reject(reason); }, | |
function(update) { deferred.progress(update); } | |
); | |
promise = deferred.promise; | |
} else { | |
// It's a value, not a promise. Create a resolved promise for it. | |
promise = fulfilled(promiseOrValue); | |
} | |
} | |
return promise; | |
} | |
/** | |
* Returns a rejected promise for the supplied promiseOrValue. The returned | |
* promise will be rejected with: | |
* - promiseOrValue, if it is a value, or | |
* - if promiseOrValue is a promise | |
* - promiseOrValue's value after it is fulfilled | |
* - promiseOrValue's reason after it is rejected | |
* @param {*} promiseOrValue the rejected value of the returned {@link Promise} | |
* @return {Promise} rejected {@link Promise} | |
*/ | |
function reject(promiseOrValue) { | |
return when(promiseOrValue, rejected); | |
} | |
/** | |
* Trusted Promise constructor. A Promise created from this constructor is | |
* a trusted when.js promise. Any other duck-typed promise is considered | |
* untrusted. | |
* @constructor | |
* @name Promise | |
*/ | |
function Promise(then) { | |
this.then = then; | |
} | |
Promise.prototype = { | |
/** | |
* Register a callback that will be called when a promise is | |
* fulfilled or rejected. Optionally also register a progress handler. | |
* Shortcut for .then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress) | |
* @param {function?} [onFulfilledOrRejected] | |
* @param {function?} [onProgress] | |
* @return {Promise} | |
*/ | |
always: function(onFulfilledOrRejected, onProgress) { | |
return this.then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress); | |
}, | |
/** | |
* Register a rejection handler. Shortcut for .then(undefined, onRejected) | |
* @param {function?} onRejected | |
* @return {Promise} | |
*/ | |
otherwise: function(onRejected) { | |
return this.then(undef, onRejected); | |
}, | |
/** | |
* Shortcut for .then(function() { return value; }) | |
* @param {*} value | |
* @return {Promise} a promise that: | |
* - is fulfilled if value is not a promise, or | |
* - if value is a promise, will fulfill with its value, or reject | |
* with its reason. | |
*/ | |
yield: function(value) { | |
return this.then(function() { | |
return value; | |
}); | |
}, | |
/** | |
* Assumes that this promise will fulfill with an array, and arranges | |
* for the onFulfilled to be called with the array as its argument list | |
* i.e. onFulfilled.spread(undefined, array). | |
* @param {function} onFulfilled function to receive spread arguments | |
* @return {Promise} | |
*/ | |
spread: function(onFulfilled) { | |
return this.then(function(array) { | |
// array may contain promises, so resolve its contents. | |
return all(array, function(array) { | |
return onFulfilled.apply(undef, array); | |
}); | |
}); | |
} | |
}; | |
/** | |
* Create an already-resolved promise for the supplied value | |
* @private | |
* | |
* @param {*} value | |
* @return {Promise} fulfilled promise | |
*/ | |
function fulfilled(value) { | |
var p = new Promise(function(onFulfilled) { | |
// TODO: Promises/A+ check typeof onFulfilled | |
try { | |
return resolve(onFulfilled ? onFulfilled(value) : value); | |
} catch(e) { | |
return rejected(e); | |
} | |
}); | |
return p; | |
} | |
/** | |
* Create an already-rejected {@link Promise} with the supplied | |
* rejection reason. | |
* @private | |
* | |
* @param {*} reason | |
* @return {Promise} rejected promise | |
*/ | |
function rejected(reason) { | |
var p = new Promise(function(_, onRejected) { | |
// TODO: Promises/A+ check typeof onRejected | |
try { | |
return onRejected ? resolve(onRejected(reason)) : rejected(reason); | |
} catch(e) { | |
return rejected(e); | |
} | |
}); | |
return p; | |
} | |
/** | |
* Creates a new, Deferred with fully isolated resolver and promise parts, | |
* either or both of which may be given out safely to consumers. | |
* The Deferred itself has the full API: resolve, reject, progress, and | |
* then. The resolver has resolve, reject, and progress. The promise | |
* only has then. | |
* | |
* @return {Deferred} | |
*/ | |
function defer() { | |
var deferred, promise, handlers, progressHandlers, | |
_then, _progress, _resolve; | |
/** | |
* The promise for the new deferred | |
* @type {Promise} | |
*/ | |
promise = new Promise(then); | |
/** | |
* The full Deferred object, with {@link Promise} and {@link Resolver} parts | |
* @class Deferred | |
* @name Deferred | |
*/ | |
deferred = { | |
then: then, // DEPRECATED: use deferred.promise.then | |
resolve: promiseResolve, | |
reject: promiseReject, | |
// TODO: Consider renaming progress() to notify() | |
progress: promiseProgress, | |
promise: promise, | |
resolver: { | |
resolve: promiseResolve, | |
reject: promiseReject, | |
progress: promiseProgress | |
} | |
}; | |
handlers = []; | |
progressHandlers = []; | |
/** | |
* Pre-resolution then() that adds the supplied callback, errback, and progback | |
* functions to the registered listeners | |
* @private | |
* | |
* @param {function?} [onFulfilled] resolution handler | |
* @param {function?} [onRejected] rejection handler | |
* @param {function?} [onProgress] progress handler | |
*/ | |
_then = function(onFulfilled, onRejected, onProgress) { | |
// TODO: Promises/A+ check typeof onFulfilled, onRejected, onProgress | |
var deferred, progressHandler; | |
deferred = defer(); | |
progressHandler = typeof onProgress === 'function' | |
? function(update) { | |
try { | |
// Allow progress handler to transform progress event | |
deferred.progress(onProgress(update)); | |
} catch(e) { | |
// Use caught value as progress | |
deferred.progress(e); | |
} | |
} | |
: function(update) { deferred.progress(update); }; | |
handlers.push(function(promise) { | |
promise.then(onFulfilled, onRejected) | |
.then(deferred.resolve, deferred.reject, progressHandler); | |
}); | |
progressHandlers.push(progressHandler); | |
return deferred.promise; | |
}; | |
/** | |
* Issue a progress event, notifying all progress listeners | |
* @private | |
* @param {*} update progress event payload to pass to all listeners | |
*/ | |
_progress = function(update) { | |
processQueue(progressHandlers, update); | |
return update; | |
}; | |
/** | |
* Transition from pre-resolution state to post-resolution state, notifying | |
* all listeners of the resolution or rejection | |
* @private | |
* @param {*} value the value of this deferred | |
*/ | |
_resolve = function(value) { | |
value = resolve(value); | |
// Replace _then with one that directly notifies with the result. | |
_then = value.then; | |
// Replace _resolve so that this Deferred can only be resolved once | |
_resolve = resolve; | |
// Make _progress a noop, to disallow progress for the resolved promise. | |
_progress = noop; | |
// Notify handlers | |
processQueue(handlers, value); | |
// Free progressHandlers array since we'll never issue progress events | |
progressHandlers = handlers = undef; | |
return value; | |
}; | |
return deferred; | |
/** | |
* Wrapper to allow _then to be replaced safely | |
* @param {function?} [onFulfilled] resolution handler | |
* @param {function?} [onRejected] rejection handler | |
* @param {function?} [onProgress] progress handler | |
* @return {Promise} new promise | |
*/ | |
function then(onFulfilled, onRejected, onProgress) { | |
// TODO: Promises/A+ check typeof onFulfilled, onRejected, onProgress | |
return _then(onFulfilled, onRejected, onProgress); | |
} | |
/** | |
* Wrapper to allow _resolve to be replaced | |
*/ | |
function promiseResolve(val) { | |
return _resolve(val); | |
} | |
/** | |
* Wrapper to allow _reject to be replaced | |
*/ | |
function promiseReject(err) { | |
return _resolve(rejected(err)); | |
} | |
/** | |
* Wrapper to allow _progress to be replaced | |
*/ | |
function promiseProgress(update) { | |
return _progress(update); | |
} | |
} | |
/** | |
* Determines if promiseOrValue is a promise or not. Uses the feature | |
* test from http://wiki.commonjs.org/wiki/Promises/A to determine if | |
* promiseOrValue is a promise. | |
* | |
* @param {*} promiseOrValue anything | |
* @returns {boolean} true if promiseOrValue is a {@link Promise} | |
*/ | |
function isPromise(promiseOrValue) { | |
return promiseOrValue && typeof promiseOrValue.then === 'function'; | |
} | |
/** | |
* Initiates a competitive race, returning a promise that will resolve when | |
* howMany of the supplied promisesOrValues have resolved, or will reject when | |
* it becomes impossible for howMany to resolve, for example, when | |
* (promisesOrValues.length - howMany) + 1 input promises reject. | |
* | |
* @param {Array} promisesOrValues array of anything, may contain a mix | |
* of promises and values | |
* @param howMany {number} number of promisesOrValues to resolve | |
* @param {function?} [onFulfilled] resolution handler | |
* @param {function?} [onRejected] rejection handler | |
* @param {function?} [onProgress] progress handler | |
* @returns {Promise} promise that will resolve to an array of howMany values that | |
* resolved first, or will reject with an array of (promisesOrValues.length - howMany) + 1 | |
* rejection reasons. | |
*/ | |
function some(promisesOrValues, howMany, onFulfilled, onRejected, onProgress) { | |
checkCallbacks(2, arguments); | |
return when(promisesOrValues, function(promisesOrValues) { | |
var toResolve, toReject, values, reasons, deferred, fulfillOne, rejectOne, progress, len, i; | |
len = promisesOrValues.length >>> 0; | |
toResolve = Math.max(0, Math.min(howMany, len)); | |
values = []; | |
toReject = (len - toResolve) + 1; | |
reasons = []; | |
deferred = defer(); | |
// No items in the input, resolve immediately | |
if (!toResolve) { | |
deferred.resolve(values); | |
} else { | |
progress = deferred.progress; | |
rejectOne = function(reason) { | |
reasons.push(reason); | |
if(!--toReject) { | |
fulfillOne = rejectOne = noop; | |
deferred.reject(reasons); | |
} | |
}; | |
fulfillOne = function(val) { | |
// This orders the values based on promise resolution order | |
// Another strategy would be to use the original position of | |
// the corresponding promise. | |
values.push(val); | |
if (!--toResolve) { | |
fulfillOne = rejectOne = noop; | |
deferred.resolve(values); | |
} | |
}; | |
for(i = 0; i < len; ++i) { | |
if(i in promisesOrValues) { | |
when(promisesOrValues[i], fulfiller, rejecter, progress); | |
} | |
} | |
} | |
return deferred.then(onFulfilled, onRejected, onProgress); | |
function rejecter(reason) { | |
rejectOne(reason); | |
} | |
function fulfiller(val) { | |
fulfillOne(val); | |
} | |
}); | |
} | |
/** | |
* Initiates a competitive race, returning a promise that will resolve when | |
* any one of the supplied promisesOrValues has resolved or will reject when | |
* *all* promisesOrValues have rejected. | |
* | |
* @param {Array|Promise} promisesOrValues array of anything, may contain a mix | |
* of {@link Promise}s and values | |
* @param {function?} [onFulfilled] resolution handler | |
* @param {function?} [onRejected] rejection handler | |
* @param {function?} [onProgress] progress handler | |
* @returns {Promise} promise that will resolve to the value that resolved first, or | |
* will reject with an array of all rejected inputs. | |
*/ | |
function any(promisesOrValues, onFulfilled, onRejected, onProgress) { | |
function unwrapSingleResult(val) { | |
return onFulfilled ? onFulfilled(val[0]) : val[0]; | |
} | |
return some(promisesOrValues, 1, unwrapSingleResult, onRejected, onProgress); | |
} | |
/** | |
* Return a promise that will resolve only once all the supplied promisesOrValues | |
* have resolved. The resolution value of the returned promise will be an array | |
* containing the resolution values of each of the promisesOrValues. | |
* @memberOf when | |
* | |
* @param {Array|Promise} promisesOrValues array of anything, may contain a mix | |
* of {@link Promise}s and values | |
* @param {function?} [onFulfilled] resolution handler | |
* @param {function?} [onRejected] rejection handler | |
* @param {function?} [onProgress] progress handler | |
* @returns {Promise} | |
*/ | |
function all(promisesOrValues, onFulfilled, onRejected, onProgress) { | |
checkCallbacks(1, arguments); | |
return map(promisesOrValues, identity).then(onFulfilled, onRejected, onProgress); | |
} | |
/** | |
* Joins multiple promises into a single returned promise. | |
* @return {Promise} a promise that will fulfill when *all* the input promises | |
* have fulfilled, or will reject when *any one* of the input promises rejects. | |
*/ | |
function join(/* ...promises */) { | |
return map(arguments, identity); | |
} | |
/** | |
* Traditional map function, similar to `Array.prototype.map()`, but allows | |
* input to contain {@link Promise}s and/or values, and mapFunc may return | |
* either a value or a {@link Promise} | |
* | |
* @param {Array|Promise} promise array of anything, may contain a mix | |
* of {@link Promise}s and values | |
* @param {function} mapFunc mapping function mapFunc(value) which may return | |
* either a {@link Promise} or value | |
* @returns {Promise} a {@link Promise} that will resolve to an array containing | |
* the mapped output values. | |
*/ | |
function map(promise, mapFunc) { | |
return when(promise, function(array) { | |
var results, len, toResolve, resolve, i, d; | |
// Since we know the resulting length, we can preallocate the results | |
// array to avoid array expansions. | |
toResolve = len = array.length >>> 0; | |
results = []; | |
d = defer(); | |
if(!toResolve) { | |
d.resolve(results); | |
} else { | |
resolve = function resolveOne(item, i) { | |
when(item, mapFunc).then(function(mapped) { | |
results[i] = mapped; | |
if(!--toResolve) { | |
d.resolve(results); | |
} | |
}, d.reject); | |
}; | |
// Since mapFunc may be async, get all invocations of it into flight | |
for(i = 0; i < len; i++) { | |
if(i in array) { | |
resolve(array[i], i); | |
} else { | |
--toResolve; | |
} | |
} | |
} | |
return d.promise; | |
}); | |
} | |
/** | |
* Traditional reduce function, similar to `Array.prototype.reduce()`, but | |
* input may contain promises and/or values, and reduceFunc | |
* may return either a value or a promise, *and* initialValue may | |
* be a promise for the starting value. | |
* | |
* @param {Array|Promise} promise array or promise for an array of anything, | |
* may contain a mix of promises and values. | |
* @param {function} reduceFunc reduce function reduce(currentValue, nextValue, index, total), | |
* where total is the total number of items being reduced, and will be the same | |
* in each call to reduceFunc. | |
* @returns {Promise} that will resolve to the final reduced value | |
*/ | |
function reduce(promise, reduceFunc /*, initialValue */) { | |
var args = slice.call(arguments, 1); | |
return when(promise, function(array) { | |
var total; | |
total = array.length; | |
// Wrap the supplied reduceFunc with one that handles promises and then | |
// delegates to the supplied. | |
args[0] = function (current, val, i) { | |
return when(current, function (c) { | |
return when(val, function (value) { | |
return reduceFunc(c, value, i, total); | |
}); | |
}); | |
}; | |
return reduceArray.apply(array, args); | |
}); | |
} | |
/** | |
* Ensure that resolution of promiseOrValue will trigger resolver with the | |
* value or reason of promiseOrValue, or instead with resolveValue if it is provided. | |
* | |
* @param promiseOrValue | |
* @param {Object} resolver | |
* @param {function} resolver.resolve | |
* @param {function} resolver.reject | |
* @param {*} [resolveValue] | |
* @returns {Promise} | |
*/ | |
function chain(promiseOrValue, resolver, resolveValue) { | |
var useResolveValue = arguments.length > 2; | |
return when(promiseOrValue, | |
function(val) { | |
val = useResolveValue ? resolveValue : val; | |
resolver.resolve(val); | |
return val; | |
}, | |
function(reason) { | |
resolver.reject(reason); | |
return rejected(reason); | |
}, | |
resolver.progress | |
); | |
} | |
// | |
// Utility functions | |
// | |
/** | |
* Apply all functions in queue to value | |
* @param {Array} queue array of functions to execute | |
* @param {*} value argument passed to each function | |
*/ | |
function processQueue(queue, value) { | |
var handler, i = 0; | |
while (handler = queue[i++]) { | |
handler(value); | |
} | |
} | |
/** | |
* Helper that checks arrayOfCallbacks to ensure that each element is either | |
* a function, or null or undefined. | |
* @private | |
* @param {number} start index at which to start checking items in arrayOfCallbacks | |
* @param {Array} arrayOfCallbacks array to check | |
* @throws {Error} if any element of arrayOfCallbacks is something other than | |
* a functions, null, or undefined. | |
*/ | |
function checkCallbacks(start, arrayOfCallbacks) { | |
// TODO: Promises/A+ update type checking and docs | |
var arg, i = arrayOfCallbacks.length; | |
while(i > start) { | |
arg = arrayOfCallbacks[--i]; | |
if (arg != null && typeof arg != 'function') { | |
throw new Error('arg '+i+' must be a function'); | |
} | |
} | |
} | |
/** | |
* No-Op function used in method replacement | |
* @private | |
*/ | |
function noop() {} | |
slice = [].slice; | |
// ES5 reduce implementation if native not available | |
// See: http://es5.github.com/#x15.4.4.21 as there are many | |
// specifics and edge cases. | |
reduceArray = [].reduce || | |
function(reduceFunc /*, initialValue */) { | |
/*jshint maxcomplexity: 7*/ | |
// ES5 dictates that reduce.length === 1 | |
// This implementation deviates from ES5 spec in the following ways: | |
// 1. It does not check if reduceFunc is a Callable | |
var arr, args, reduced, len, i; | |
i = 0; | |
// This generates a jshint warning, despite being valid | |
// "Missing 'new' prefix when invoking a constructor." | |
// See https://github.com/jshint/jshint/issues/392 | |
arr = Object(this); | |
len = arr.length >>> 0; | |
args = arguments; | |
// If no initialValue, use first item of array (we know length !== 0 here) | |
// and adjust i to start at second item | |
if(args.length <= 1) { | |
// Skip to the first real element in the array | |
for(;;) { | |
if(i in arr) { | |
reduced = arr[i++]; | |
break; | |
} | |
// If we reached the end of the array without finding any real | |
// elements, it's a TypeError | |
if(++i >= len) { | |
throw new TypeError(); | |
} | |
} | |
} else { | |
// If initialValue provided, use it | |
reduced = args[1]; | |
} | |
// Do the actual reduce | |
for(;i < len; ++i) { | |
// Skip holes | |
if(i in arr) { | |
reduced = reduceFunc(reduced, arr[i], i, arr); | |
} | |
} | |
return reduced; | |
}; | |
function identity(x) { | |
return x; | |
} | |
return when; | |
}); | |
})(typeof define == 'function' && define.amd | |
? define | |
: function (factory) { typeof exports === 'object' | |
? (module.exports = factory()) | |
: (this.when = factory()); | |
} | |
// Boilerplate for AMD, Node, and browser global | |
); | |
define('when', ['when/when'], function (main) { return main; }); | |
/** | |
* TroopJS event/emitter module | |
* @license MIT http://troopjs.mit-license.org/ © Mikael Karon mailto:[email protected] | |
*/ | |
/*global define:false */ | |
define('troopjs-core/event/emitter',[ "compose", "when" ], function EventEmitterModule(Compose, when) { | |
/*jshint laxbreak:true */ | |
var MEMORY = "memory"; | |
var CONTEXT = "context"; | |
var CALLBACK = "callback"; | |
var LENGTH = "length"; | |
var HEAD = "head"; | |
var TAIL = "tail"; | |
var NEXT = "next"; | |
var HANDLED = "handled"; | |
var HANDLERS = "handlers"; | |
return Compose( | |
/** | |
* Creates a new EventEmitter | |
* @constructor | |
*/ | |
function EventEmitter() { | |
this[HANDLERS] = {}; | |
}, { | |
/** | |
* Adds a listener for the specified event. | |
* @param {String} event to subscribe to | |
* @param {Object} context to scope callbacks to | |
* @param {...Function} callback for this event | |
* @returns {Object} instance of this | |
*/ | |
on : function on(event, context, callback) { | |
var self = this; | |
var args = arguments; | |
var handlers = self[HANDLERS]; | |
var handler; | |
var head; | |
var tail; | |
var length = args[LENGTH]; | |
var offset = 2; | |
// Have handlers | |
if (event in handlers) { | |
// Get handlers | |
handlers = handlers[event]; | |
// Create new handler | |
handler = {}; | |
// Set handler callback to next arg from offset | |
handler[CALLBACK] = args[offset++]; | |
// Set handler context | |
handler[CONTEXT] = context; | |
// Get tail handler | |
tail = TAIL in handlers | |
// Have tail, update handlers[TAIL][NEXT] to point to handler | |
? handlers[TAIL][NEXT] = handler | |
// Have no tail, update handlers[HEAD] to point to handler | |
: handlers[HEAD] = handler; | |
// Iterate handlers from offset | |
while (offset < length) { | |
// Set tail -> tail[NEXT] -> handler | |
tail = tail[NEXT] = handler = {}; | |
// Set handler callback to next arg from offset | |
handler[CALLBACK] = args[offset++]; | |
// Set handler context | |
handler[CONTEXT] = context; | |
} | |
// Set tail handler | |
handlers[TAIL] = tail; | |
} | |
// No handlers | |
else { | |
// Create head and tail | |
head = tail = handler = {}; | |
// Set handler callback to next arg from offset | |
handler[CALLBACK] = args[offset++]; | |
// Set handler context | |
handler[CONTEXT] = context; | |
// Iterate handlers from offset | |
while (offset < length) { | |
// Set tail -> tail[NEXT] -> handler | |
tail = tail[NEXT] = handler = {}; | |
// Set handler callback to next arg from offset | |
handler[CALLBACK] = args[offset++]; | |
// Set handler context | |
handler[CONTEXT] = context; | |
} | |
// Create event handlers | |
handlers = handlers[event] = {}; | |
// Initialize event handlers | |
handlers[HEAD] = head; | |
handlers[TAIL] = tail; | |
handlers[HANDLED] = 0; | |
} | |
return self; | |
}, | |
/** | |
* Remove a listener for the specified event. | |
* @param {String} event to remove callback from | |
* @param {Object} context to scope callback to | |
* @param {...Function} [callback] to remove | |
* @returns {Object} instance of this | |
*/ | |
off : function off(event, context, callback) { | |
var self = this; | |
var args = arguments; | |
var handlers = self[HANDLERS]; | |
var handler; | |
var head; | |
var tail; | |
var length = args[LENGTH]; | |
var offset; | |
// Return fast if we don't have subscribers | |
if (!(event in handlers)) { | |
return self; | |
} | |
// Get handlers | |
handlers = handlers[event]; | |
// Return fast if there's no HEAD | |
if (!(HEAD in handlers)) { | |
return self; | |
} | |
// Get first handler | |
handler = handlers[HEAD]; | |
// Step through handlers | |
keep: do { | |
// Check if context matches | |
if (handler[CONTEXT] === context) { | |
// Continue if no callback was provided | |
if (length === 2) { | |
continue; | |
} | |
// Reset offset, then loop callbacks | |
for (offset = 2; offset < length; offset++) { | |
// Continue if handler CALLBACK matches | |
if (handler[CALLBACK] === args[offset]) { | |
continue keep; | |
} | |
} | |
} | |
// It there's no head - link head -> tail -> handler | |
if (!head) { | |
head = tail = handler; | |
} | |
// Otherwise just link tail -> tail[NEXT] -> handler | |
else { | |
tail = tail[NEXT] = handler; | |
} | |
} while ((handler = handler[NEXT])); | |
// If we have both head and tail we should update handlers | |
if (head && tail) { | |
// Set handlers HEAD and TAIL | |
handlers[HEAD] = head; | |
handlers[TAIL] = tail; | |
// Make sure to remove NEXT from tail | |
delete tail[NEXT]; | |
} | |
// Otherwise we remove the handlers list | |
else { | |
delete handlers[HEAD]; | |
delete handlers[TAIL]; | |
} | |
return self; | |
}, | |
/** | |
* Reemit event from memory | |
* @param {String} event to reemit | |
* @param {Object} context to scope callback to | |
* @param {...Function} callback to reemit | |
* @returns {Object} instance of this | |
*/ | |
reemit : function reemit(event, context, callback) { | |
var self = this; | |
var args = arguments; | |
var handlers = self[HANDLERS]; | |
var handler; | |
var handled; | |
var length = args[LENGTH]; | |
var offset; | |
// Have event in handlers | |
if (event in handlers) { | |
// Get handlers | |
handlers = handlers[event]; | |
// Have memory in handlers | |
if (MEMORY in handlers) { | |
// If we have no HEAD we can return a promise resolved with memory | |
if (!(HEAD in handlers)) { | |
return when.resolve(handlers[MEMORY]); | |
} | |
// Get first handler | |
handler = handlers[HEAD]; | |
// Compute next handled | |
handled = handlers[HANDLED] + 1; | |
// Step through handlers | |
mark: do { | |
// Check if context matches | |
if (handler[CONTEXT] === context) { | |
// Continue if no callback was provided | |
if (length === 2) { | |
continue; | |
} | |
// Reset offset, then loop callbacks | |
for (offset = 2; offset < length; offset++) { | |
// Break if handler CALLBACK matches | |
if (handler[CALLBACK] === args[offset]) { | |
continue mark; | |
} | |
} | |
} | |
// Mark this handler as handled (to prevent reemit) | |
handler[HANDLED] = handled; | |
} while ((handler = handler[NEXT])); | |
// Return self.emit with memory | |
return self.emit.apply(self, handlers[MEMORY]); | |
} | |
} | |
// Return resolved promise | |
return when.resolve(); | |
}, | |
/** | |
* Execute each of the listeners in order with the supplied arguments | |
* @param {String} event to emit | |
* @returns {Promise} promise that resolves with results from all listeners | |
*/ | |
emit : function emit(event) { | |
var self = this; | |
var args = arguments; | |
var handlers = self[HANDLERS]; | |
var handler; | |
var handled; | |
/** | |
* Internal function for async execution of callbacks | |
* @private | |
* @param {Array} [_arg] result from previous callback | |
* @return {Promise} promise of next execution | |
*/ | |
function next(_arg) { | |
// Update arg | |
args = _arg || args; | |
// Step forward until we find a unhandled handler | |
while(handler[HANDLED] === handled) { | |
// No more handlers, escape! | |
if (!(handler = handler[NEXT])) { | |
// Remember arg | |
handlers[MEMORY] = args; | |
// Return promise resolved with arg | |
return when.resolve(args); | |
} | |
} | |
// Update handled | |
handler[HANDLED] = handled; | |
// Return promise of callback execution, chain next | |
return when(handler[CALLBACK].apply(handler[CONTEXT], args), next); | |
} | |
// Have event in handlers | |
if (event in handlers) { | |
// Get handlers | |
handlers = handlers[event]; | |
// Update handled | |
handled = ++handlers[HANDLED]; | |
// Have head in handlers | |
if (HEAD in handlers) { | |
// Get first handler | |
handler = handlers[HEAD]; | |
try { | |
// Return promise | |
return next(args); | |
} | |
catch (e) { | |
// Return promise rejected with exception | |
return when.reject(e); | |
} | |
} | |
} | |
// No event in handlers | |
else { | |
// Create handlers and store with event | |
handlers[event] = handlers = {}; | |
// Set handled | |
handlers[HANDLED] = 0; | |
} | |
// Remember arg | |
handlers[MEMORY] = args; | |
// Return promise resolved with arg | |
return when.resolve(args); | |
} | |
}); | |
}); | |
/** | |
* TroopJS base component | |
* @license MIT http://troopjs.mit-license.org/ © Mikael Karon mailto:[email protected] | |
*/ | |
/*global define:false */ | |
define('troopjs-core/component/base',[ "../event/emitter", "when" ], function ComponentModule(Emitter, when) { | |
/*jshint laxbreak:true */ | |
var NULL = null; | |
var ARRAY_PROTO = Array.prototype; | |
var ARRAY_UNSHIFT = ARRAY_PROTO.unshift; | |
var ARRAY_SLICE = ARRAY_PROTO.slice; | |
var INSTANCE_COUNT = "instanceCount"; | |
var LENGTH = "length"; | |
var FEATURES = "features"; | |
var CONTEXT = "context"; | |
var VALUE = "value"; | |
var PROPERTIES = "properties"; | |
var SIG = "sig"; | |
var RE_PRO = /^(\w+)(?::(\w+))?\/(.+)/; | |
var COUNT = 0; | |
var Component = Emitter.extend( | |
/** | |
* Creates a new component | |
* @constructor | |
*/ | |
function Component() { | |
var self = this; | |
var bases = self.constructor._getBases(true); | |
var base; | |
var i = bases[LENGTH]; | |
var properties = self[PROPERTIES] = {}; | |
var property; | |
var matches; | |
var key; | |
var type; | |
var name; | |
// Iterate base chain (backwards) | |
while((base = bases[--i])) { | |
// Iterate keys | |
for (key in base) { | |
// Continue if this is not a property on base | |
if (!base.hasOwnProperty(key)) { | |
continue; | |
} | |
// Continue if we can't match | |
if ((matches = RE_PRO.exec(key)) === NULL) { | |
continue; | |
} | |
// Get type | |
type = matches[1]; | |
// Get or create type from properties | |
type = type in properties | |
? properties[type] | |
: properties[type] = {}; | |
// Get name | |
name = matches[3]; | |
// Get or create name from type | |
name = name in type | |
? type[name] | |
: type[name] = []; | |
// Create and set property by type/name | |
property = name[name[LENGTH]] = {}; | |
// Init property | |
property[FEATURES] = matches[2]; | |
property[CONTEXT] = base; | |
property[VALUE] = base[key]; | |
} | |
} | |
// Update instance count | |
self[INSTANCE_COUNT] = COUNT++; | |
}, { | |
"instanceCount" : COUNT, | |
"displayName" : "core/component", | |
/** | |
* Signals the component | |
* @param signal {String} Signal | |
* @return {*} | |
*/ | |
"signal" : function onSignal(signal) { | |
var self = this; | |
var args = ARRAY_SLICE.call(arguments); | |
var signals = self[PROPERTIES][SIG][signal]; | |
var length = signals | |
? signals[LENGTH] | |
: 0; | |
var index = 0; | |
function next(_args) { | |
// Update args | |
args = _args || args; | |
// Return a chained promise of next callback, or a promise resolved with args | |
return length > index | |
? when(signals[index++][VALUE].apply(self, args), next) | |
: when.resolve(args); | |
} | |
try { | |
// Return promise | |
return next(); | |
} | |
catch (e) { | |
// Return rejected promise | |
return when.reject(e); | |
} | |
}, | |
/** | |
* Start the component | |
* @return {*} | |
*/ | |
"start" : function start() { | |
var self = this; | |
var _signal = self.signal; | |
var args = arguments; | |
// Add signal to arguments | |
ARRAY_UNSHIFT.call(args, "initialize"); | |
return _signal.apply(self, args).then(function _start() { | |
// Modify args to change signal | |
args[0] = "start"; | |
return _signal.apply(self, args); | |
}); | |
}, | |
/** | |
* Stops the component | |
* @return {*} | |
*/ | |
"stop" : function stop() { | |
var self = this; | |
var _signal = self.signal; | |
var args = arguments; | |
// Add signal to arguments | |
ARRAY_UNSHIFT.call(args, "stop"); | |
return _signal.apply(self, args).then(function _stop() { | |
// Modify args to change signal | |
args[0] = "finalize"; | |
return _signal.apply(self, args); | |
}); | |
} | |
}); | |
/** | |
* Generates string representation of this object | |
* @returns {string} displayName and instanceCount | |
*/ | |
Component.prototype.toString = function () { | |
var self = this; | |
return self.displayName + "@" + self[INSTANCE_COUNT]; | |
}; | |
return Component; | |
}); | |
/*! | |
* TroopJS pubsub/hub module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-core/pubsub/hub',[ "compose", "../component/base" ], function HubModule(Compose, Component) { | |
/*jshint strict:false, smarttabs:true */ | |
var from = Compose.from; | |
return Compose.create(Component, { | |
displayName: "core/pubsub/hub", | |
subscribe : from(Component, "on"), | |
unsubscribe : from(Component, "off"), | |
publish : from(Component, "emit"), | |
republish : from(Component, "reemit") | |
}); | |
}); | |
/** | |
* TroopJS gadget component | |
* @license MIT http://troopjs.mit-license.org/ © Mikael Karon mailto:[email protected] | |
*/ | |
/*global define:false */ | |
define('troopjs-core/component/gadget',[ "./base", "when", "../pubsub/hub" ], function GadgetModule(Component, when, hub) { | |
/*jshint laxbreak:true */ | |
var ARRAY_SPLICE = Array.prototype.splice; | |
var PUBLISH = hub.publish; | |
var REPUBLISH = hub.republish; | |
var SUBSCRIBE = hub.subscribe; | |
var UNSUBSCRIBE = hub.unsubscribe; | |
var HUB = "hub"; | |
var LENGTH = "length"; | |
var FEATURES = "features"; | |
var VALUE = "value"; | |
var PROPERTIES = "properties"; | |
return Component.extend({ | |
"displayName" : "core/component/gadget", | |
/** | |
* Signal handler for 'initialize' | |
*/ | |
"sig/initialize" : function initialize() { | |
var self = this; | |
var properties = self[PROPERTIES][HUB]; | |
var subscriptions; | |
var args; | |
var key; | |
var i; | |
var j; | |
var iMax; | |
// Iterate properties | |
for (key in properties) { | |
// Get subscriptions | |
subscriptions = properties[key]; | |
// Create args | |
args = [key, self]; | |
// Extract VALUE into args | |
for (i = 0, iMax = subscriptions[LENGTH], j = args[LENGTH]; i < iMax; i++) { | |
args[j++] = subscriptions[i][VALUE]; | |
} | |
// Did we capture any args? | |
if (j > 2) { | |
SUBSCRIBE.apply(hub, args); | |
} | |
} | |
}, | |
/** | |
* Signal handler for 'start' | |
*/ | |
"sig/start" : function start() { | |
var self = this; | |
var properties = self[PROPERTIES][HUB]; | |
var results = []; | |
var subscriptions; | |
var subscription; | |
var args; | |
var key; | |
var i; | |
var j; | |
var iMax; | |
for (key in properties) { | |
// Get subscriptions | |
subscriptions = properties[key]; | |
// Create args | |
args = [key, self]; | |
// Extract callbacks into args | |
for (i = 0, iMax = subscriptions[LENGTH], j = args[LENGTH]; i < iMax; i++) { | |
subscription = subscriptions[i]; | |
// Only add onto args if we have "memory" | |
if (subscription[FEATURES] === "memory") { | |
args[j++] = subscription[VALUE]; | |
} | |
} | |
// Did we capture any args? | |
if (j > 2) { | |
results[results[LENGTH]] = REPUBLISH.apply(hub, args); | |
} | |
} | |
// Return promise that will resolve when all republish is done | |
return when.all(results); | |
}, | |
/** | |
* Signal handler for 'finalize' | |
*/ | |
"sig/finalize" : function finalize() { | |
var self = this; | |
var properties = self[PROPERTIES][HUB]; | |
var subscriptions; | |
var args; | |
var key; | |
var i; | |
var j; | |
var iMax; | |
for (key in properties) { | |
// Get subscriptions | |
subscriptions = properties[key]; | |
// Create args | |
args = [key, self]; | |
// Extract callbacks into args | |
for (i = 0, iMax = subscriptions[LENGTH], j = args[LENGTH]; i < iMax; i++) { | |
args[j++] = subscriptions[i][VALUE]; | |
} | |
// Did we capture any args? | |
if (j > 2) { | |
UNSUBSCRIBE.apply(hub, args); | |
} | |
} | |
}, | |
/** | |
* Calls hub.publish in self context | |
*/ | |
"publish" : function publish() { | |
return PUBLISH.apply(hub, arguments); | |
}, | |
/** | |
* Calls hub.subscribe in self context | |
*/ | |
"subscribe" : function subscribe() { | |
var self = this; | |
var args = arguments; | |
// Add self as context | |
ARRAY_SPLICE.call(args, 1, 0, self); | |
// Subscribe | |
SUBSCRIBE.apply(hub, args); | |
return self; | |
}, | |
/** | |
* Calls hub.unsubscribe in self context | |
*/ | |
"unsubscribe" : function unsubscribe() { | |
var self = this; | |
var args = arguments; | |
// Add self as context | |
ARRAY_SPLICE.call(args, 1, 0, self); | |
// Unsubscribe | |
UNSUBSCRIBE.apply(hub, args); | |
return self; | |
} | |
}); | |
}); | |
/** | |
* TroopJS service component | |
* @license MIT http://troopjs.mit-license.org/ © Mikael Karon mailto:[email protected] | |
*/ | |
/*global define:false */ | |
define('troopjs-core/component/service',[ "./gadget" ], function ServiceModule(Gadget) { | |
return Gadget.extend({ | |
"displayName" : "core/component/service" | |
}); | |
}); | |
/*! | |
* TroopJS Data query component | |
* @license TroopJS Copyright 2013, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-data/query/component', [ "troopjs-core/component/base" ], function QueryModule(Component) { | |
/*jshint laxbreak:true */ | |
var UNDEFINED; | |
var TRUE = true; | |
var FALSE = false; | |
var OBJECT = Object; | |
var ARRAY = Array; | |
var CONSTRUCTOR = "constructor"; | |
var LENGTH = "length"; | |
var OP = "op"; | |
var OP_ID = "!"; | |
var OP_PROPERTY = "."; | |
var OP_PATH = ","; | |
var OP_QUERY = "|"; | |
var TEXT = "text"; | |
var RAW = "raw"; | |
var RESOLVED = "resolved"; | |
var _ID = "id"; | |
var _EXPIRES = "expires"; | |
var _COLLAPSED = "collapsed"; | |
var _AST = "_ast"; | |
var _QUERY = "_query"; | |
var RE_TEXT = /("|')(.*?)\1/; | |
var TO_RAW = "$2"; | |
var RE_RAW = /!(.*[!,|.\s]+.*)/; | |
var TO_TEXT = "!'$1'"; | |
return Component.extend(function Query(query) { | |
var self = this; | |
if (query !== UNDEFINED) { | |
self[_QUERY] = query; | |
} | |
}, { | |
"displayName" : "data/query/component", | |
"parse" : function parse(query) { | |
var self = this; | |
// Reset _AST | |
delete self[_AST]; | |
// Set _QUERY | |
query = self[_QUERY] = (query || self[_QUERY] || ""); | |
var i; // Index | |
var l; // Length | |
var c; // Current character | |
var m; // Current mark | |
var q; // Current quote | |
var o; // Current operation | |
var ast = []; // _AST | |
// Step through the query | |
for (i = m = 0, l = query[LENGTH]; i < l; i++) { | |
c = query.charAt(i); | |
switch (c) { | |
case "\"" : // Double quote | |
case "'" : // Single quote | |
// Set / unset quote char | |
q = q === c | |
? UNDEFINED | |
: c; | |
break; | |
case OP_ID : | |
// Break fast if we're quoted | |
if (q !== UNDEFINED) { | |
break; | |
} | |
// Init new op | |
o = {}; | |
o[OP] = c; | |
break; | |
case OP_PROPERTY : | |
case OP_PATH : | |
// Break fast if we're quoted | |
if (q !== UNDEFINED) { | |
break; | |
} | |
// If there's an active op, store TEXT and push on _AST | |
if (o !== UNDEFINED) { | |
o[RAW] = (o[TEXT] = query.substring(m, i)).replace(RE_TEXT, TO_RAW); | |
ast.push(o); | |
} | |
// Init new op | |
o = {}; | |
o[OP] = c; | |
// Set mark | |
m = i + 1; | |
break; | |
case OP_QUERY : | |
case " " : // Space | |
case "\t" : // Horizontal tab | |
case "\r" : // Carriage return | |
case "\n" : // Newline | |
// Break fast if we're quoted | |
if (q !== UNDEFINED) { | |
break; | |
} | |
// If there's an active op, store TEXT and push on _AST | |
if (o !== UNDEFINED) { | |
o[RAW] = (o[TEXT] = query.substring(m, i)).replace(RE_TEXT, TO_RAW); | |
ast.push(o); | |
} | |
// Reset op | |
o = UNDEFINED; | |
// Set mark | |
m = i + 1; | |
break; | |
} | |
} | |
// If there's an active op, store TEXT and push on _AST | |
if (o !== UNDEFINED) { | |
o[RAW] = (o[TEXT] = query.substring(m, l)).replace(RE_TEXT, TO_RAW); | |
ast.push(o); | |
} | |
// Set _AST | |
self[_AST] = ast; | |
return self; | |
}, | |
"reduce" : function reduce(cache) { | |
var self = this; | |
var now = 0 | new Date().getTime() / 1000; | |
// If we're not parsed - parse | |
if (!(_AST in self)) { | |
self.parse(); | |
} | |
var ast = self[_AST]; // _AST | |
var result = []; // Result | |
var i; // Index | |
var j; | |
var c; | |
var l; // Length | |
var o; // Current operation | |
var x; // Current raw | |
var r; // Current root | |
var n; // Current node | |
var k = FALSE; // Keep flag | |
// First step is to resolve what we can from the _AST | |
for (i = 0, l = ast[LENGTH]; i < l; i++) { | |
o = ast[i]; | |
switch (o[OP]) { | |
case OP_ID : | |
// Set root | |
r = o; | |
// Get e from o | |
x = o[RAW]; | |
// Do we have this item in cache | |
if (x in cache) { | |
// Set current node | |
n = cache[x]; | |
// Set RESOLVED if we're not collapsed or expired | |
o[RESOLVED] = n[_COLLAPSED] !== TRUE && !(_EXPIRES in n) || n[_EXPIRES] > now; | |
} | |
else { | |
// Reset current root and node | |
n = UNDEFINED; | |
// Reset RESOLVED | |
o[RESOLVED] = FALSE; | |
} | |
break; | |
case OP_PROPERTY : | |
// Get e from o | |
x = o[RAW]; | |
// Do we have a node and this item in the node | |
if (n && x in n) { | |
// Set current node | |
n = n[x]; | |
// Get constructor | |
c = n[CONSTRUCTOR]; | |
// If the constructor is an array | |
if (c === ARRAY) { | |
// Set naive resolved | |
o[RESOLVED] = TRUE; | |
// Iterate backwards over n | |
for (j = n[LENGTH]; j-- > 0;) { | |
// Get item | |
c = n[j]; | |
// If the constructor is not an object | |
// or the object does not duck-type _ID | |
// or the object is not collapsed | |
// and the object does not duck-type _EXPIRES | |
// or the objects is not expired | |
if (c[CONSTRUCTOR] !== OBJECT | |
|| !(_ID in c) | |
|| c[_COLLAPSED] !== TRUE | |
&& !(_EXPIRES in c) | |
|| c[_EXPIRES] > now) { | |
continue; | |
} | |
// Change RESOLVED | |
o[RESOLVED] = FALSE; | |
break; | |
} | |
} | |
// If the constructor is _not_ an object or n does not duck-type _ID | |
else if (c !== OBJECT || !(_ID in n)) { | |
o[RESOLVED] = TRUE; | |
} | |
// We know c _is_ and object and n _does_ duck-type _ID | |
else { | |
// Change OP to OP_ID | |
o[OP] = OP_ID; | |
// Update RAW to _ID and TEXT to escaped version of RAW | |
o[TEXT] = (o[RAW] = n[_ID]).replace(RE_RAW, TO_TEXT); | |
// Set RESOLVED if we're not collapsed or expired | |
o[RESOLVED] = n[_COLLAPSED] !== TRUE && !(_EXPIRES in n) || n[_EXPIRES] > now; | |
} | |
} | |
else { | |
// Reset current node and RESOLVED | |
n = UNDEFINED; | |
o[RESOLVED] = FALSE; | |
} | |
break; | |
case OP_PATH : | |
// Get e from r | |
x = r[RAW]; | |
// Set current node | |
n = cache[x]; | |
// Change OP to OP_ID | |
o[OP] = OP_ID; | |
// Copy properties from r | |
o[TEXT] = r[TEXT]; | |
o[RAW] = x; | |
o[RESOLVED] = r[RESOLVED]; | |
break; | |
} | |
} | |
// After that we want to reduce 'dead' operations from the _AST | |
while (l-- > 0) { | |
o = ast[l]; | |
switch(o[OP]) { | |
case OP_ID : | |
// If the keep flag is set, or the op is not RESOLVED | |
if (k || o[RESOLVED] !== TRUE) { | |
result.unshift(o); | |
} | |
// Reset keep flag | |
k = FALSE; | |
break; | |
case OP_PROPERTY : | |
result.unshift(o); | |
// Set keep flag | |
k = TRUE; | |
break; | |
} | |
} | |
// Update _AST | |
self[_AST] = result; | |
return self; | |
}, | |
"ast" : function ast() { | |
var self = this; | |
// If we're not parsed - parse | |
if (!(_AST in self)) { | |
self.parse(); | |
} | |
return self[_AST]; | |
}, | |
"rewrite" : function rewrite() { | |
var self = this; | |
// If we're not parsed - parse | |
if (!(_AST in self)) { | |
self.parse(); | |
} | |
var ast = self[_AST]; // AST | |
var result = ""; // Result | |
var l; // Current length | |
var i; // Current index | |
var o; // Current operation | |
// Step through AST | |
for (i = 0, l = ast[LENGTH]; i < l; i++) { | |
o = ast[i]; | |
switch(o[OP]) { | |
case OP_ID : | |
// If this is the first OP_ID, there's no need to add OP_QUERY | |
result += i === 0 | |
? o[TEXT] | |
: OP_QUERY + o[TEXT]; | |
break; | |
case OP_PROPERTY : | |
result += OP_PROPERTY + o[TEXT]; | |
break; | |
} | |
} | |
return result; | |
} | |
}); | |
}); | |
/*! | |
* TroopJS pubsub/topic module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-core/pubsub/topic',[ "../component/base", "troopjs-utils/unique" ], function TopicModule(Component, unique) { | |
/*jshint strict:false, smarttabs:true, laxbreak:true */ | |
var TOSTRING = Object.prototype.toString; | |
var TOSTRING_ARRAY = TOSTRING.call(Array.prototype); | |
function comparator (a, b) { | |
return a.publisherInstanceCount === b.publisherInstanceCount; | |
} | |
var Topic = Component.extend(function Topic(topic, publisher, parent) { | |
var self = this; | |
self.topic = topic; | |
self.publisher = publisher; | |
self.parent = parent; | |
self.publisherInstanceCount = publisher.instanceCount; | |
}, { | |
displayName : "core/pubsub/topic", | |
/** | |
* Traces topic origin to root | |
* @returns String representation of all topics traced down to root | |
*/ | |
trace : function trace() { | |
var current = this; | |
var constructor = current.constructor; | |
var parent; | |
var item; | |
var stack = ""; | |
var i; | |
var u; | |
var iMax; | |
while (current) { | |
if (TOSTRING.call(current) === TOSTRING_ARRAY) { | |
u = unique.call(current, comparator); | |
for (i = 0, iMax = u.length; i < iMax; i++) { | |
item = u[i]; | |
u[i] = item.constructor === constructor | |
? item.trace() | |
: item.topic; | |
} | |
stack += u.join(","); | |
break; | |
} | |
parent = current.parent; | |
stack += parent | |
? current.publisher + ":" | |
: current.publisher; | |
current = parent; | |
} | |
return stack; | |
} | |
}); | |
/** | |
* Generates string representation of this object | |
* @returns Instance topic | |
*/ | |
Topic.prototype.toString = function () { | |
return this.topic; | |
}; | |
return Topic; | |
}); | |
/*! | |
* TroopJS Data query service | |
* @license TroopJS Copyright 2013, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-data/query/service',[ "module", "troopjs-core/component/service", "./component", "troopjs-core/pubsub/topic", "when", "troopjs-utils/merge" ], function QueryServiceModule(module, Service, Query, Topic, when, merge) { | |
/*jshint laxbreak:true */ | |
var ARRAY_PROTO = Array.prototype; | |
var SLICE = ARRAY_PROTO.slice; | |
var CONCAT = ARRAY_PROTO.concat; | |
var PUSH = ARRAY_PROTO.push; | |
var LENGTH = "length"; | |
var BATCHES = "batches"; | |
var INTERVAL = "interval"; | |
var CACHE = "cache"; | |
var TOPIC = "topic"; | |
var QUERIES = "queries"; | |
var RESOLVED = "resolved"; | |
var RAW = "raw"; | |
var ID = "id"; | |
var Q = "q"; | |
var CONFIG = module.config(); | |
var QueryService = Service.extend(function (cache) { | |
var self = this; | |
self[BATCHES] = []; | |
self[CACHE] = cache; | |
}, { | |
"displayName" : "data/query/service", | |
"sig/start" : function start() { | |
var self = this; | |
var cache = self[CACHE]; | |
// Set interval (if we don't have one) | |
self[INTERVAL] = INTERVAL in self | |
? self[INTERVAL] | |
: setInterval(function scan() { | |
var batches = self[BATCHES]; | |
// Return fast if there is nothing to do | |
if (batches[LENGTH] === 0) { | |
return; | |
} | |
// Reset batches | |
self[BATCHES] = []; | |
function request() { | |
var q = []; | |
var topics = []; | |
var batch; | |
var i; | |
// Iterate batches | |
for (i = batches[LENGTH]; i--;) { | |
batch = batches[i]; | |
// Add batch[TOPIC] to topics | |
PUSH.call(topics, batch[TOPIC]); | |
// Add batch[Q] to q | |
PUSH.apply(q, batch[Q]); | |
} | |
// Publish ajax | |
return self.publish(Topic("ajax", self, topics), merge.call({ | |
"data": { | |
"q": q.join("|") | |
} | |
}, CONFIG)); | |
} | |
function done(data) { | |
var batch; | |
var queries; | |
var id; | |
var i; | |
var j; | |
// Add all new data to cache | |
cache.put(data); | |
// Iterate batches | |
for (i = batches[LENGTH]; i--;) { | |
batch = batches[i]; | |
queries = batch[QUERIES]; | |
id = batch[ID]; | |
// Iterate queries | |
for (j = queries[LENGTH]; j--;) { | |
// If we have a corresponding ID, fetch from cache | |
if (j in id) { | |
queries[j] = cache[id[j]]; | |
} | |
} | |
// Resolve batch | |
batch.resolve(queries); | |
} | |
} | |
function fail() { | |
var batch; | |
var i; | |
// Iterate batches | |
for (i = batches[LENGTH]; i--;) { | |
batch = batches[i]; | |
// Reject (with original queries as argument) | |
batch.reject(batch[QUERIES]); | |
} | |
} | |
// Request and handle response | |
return request().then(done, fail); | |
}, 200); | |
}, | |
"sig/stop" : function stop() { | |
var self = this; | |
// Only do this if we have an interval | |
if (INTERVAL in self) { | |
// Clear interval | |
clearInterval(self[INTERVAL]); | |
// Reset interval | |
delete self[INTERVAL]; | |
} | |
}, | |
"hub/query" : function hubQuery(topic /* query, query, query, .., */) { | |
var self = this; | |
var batches = self[BATCHES]; | |
var cache = self[CACHE]; | |
var q = []; | |
var id = []; | |
var ast; | |
var i; | |
var j; | |
var iMax; | |
var queries; | |
var query; | |
// Create deferred batch | |
var batch = when.defer(); | |
try { | |
// Slice and flatten queries | |
queries = CONCAT.apply(ARRAY_PROTO, SLICE.call(arguments, 1)); | |
// Iterate queries | |
for (i = 0, iMax = queries[LENGTH]; i < iMax; i++) { | |
// Init Query | |
query = Query(queries[i]); | |
// Get AST | |
ast = query.ast(); | |
// If we have an ID | |
if (ast[LENGTH] > 0) { | |
// Store raw ID | |
id[i] = ast[0][RAW]; | |
} | |
// Get reduced AST | |
ast = query.reduce(cache).ast(); | |
// Step backwards through AST | |
for (j = ast[LENGTH]; j-- > 0;) { | |
// If this op is not resolved | |
if (!ast[j][RESOLVED]) { | |
// Add rewritten (and reduced) query to q | |
PUSH.call(q, query.rewrite()); | |
break; | |
} | |
} | |
} | |
// If all queries were fully reduced, we can quick resolve | |
if (q[LENGTH] === 0) { | |
// Iterate queries | |
for (i = 0; i < iMax; i++) { | |
// If we have a corresponding ID, fetch from cache | |
if (i in id) { | |
queries[i] = cache[id[i]]; | |
} | |
} | |
// Resolve batch | |
batch.resolve(queries); | |
} | |
else { | |
// Store properties on batch | |
batch[TOPIC] = topic; | |
batch[QUERIES] = queries; | |
batch[ID] = id; | |
batch[Q] = q; | |
// Add batch to batches | |
batches.push(batch); | |
} | |
} | |
catch (e) { | |
batch.reject(e); | |
} | |
// Return promise | |
return batch.promise; | |
} | |
}); | |
QueryService.config = function config(_config) { | |
return merge.call(CONFIG, _config); | |
}; | |
return QueryService; | |
}); | |
/** | |
* TroopJS Data cache component | |
* @license MIT http://troopjs.mit-license.org/ © Mikael Karon mailto:[email protected] | |
*/ | |
/*global define:false */ | |
define('troopjs-data/cache/component', [ "troopjs-core/component/gadget" ], function CacheModule(Gadget) { | |
/*jshint laxbreak:true */ | |
var UNDEFINED; | |
var FALSE = false; | |
var NULL = null; | |
var OBJECT = Object; | |
var ARRAY = Array; | |
var SECOND = 1000; | |
var INTERVAL = "interval"; | |
var GENERATIONS = "generations"; | |
var AGE = "age"; | |
var HEAD = "head"; | |
var NEXT = "next"; | |
var EXPIRES = "expires"; | |
var CONSTRUCTOR = "constructor"; | |
var LENGTH = "length"; | |
var _ID = "id"; | |
var _MAXAGE = "maxAge"; | |
var _EXPIRES = "expires"; | |
var _INDEXED = "indexed"; | |
var _COLLAPSED = "collapsed"; | |
/** | |
* Internal method to put a node in the cache | |
* @param node Node | |
* @param constructor Constructor of value | |
* @param now Current time (seconds) | |
* @returns Cached node | |
*/ | |
function _put(node, constructor, now) { | |
var self = this; | |
var result; | |
var id; | |
var i; | |
var iMax; | |
var expires; | |
var expired; | |
var head; | |
var current; | |
var next; | |
var generation; | |
var generations = self[GENERATIONS]; | |
var property; | |
var value; | |
// First add node to cache (or get the already cached instance) | |
cache : { | |
// Can't cache if there is no _ID | |
if (!(_ID in node)) { | |
result = node; // Reuse ref to node (avoids object creation) | |
break cache; | |
} | |
// Get _ID | |
id = node[_ID]; | |
// In cache, get it! | |
if (id in self) { | |
result = self[id]; | |
break cache; | |
} | |
// Not in cache, add it! | |
result = self[id] = node; // Reuse ref to node (avoids object creation) | |
// Update _INDEXED | |
result[_INDEXED] = now; | |
} | |
// We have to deep traverse the graph before we do any expiration (as more data for this object can be available) | |
// Check that this is an ARRAY | |
if (constructor === ARRAY) { | |
// Index all values | |
for (i = 0, iMax = node[LENGTH]; i < iMax; i++) { | |
// Keep value | |
value = node[i]; | |
// Get constructor of value (safely, falling back to UNDEFINED) | |
constructor = value === NULL || value === UNDEFINED | |
? UNDEFINED | |
: value[CONSTRUCTOR]; | |
// Do magic comparison to see if we recursively put this in the cache, or plain put | |
result[i] = (constructor === OBJECT || constructor === ARRAY && value[LENGTH] !== 0) | |
? _put.call(self, value, constructor, now) | |
: value; | |
} | |
} | |
// Check that this is an OBJECT | |
else if (constructor === OBJECT) { | |
// Index all properties | |
for (property in node) { | |
// Except the _ID property | |
// or the _COLLAPSED property, if it's false | |
if (property === _ID | |
|| (property === _COLLAPSED && result[_COLLAPSED] === FALSE)) { | |
continue; | |
} | |
// Keep value | |
value = node[property]; | |
// Get constructor of value (safely, falling back to UNDEFINED) | |
constructor = value === NULL || value === UNDEFINED | |
? UNDEFINED | |
: value[CONSTRUCTOR]; | |
// Do magic comparison to see if we recursively put this in the cache, or plain put | |
result[property] = (constructor === OBJECT || constructor === ARRAY && value[LENGTH] !== 0) | |
? _put.call(self, value, constructor, now) | |
: value; | |
} | |
} | |
// Check if we need to move result between generations | |
move : { | |
// Break fast if id is NULL | |
if (id === NULL) { | |
break move; | |
} | |
// Calculate expiration and floor | |
// '>>>' means convert anything other than posiitive integer into 0 | |
expires = 0 | now + (result[_MAXAGE] >>> 0); | |
remove : { | |
// Fail fast if there is no old expiration | |
if (!(_EXPIRES in result)) { | |
break remove; | |
} | |
// Get current expiration | |
expired = result[_EXPIRES]; | |
// If expiration has not changed, we can continue | |
if (expired === expires) { | |
break move; | |
} | |
// Remove ref from generation (if that generation exists) | |
if (expired in generations) { | |
delete generations[expired][id]; | |
} | |
} | |
add : { | |
// Update expiration time | |
result[_EXPIRES] = expires; | |
// Existing generation | |
if (expires in generations) { | |
// Add result to generation | |
generations[expires][id] = result; | |
break add; | |
} | |
// Create generation with expiration set | |
(generation = generations[expires] = {})[EXPIRES] = expires; | |
// Add result to generation | |
generation[id] = result; | |
// Short circuit if there is no head | |
if (generations[HEAD] === UNDEFINED) { | |
generations[HEAD] = generation; | |
break add; | |
} | |
// Step through list as long as there is a next, and expiration is "older" than the next expiration | |
for (current = head = generations[HEAD]; (next = current[NEXT]) !== UNDEFINED && next[EXPIRES] < expires; current = next); | |
// Check if we're still on the head and if we're younger | |
if (current === head && current[EXPIRES] > expires) { | |
// Next generation is the current one (head) | |
generation[NEXT] = current; | |
// Reset head to new generation | |
generations[HEAD] = generation; | |
break add; | |
} | |
// Insert new generation between current and current.next | |
generation[NEXT] = current[NEXT]; | |
current[NEXT] = generation; | |
} | |
} | |
return result; | |
} | |
return Gadget.extend(function (age) { | |
var me = this; | |
me[AGE] = age || (60 * SECOND); | |
me[GENERATIONS] = {}; | |
}, { | |
"displayName" : "data/cache/component", | |
"sig/start" : function start() { | |
var self = this; | |
var generations = self[GENERATIONS]; | |
// Create new sweep interval | |
self[INTERVAL] = INTERVAL in self | |
? self[INTERVAL] | |
: setInterval(function sweep() { | |
// Calculate expiration of this generation | |
var expires = 0 | new Date().getTime() / SECOND; | |
var property; | |
var current; | |
// Get head | |
current = generations[HEAD]; | |
// Fail fast if there's no head | |
if (current === UNDEFINED) { | |
return; | |
} | |
do { | |
// Exit if this generation is to young | |
if (current[EXPIRES] > expires) { | |
break; | |
} | |
// Iterate all properties on current | |
for (property in current) { | |
// And is it not a reserved property | |
if (property === EXPIRES || property === NEXT || property === GENERATIONS) { | |
continue; | |
} | |
// Delete from self (cache) | |
delete self[property]; | |
} | |
// Delete generation | |
delete generations[current[EXPIRES]]; | |
} | |
// While there's a next | |
while ((current = current[NEXT])); | |
// Reset head | |
generations[HEAD] = current; | |
}, self[AGE]); | |
}, | |
"sig/stop" : function stop() { | |
var self = this; | |
// Only do this if we have an interval | |
if (INTERVAL in self) { | |
// Clear interval | |
clearInterval(self[INTERVAL]); | |
// Reset interval | |
delete self[INTERVAL]; | |
} | |
}, | |
"sig/finalize" : function finalize() { | |
var self = this; | |
var property; | |
// Iterate all properties on self | |
for (property in self) { | |
// Don't delete non-objects or objects that don't ducktype cachable | |
if (self[property][CONSTRUCTOR] !== OBJECT || !(_ID in self[property])) { | |
continue; | |
} | |
// Delete from self (cache) | |
delete self[property]; | |
} | |
}, | |
/** | |
* Puts a node into the cache | |
* @param node Node to add (object || array) | |
* @returns Cached node (if it existed in the cache before), otherwise the node sent in | |
*/ | |
"put" : function put(node) { | |
var self = this; | |
// Get constructor of node (safely, falling back to UNDEFINED) | |
var constructor = node === NULL || node === UNDEFINED | |
? UNDEFINED | |
: node[CONSTRUCTOR]; | |
// Do magic comparison to see if we should cache this object | |
return constructor === OBJECT || constructor === ARRAY && node[LENGTH] !== 0 | |
? _put.call(self, node, constructor, 0 | new Date().getTime() / SECOND) | |
: node; | |
} | |
}); | |
}); | |
/*! | |
* TroopJS ajax/service module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-browser/ajax/service',[ "troopjs-core/component/service", "jquery", "troopjs-utils/merge" ], function AjaxModule(Service, $, merge) { | |
var TRACE = "trace"; | |
return Service.extend({ | |
"displayName" : "browser/ajax/service", | |
"hub/ajax" : function ajax(topic, settings) { | |
// Request | |
return $.ajax(merge.call({ | |
"headers": { | |
"x-request-id": new Date().getTime(), | |
"x-components": topic[TRACE] instanceof Function ? topic[TRACE]() : topic | |
} | |
}, settings)); | |
} | |
}); | |
}); | |
/*! | |
* TroopJS widget component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-browser/component/widget',[ "troopjs-core/component/gadget", "jquery", "troopjs-jquery/weave", "troopjs-jquery/action" ], function WidgetModule(Gadget, $) { | |
var UNDEFINED; | |
var NULL = null; | |
var FUNCTION = Function; | |
var ARRAY_PROTO = Array.prototype; | |
var SHIFT = ARRAY_PROTO.shift; | |
var UNSHIFT = ARRAY_PROTO.unshift; | |
var $TRIGGER = $.fn.trigger; | |
var $ONE = $.fn.one; | |
var $BIND = $.fn.bind; | |
var $UNBIND = $.fn.unbind; | |
var RE = /^dom(?::(\w+))?\/([^\.]+(?:\.(.+))?)/; | |
var $ELEMENT = "$element"; | |
var $PROXIES = "$proxies"; | |
var ONE = "one"; | |
var FEATURES = "features"; | |
var ATTR_WEAVE = "[data-weave]"; | |
var ATTR_WOVEN = "[data-woven]"; | |
/** | |
* Creates a proxy of the inner method 'handlerProxy' with the 'topic', 'widget' and handler parameters set | |
* @param topic event topic | |
* @param widget target widget | |
* @param handler target handler | |
* @returns {Function} proxied handler | |
*/ | |
function eventProxy(topic, widget, handler) { | |
/** | |
* Creates a proxy of the outer method 'handler' that first adds 'topic' to the arguments passed | |
* @returns result of proxied hanlder invocation | |
*/ | |
return function handlerProxy() { | |
// Add topic to front of arguments | |
UNSHIFT.call(arguments, topic); | |
// Apply with shifted arguments to handler | |
return handler.apply(widget, arguments); | |
}; | |
} | |
/** | |
* Creates a proxy of the inner method 'render' with the '$fn' parameter set | |
* @param $fn jQuery method | |
* @returns {Function} proxied render | |
*/ | |
function renderProxy($fn) { | |
/** | |
* Renders contents into element | |
* @param contents (Function | String) Template/String to render | |
* @param data (Object) If contents is a template - template data (optional) | |
* @returns self | |
*/ | |
function render(/* contents, data, ... */) { | |
var self = this; | |
var arg = arguments; | |
// Shift contents from first argument | |
var contents = SHIFT.call(arg); | |
// Call render with contents (or result of contents if it's a function) | |
$fn.call(self[$ELEMENT], contents instanceof FUNCTION ? contents.apply(self, arg) : contents); | |
return self.weave(); | |
} | |
return render; | |
} | |
return Gadget.extend(function Widget($element, displayName) { | |
var self = this; | |
self[$ELEMENT] = $element; | |
if (displayName) { | |
self.displayName = displayName; | |
} | |
}, { | |
"displayName" : "browser/component/widget", | |
"sig/initialize" : function initialize() { | |
var self = this; | |
var $element = self[$ELEMENT]; | |
var $proxies = self[$PROXIES] = []; | |
var $proxy; | |
var key; | |
var value; | |
var matches; | |
var topic; | |
// Loop over each property in widget | |
for (key in self) { | |
// Get value | |
value = self[key]; | |
// Continue if value is not a function | |
if (!(value instanceof FUNCTION)) { | |
continue; | |
} | |
// Match signature in key | |
matches = RE.exec(key); | |
if (matches !== NULL) { | |
// Get topic | |
topic = matches[2]; | |
// Replace value with a scoped proxy | |
value = eventProxy(topic, self, value); | |
// Either ONE or BIND element | |
(matches[2] === ONE ? $ONE : $BIND).call($element, topic, self, value); | |
// Create and store $proxy | |
$proxies[$proxies.length] = $proxy = [topic, value]; | |
// Store features | |
$proxy[FEATURES] = matches[1]; | |
// NULL value | |
self[key] = NULL; | |
} | |
} | |
}, | |
"sig/finalize" : function finalize() { | |
var self = this; | |
var $element = self[$ELEMENT]; | |
var $proxies = self[$PROXIES]; | |
var $proxy; | |
// Loop over subscriptions | |
while (($proxy = $proxies.shift()) !== UNDEFINED) { | |
$element.unbind($proxy[0], $proxy[1]); | |
} | |
delete self[$ELEMENT]; | |
}, | |
/** | |
* Weaves all children of $element | |
* @returns self | |
*/ | |
"weave" : function weave() { | |
return this[$ELEMENT].find(ATTR_WEAVE).weave(); | |
}, | |
/** | |
* Unweaves all children of $element _and_ self | |
* @returns self | |
*/ | |
"unweave" : function unweave() { | |
return this[$ELEMENT].find(ATTR_WOVEN).addBack().unweave(); | |
}, | |
/** | |
* Binds event from $element, exactly once | |
* @returns self | |
*/ | |
"one" : function one() { | |
var self = this; | |
$ONE.apply(self[$ELEMENT], arguments); | |
return self; | |
}, | |
/** | |
* Binds event to $element | |
* @returns self | |
*/ | |
"bind" : function bind() { | |
var self = this; | |
$BIND.apply(self[$ELEMENT], arguments); | |
return self; | |
}, | |
/** | |
* Unbinds event from $element | |
* @returns self | |
*/ | |
"unbind" : function unbind() { | |
var self = this; | |
$UNBIND.apply(self[$ELEMENT], arguments); | |
return self; | |
}, | |
/** | |
* Triggers event on $element | |
* @returns self | |
*/ | |
"trigger" : function trigger() { | |
var self = this; | |
$TRIGGER.apply(self[$ELEMENT], arguments); | |
return self; | |
}, | |
/** | |
* Renders content and inserts it before $element | |
*/ | |
"before" : renderProxy($.fn.before), | |
/** | |
* Renders content and inserts it after $element | |
*/ | |
"after" : renderProxy($.fn.after), | |
/** | |
* Renders content and replaces $element contents | |
*/ | |
"html" : renderProxy($.fn.html), | |
/** | |
* Renders content and replaces $element contents | |
*/ | |
"text" : renderProxy($.fn.text), | |
/** | |
* Renders content and appends it to $element | |
*/ | |
"append" : renderProxy($.fn.append), | |
/** | |
* Renders content and prepends it to $element | |
*/ | |
"prepend" : renderProxy($.fn.prepend) | |
}); | |
}); | |
/*! | |
* TroopJS dimensions/widget module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-browser/dimensions/widget',[ "../component/widget", "troopjs-jquery/dimensions", "troopjs-jquery/resize" ], function DimensionsModule(Widget) { | |
var DIMENSIONS = "dimensions"; | |
function onDimensions($event, w, h) { | |
var self = $event.data; | |
self.publish(self.displayName, w, h, $event); | |
} | |
return Widget.extend(function DimensionsWidget($element, displayName, dimensions) { | |
this[DIMENSIONS] = dimensions; | |
}, { | |
"displayName" : "browser/dimensions/widget", | |
"sig/initialize" : function initialize(signal) { | |
var self = this; | |
self.bind(DIMENSIONS + "." + self[DIMENSIONS], self, onDimensions); | |
}, | |
"sig/start" : function start() { | |
this.trigger("resize." + DIMENSIONS); | |
}, | |
"sig/finalize" : function finalize() { | |
var self = this; | |
self.unbind(DIMENSIONS + "." + self[DIMENSIONS], onDimensions); | |
} | |
}); | |
}); | |
/** | |
* TroopJS store/base module | |
* @license MIT http://troopjs.mit-license.org/ © Mikael Karon mailto:[email protected] | |
*/ | |
/*global define:false */ | |
define('troopjs-browser/store/base',[ "compose", "troopjs-core/component/gadget", "when" ], function StoreModule(Compose, Gadget, when) { | |
var STORAGE = "storage"; | |
return Gadget.extend({ | |
"storage" : Compose.required, | |
"set" : function set(key, value) { | |
// JSON encoded 'value' then store as 'key' | |
return when(this[STORAGE].setItem(key, JSON.stringify(value))); | |
}, | |
"get" : function get(key) { | |
// Get value from 'key', parse JSON | |
return when(JSON.parse(this[STORAGE].getItem(key))); | |
}, | |
"remove" : function remove(key) { | |
// Remove key | |
return when(this[STORAGE].removeItem(key)); | |
}, | |
"clear" : function clear() { | |
// Clear | |
return when(this[STORAGE].clear()); | |
} | |
}); | |
}); | |
/** | |
* TroopJS store/session module | |
* @license MIT http://troopjs.mit-license.org/ © Mikael Karon mailto:[email protected] | |
*/ | |
/*global define:false */ | |
define('troopjs-browser/store/session',[ "compose", "./base" ], function StoreSessionModule(Compose, Store) { | |
return Compose.create(Store, { | |
"displayName" : "browser/store/session", | |
"storage": window.sessionStorage | |
}); | |
}); | |
/** | |
* TroopJS store/local module | |
* @license MIT http://troopjs.mit-license.org/ © Mikael Karon mailto:[email protected] | |
*/ | |
/*global define:false */ | |
define('troopjs-browser/store/local',[ "compose", "./base" ], function StoreLocalModule(Compose, Store) { | |
return Compose.create(Store, { | |
"displayName" : "browser/store/local", | |
"storage" : window.localStorage | |
}); | |
}); | |
/*! | |
* TroopJS route/widget module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-browser/route/widget',[ "../component/widget", "troopjs-utils/uri", "troopjs-jquery/hashchange" ], function RouteWidgetModule(Widget, URI) { | |
var HASHCHANGE = "hashchange"; | |
var ROUTE = "route"; | |
var RE = /^#/; | |
function onHashChange($event) { | |
var self = $event.data; | |
// Create URI | |
var uri = URI($event.target.location.hash.replace(RE, "")); | |
// Convert to string | |
var route = uri.toString(); | |
// Did anything change? | |
if (route !== self[ROUTE]) { | |
// Store new value | |
self[ROUTE] = route; | |
// Publish route | |
self.publish(self.displayName, uri, $event); | |
} | |
} | |
return Widget.extend({ | |
"displayName" :"browser/route/widget", | |
"sig/initialize" : function initialize() { | |
var self = this; | |
self.bind(HASHCHANGE, self, onHashChange); | |
}, | |
"sig/start" : function start() { | |
this.trigger(HASHCHANGE); | |
}, | |
"sig/finalize" : function finalize() { | |
this.unbind(HASHCHANGE, onHashChange); | |
} | |
}); | |
}); | |
/*! | |
* TroopJS widget/application component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*global define:false */ | |
define('troopjs-browser/application/widget',[ "module", "../component/widget", "when" ], function ApplicationWidgetModule(module, Widget, when) { | |
/*jshint laxbreak:true */ | |
var CHILDREN = "children"; | |
var ARRAY_SLICE = Array.prototype.slice; | |
function forward(signal) { | |
var self = this; | |
var args = arguments; | |
var children = self[CHILDREN]; | |
var length = children ? children.length : 0; | |
var index = 0; | |
function next(_args) { | |
args = _args || args; | |
return length > index | |
? when(children[index++].signal(signal), next) | |
: when.resolve(args); | |
} | |
return next(); | |
} | |
return Widget.extend(function ApplicationWidget($element, name, children) { | |
this[CHILDREN] = children; | |
}, { | |
"displayName" : "browser/application/widget", | |
"sig/initialize" : forward, | |
"sig/start" : function start() { | |
var self = this; | |
var _weave = self.weave; | |
var args = arguments; | |
return forward.apply(self, args).then(function started() { | |
return _weave.apply(self, ARRAY_SLICE.call(args, 1)); | |
}); | |
}, | |
"sig/stop" : function stop() { | |
var self = this; | |
var _unweave = self.unweave; | |
var args = arguments; | |
return _unweave.apply(self, ARRAY_SLICE.call(args, 1)).then(function stopped() { | |
return forward.apply(self, args); | |
}); | |
}, | |
"sig/finalize" : forward | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* TroopJS Bundle - 1.0.7-0-gf886cba | |
* http://troopjs.com/ | |
* Copyright (c) 2012 Mikael Karon <[email protected]> | |
* Licensed MIT | |
*/ | |
/*! | |
* TroopJS RequireJS template plug-in | |
* | |
* parts of code from require-cs 0.4.0+ Copyright (c) 2010-2011, The Dojo Foundation | |
* | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*jshint strict:false, smarttabs:true, laxbreak:true, newcap:false, loopfunc:true */ | |
/*global define:true */ | |
define('troopjs-requirejs/template',[],function TemplateModule() { | |
var FACTORIES = { | |
"node" : function () { | |
// Using special require.nodeRequire, something added by r.js. | |
var fs = require.nodeRequire("fs"); | |
return function fetchText(path, callback) { | |
callback(fs.readFileSync(path, 'utf8')); | |
}; | |
}, | |
"browser" : function () { | |
// Would love to dump the ActiveX crap in here. Need IE 6 to die first. | |
var progIds = [ "Msxml2.XMLHTTP", "Microsoft.XMLHTTP", "Msxml2.XMLHTTP.4.0"]; | |
var progId; | |
var XHR; | |
var i; | |
if (typeof XMLHttpRequest !== "undefined") { | |
XHR = XMLHttpRequest; | |
} | |
else { | |
for (i = 0; i < 3; i++) { | |
progId = progIds[i]; | |
try { | |
new ActiveXObject(progId); | |
XHR = function(){ | |
return new ActiveXObject(progId); | |
}; | |
break; | |
} | |
catch (e) { | |
} | |
} | |
if (!XHR){ | |
throw new Error("XHR: XMLHttpRequest not available"); | |
} | |
} | |
return function fetchText(url, callback) { | |
var xhr = new XHR(); | |
xhr.open('GET', url, true); | |
xhr.onreadystatechange = function (evt) { | |
// Do not explicitly handle errors, those should be | |
// visible via console output in the browser. | |
if (xhr.readyState === 4) { | |
callback(xhr.responseText); | |
} | |
}; | |
xhr.send(null); | |
}; | |
}, | |
"rhino" : function () { | |
var encoding = "utf-8"; | |
var lineSeparator = java.lang.System.getProperty("line.separator"); | |
// Why Java, why is this so awkward? | |
return function fetchText(path, callback) { | |
var file = new java.io.File(path); | |
var input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)); | |
var stringBuffer = new java.lang.StringBuffer(); | |
var line; | |
var content = ""; | |
try { | |
line = input.readLine(); | |
// Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 | |
// http://www.unicode.org/faq/utf_bom.html | |
// Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: | |
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 | |
if (line && line.length() && line.charAt(0) === 0xfeff) { | |
// Eat the BOM, since we've already found the encoding on this file, | |
// and we plan to concatenating this buffer with others; the BOM should | |
// only appear at the top of a file. | |
line = line.substring(1); | |
} | |
stringBuffer.append(line); | |
while ((line = input.readLine()) !== null) { | |
stringBuffer.append(lineSeparator); | |
stringBuffer.append(line); | |
} | |
// Make sure we return a JavaScript string and not a Java string. | |
content = String(stringBuffer.toString()); // String | |
} finally { | |
input.close(); | |
} | |
callback(content); | |
}; | |
}, | |
"borked" : function () { | |
return function fetchText() { | |
throw new Error("Environment unsupported."); | |
}; | |
} | |
}; | |
var RE_SANITIZE = /^[\n\t\r]+|[\n\t\r]+$/g; | |
var RE_BLOCK = /<%(=)?([\S\s]*?)%>/g; | |
var RE_TOKENS = /<%(\d+)%>/gm; | |
var RE_REPLACE = /(["\n\t\r])/gm; | |
var RE_CLEAN = /o \+= "";| \+ ""/gm; | |
var EMPTY = ""; | |
var REPLACE = { | |
"\"" : "\\\"", | |
"\n" : "\\n", | |
"\t" : "\\t", | |
"\r" : "\\r" | |
}; | |
/** | |
* Compiles template | |
* | |
* @param body Template body | |
* @returns {Function} | |
*/ | |
function compile(body) { | |
var blocks = []; | |
var length = 0; | |
function blocksTokens(original, prefix, block) { | |
blocks[length] = prefix | |
? "\" +" + block + "+ \"" | |
: "\";" + block + "o += \""; | |
return "<%" + String(length++) + "%>"; | |
} | |
function tokensBlocks(original, token) { | |
return blocks[token]; | |
} | |
function replace(original, token) { | |
return REPLACE[token] || token; | |
} | |
return ("function template(data) { var o = \"" | |
// Sanitize body before we start templating | |
+ body.replace(RE_SANITIZE, "") | |
// Replace script blocks with tokens | |
.replace(RE_BLOCK, blocksTokens) | |
// Replace unwanted tokens | |
.replace(RE_REPLACE, replace) | |
// Replace tokens with script blocks | |
.replace(RE_TOKENS, tokensBlocks) | |
+ "\"; return o; }") | |
// Clean | |
.replace(RE_CLEAN, EMPTY); | |
} | |
var buildMap = {}; | |
var fetchText = FACTORIES[ typeof process !== "undefined" && process.versions && !!process.versions.node | |
? "node" | |
: (typeof window !== "undefined" && window.navigator && window.document) || typeof importScripts !== "undefined" | |
? "browser" | |
: typeof Packages !== "undefined" | |
? "rhino" | |
: "borked" ](); | |
return { | |
load: function (name, parentRequire, load, config) { | |
var path = parentRequire.toUrl(name); | |
fetchText(path, function (text) { | |
try { | |
text = "define(function() { return " + compile(text, name, path, config.template) + "; })"; | |
} | |
catch (err) { | |
err.message = "In " + path + ", " + err.message; | |
throw(err); | |
} | |
if (config.isBuild) { | |
buildMap[name] = text; | |
} | |
// IE with conditional comments on cannot handle the | |
// sourceURL trick, so skip it if enabled | |
/*@if (@_jscript) @else @*/ | |
else { | |
text += "\n//@ sourceURL='" + path +"'"; | |
} | |
/*@end@*/ | |
load.fromText(name, text); | |
// Give result to load. Need to wait until the module | |
// is fully parse, which will happen after this | |
// execution. | |
parentRequire([name], function (value) { | |
load(value); | |
}); | |
}); | |
}, | |
write: function (pluginName, name, write) { | |
if (buildMap.hasOwnProperty(name)) { | |
write.asModule(pluginName + "!" + name, buildMap[name]); | |
} | |
} | |
}; | |
}); | |
/*! | |
* TroopJS jQuery hashchange plug-in | |
* | |
* Normalized hashchange event, ripped a _lot_ of code from | |
* https://github.com/millermedeiros/Hasher | |
* | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*jshint strict:false, smarttabs:true, laxbreak:true, evil:true */ | |
/*global define:true */ | |
define('troopjs-jquery/hashchange',[ "jquery" ], function HashchangeModule($) { | |
var INTERVAL = "interval"; | |
var HASHCHANGE = "hashchange"; | |
var ONHASHCHANGE = "on" + HASHCHANGE; | |
var RE_HASH = /#(.*)$/; | |
var RE_LOCAL = /\?/; | |
// hack based on this: http://code.google.com/p/closure-compiler/issues/detail?id=47#c13 | |
var _isIE = /**@preserve@cc_on !@*/0; | |
function getHash(window) { | |
// parsed full URL instead of getting location.hash because Firefox | |
// decode hash value (and all the other browsers don't) | |
// also because of IE8 bug with hash query in local file | |
var result = RE_HASH.exec(window.location.href); | |
return result && result[1] | |
? decodeURIComponent(result[1]) | |
: ""; | |
} | |
function Frame(document) { | |
var self = this; | |
var element; | |
self.element = element = document.createElement("iframe"); | |
element.src = "about:blank"; | |
element.style.display = "none"; | |
} | |
Frame.prototype = { | |
getElement : function () { | |
return this.element; | |
}, | |
getHash : function () { | |
return this.element.contentWindow.frameHash; | |
}, | |
update : function (hash) { | |
var self = this; | |
var document = self.element.contentWindow.document; | |
// Quick return if hash has not changed | |
if (self.getHash() === hash) { | |
return; | |
} | |
// update iframe content to force new history record. | |
// based on Really Simple History, SWFAddress and YUI.history. | |
document.open(); | |
document.write("<html><head><title>' + document.title + '</title><script type='text/javascript'>var frameHash='" + hash + "';</script></head><body> </body></html>"); | |
document.close(); | |
} | |
}; | |
$.event.special[HASHCHANGE] = { | |
/** | |
* @param data (Anything) Whatever eventData (optional) was passed in | |
* when binding the event. | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
* @param eventHandle (Function) The actual function that will be bound | |
* to the browser’s native event (this is used internally for the | |
* beforeunload event, you’ll never use it). | |
*/ | |
setup : function hashChangeSetup(data, namespaces, eventHandle) { | |
var window = this; | |
// Quick return if we support onHashChange natively | |
// FF3.6+, IE8+, Chrome 5+, Safari 5+ | |
if (ONHASHCHANGE in window) { | |
return false; | |
} | |
// Make sure we're always a window | |
if (!$.isWindow(window)) { | |
throw new Error("Unable to bind 'hashchange' to a non-window object"); | |
} | |
var $window = $(window); | |
var hash = getHash(window); | |
var location = window.location; | |
$window.data(INTERVAL, window.setInterval(_isIE | |
? (function hashChangeIntervalWrapper() { | |
var document = window.document; | |
var _isLocal = location.protocol === "file:"; | |
var frame = new Frame(document); | |
document.body.appendChild(frame.getElement()); | |
frame.update(hash); | |
return function hashChangeInterval() { | |
var oldHash = hash; | |
var newHash; | |
var windowHash = getHash(window); | |
var frameHash = frame.getHash(); | |
// Detect changes made pressing browser history buttons. | |
// Workaround since history.back() and history.forward() doesn't | |
// update hash value on IE6/7 but updates content of the iframe. | |
if (frameHash !== hash && frameHash !== windowHash) { | |
// Fix IE8 while offline | |
newHash = decodeURIComponent(frameHash); | |
if (hash !== newHash) { | |
hash = newHash; | |
frame.update(hash); | |
$window.trigger(HASHCHANGE, [ newHash, oldHash ]); | |
} | |
// Sync location.hash with frameHash | |
location.hash = "#" + encodeURI(_isLocal | |
? frameHash.replace(RE_LOCAL, "%3F") | |
: frameHash); | |
} | |
// detect if hash changed (manually or using setHash) | |
else if (windowHash !== hash) { | |
// Fix IE8 while offline | |
newHash = decodeURIComponent(windowHash); | |
if (hash !== newHash) { | |
hash = newHash; | |
$window.trigger(HASHCHANGE, [ newHash, oldHash ]); | |
} | |
} | |
}; | |
})() | |
: function hashChangeInterval() { | |
var oldHash = hash; | |
var newHash; | |
var windowHash = getHash(window); | |
if (windowHash !== hash) { | |
// Fix IE8 while offline | |
newHash = decodeURIComponent(windowHash); | |
if (hash !== newHash) { | |
hash = newHash; | |
$window.trigger(HASHCHANGE, [ newHash, oldHash ]); | |
} | |
} | |
}, 25)); | |
}, | |
/** | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
*/ | |
teardown : function hashChangeTeardown(namespaces) { | |
var window = this; | |
// Quick return if we support onHashChange natively | |
if (ONHASHCHANGE in window) { | |
return false; | |
} | |
window.clearInterval($.data(window, INTERVAL)); | |
} | |
}; | |
}); | |
/*! | |
* TroopJS Utils getargs module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
define('troopjs-utils/getargs',[],function GetArgsModule() { | |
var PUSH = Array.prototype.push; | |
var SUBSTRING = String.prototype.substring; | |
var RE_BOOLEAN = /^(?:false|true)$/i; | |
var RE_BOOLEAN_TRUE = /^true$/i; | |
var RE_DIGIT = /^\d+$/; | |
return function getargs() { | |
var self = this; | |
var result = []; | |
var length; | |
var from; | |
var to; | |
var i; | |
var c; | |
var a; | |
var q = false; | |
// Iterate over string | |
for (from = to = i = 0, length = self.length; i < length; i++) { | |
// Get char | |
c = self.charAt(i); | |
switch(c) { | |
case "\"" : | |
case "'" : | |
// If we are currently quoted... | |
if (q === c) { | |
// Stop quote | |
q = false; | |
// Store result (no need to convert, we know this is a string) | |
PUSH.call(result, SUBSTRING.call(self, from, to)); | |
} | |
// Otherwise | |
else { | |
// Start quote | |
q = c; | |
} | |
// Update from/to | |
from = to = i + 1; | |
break; | |
case "," : | |
// Continue if we're quoted | |
if (q) { | |
to = i + 1; | |
break; | |
} | |
// If we captured something... | |
if (from !== to) { | |
a = SUBSTRING.call(self, from, to); | |
if (RE_BOOLEAN.test(a)) { | |
a = RE_BOOLEAN_TRUE.test(a); | |
} | |
else if (RE_DIGIT.test(a)) { | |
a = +a; | |
} | |
// Store result | |
PUSH.call(result, a); | |
} | |
// Update from/to | |
from = to = i + 1; | |
break; | |
case " " : | |
case "\t" : | |
// Continue if we're quoted | |
if (q) { | |
to = i + 1; | |
break; | |
} | |
// Update from/to | |
if (from === to) { | |
from = to = i + 1; | |
} | |
break; | |
default : | |
// Update to | |
to = i + 1; | |
} | |
} | |
// If we captured something... | |
if (from !== to) { | |
a = SUBSTRING.call(self, from, to); | |
if (RE_BOOLEAN.test(a)) { | |
a = RE_BOOLEAN_TRUE.test(a); | |
} | |
else if (RE_DIGIT.test(a)) { | |
a = +a; | |
} | |
// Store result | |
PUSH.call(result, a); | |
} | |
return result; | |
}; | |
}); | |
/*! | |
* TroopJS jQuery action plug-in | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*jshint strict:false, smarttabs:true, laxbreak:true */ | |
/*global define:true */ | |
define('troopjs-jquery/action',[ "jquery", "troopjs-utils/getargs" ], function ActionModule($, getargs) { | |
var UNDEFINED; | |
var FALSE = false; | |
var NULL = null; | |
var SLICE = Array.prototype.slice; | |
var ACTION = "action"; | |
var ORIGINALEVENT = "originalEvent"; | |
var RE_ACTION = /^([\w\d\s_\-\/]+)(?:\.([\w\.]+))?(?:\((.*)\))?$/; | |
var RE_DOT = /\.+/; | |
/** | |
* Namespace iterator | |
* @param namespace (string) namespace | |
* @param index (number) index | |
*/ | |
function namespaceIterator(namespace, index) { | |
return namespace ? namespace + "." + ACTION : NULL; | |
} | |
/** | |
* Action handler | |
* @param $event (jQuery.Event) event | |
*/ | |
function onAction($event) { | |
// Set $target | |
var $target = $(this); | |
// Get argv | |
var argv = SLICE.call(arguments, 1); | |
// Extract type | |
var type = ORIGINALEVENT in $event | |
? $event[ORIGINALEVENT].type | |
: ACTION; | |
// Extract name | |
var name = $event[ACTION]; | |
// Reset $event.type | |
$event.type = ACTION + "/" + name + "." + type; | |
// Trigger 'ACTION/{name}.{type}' | |
$target.trigger($event, argv); | |
// No handler, try without namespace, but exclusive | |
if ($event.result !== FALSE) { | |
// Reset $event.type | |
$event.type = ACTION + "/" + name + "!"; | |
// Trigger 'ACTION/{name}' | |
$target.trigger($event, argv); | |
// Still no handler, try generic action with namespace | |
if ($event.result !== FALSE) { | |
// Reset $event.type | |
$event.type = ACTION + "." + type; | |
// Trigger 'ACTION.{type}' | |
$target.trigger($event, argv); | |
} | |
} | |
} | |
/** | |
* Internal handler | |
* | |
* @param $event jQuery event | |
*/ | |
function handler($event) { | |
// Get closest element that has an action defined | |
var $target = $($event.target).closest("[data-action]"); | |
// Fail fast if there is no action available | |
if ($target.length === 0) { | |
return; | |
} | |
// Extract all data in one go | |
var $data = $target.data(); | |
// Extract matches from 'data-action' | |
var matches = RE_ACTION.exec($data[ACTION]); | |
// Return fast if action parameter was f*cked (no matches) | |
if (matches === NULL) { | |
return; | |
} | |
// Extract action name | |
var name = matches[1]; | |
// Extract action namespaces | |
var namespaces = matches[2]; | |
// Extract action args | |
var args = matches[3]; | |
// If there are action namespaces, make sure we're only triggering action on applicable types | |
if (namespaces !== UNDEFINED && !RegExp(namespaces.split(RE_DOT).join("|")).test($event.type)) { | |
return; | |
} | |
// Split args by separator (if there were args) | |
var argv = args !== UNDEFINED | |
? getargs.call(args) | |
: []; | |
// Iterate argv to determine arg type | |
$.each(argv, function argsIterator(i, value) { | |
if (value in $data) { | |
argv[i] = $data[value]; | |
} | |
}); | |
$target | |
// Trigger exclusive ACTION event | |
.trigger($.Event($event, { | |
type: ACTION + "!", | |
action: name | |
}), argv); | |
// Since we've translated the event, stop propagation | |
$event.stopPropagation(); | |
} | |
$.event.special[ACTION] = { | |
/** | |
* @param data (Anything) Whatever eventData (optional) was passed in | |
* when binding the event. | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
* @param eventHandle (Function) The actual function that will be bound | |
* to the browser’s native event (this is used internally for the | |
* beforeunload event, you’ll never use it). | |
*/ | |
setup : function onActionSetup(data, namespaces, eventHandle) { | |
$(this).bind(ACTION, data, onAction); | |
}, | |
/** | |
* Do something each time an event handler is bound to a particular element | |
* @param handleObj (Object) | |
*/ | |
add : function onActionAdd(handleObj) { | |
var events = $.map(handleObj.namespace.split(RE_DOT), namespaceIterator); | |
if (events.length !== 0) { | |
$(this).bind(events.join(" "), handler); | |
} | |
}, | |
/** | |
* Do something each time an event handler is unbound from a particular element | |
* @param handleObj (Object) | |
*/ | |
remove : function onActionRemove(handleObj) { | |
var events = $.map(handleObj.namespace.split(RE_DOT), namespaceIterator); | |
if (events.length !== 0) { | |
$(this).unbind(events.join(" "), handler); | |
} | |
}, | |
/** | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
*/ | |
teardown : function onActionTeardown(namespaces) { | |
$(this).unbind(ACTION, onAction); | |
} | |
}; | |
$.fn[ACTION] = function action(name) { | |
return $(this).trigger({ | |
type: ACTION + "!", | |
action: name | |
}, SLICE.call(arguments, 1)); | |
}; | |
}); | |
/*! | |
* TroopJS jQuery weave plug-in | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*jshint strict:false, smarttabs:true, laxbreak:true, loopfunc:true */ | |
/*global define:true */ | |
define('troopjs-jquery/weave',[ "jquery", "troopjs-utils/getargs", "require" ], function WeaveModule($, getargs, parentRequire) { | |
var UNDEFINED; | |
var NULL = null; | |
var ARRAY = Array; | |
var FUNCTION = Function; | |
var ARRAY_PROTO = ARRAY.prototype; | |
var JOIN = ARRAY_PROTO.join; | |
var PUSH = ARRAY_PROTO.push; | |
var POP = ARRAY_PROTO.pop; | |
var $WHEN = $.when; | |
var THEN = "then"; | |
var WEAVE = "weave"; | |
var UNWEAVE = "unweave"; | |
var WOVEN = "woven"; | |
var WEAVING = "weaving"; | |
var PENDING = "pending"; | |
var DESTROY = "destroy"; | |
var DATA = "data-"; | |
var DATA_WEAVE = DATA + WEAVE; | |
var DATA_WOVEN = DATA + WOVEN; | |
var DATA_WEAVING = DATA + WEAVING; | |
var SELECTOR_WEAVE = "[" + DATA_WEAVE + "]"; | |
var SELECTOR_UNWEAVE = "[" + DATA_WEAVING + "],[" + DATA_WOVEN + "]"; | |
/** | |
* Generic destroy handler. | |
* Simply makes sure that unweave has been called | |
*/ | |
function onDestroy() { | |
$(this).unweave(); | |
} | |
$.expr[":"][WEAVE] = $.expr.createPseudo | |
? $.expr.createPseudo(function (widgets) { | |
if (widgets !== UNDEFINED) { | |
widgets = RegExp($.map(getargs.call(widgets), function (widget) { | |
return "^" + widget + "$"; | |
}).join("|"), "m"); | |
} | |
return function (element, context, isXml) { | |
var weave = $(element).attr(DATA_WEAVE); | |
return weave === UNDEFINED | |
? false | |
: widgets === UNDEFINED | |
? true | |
: widgets.test(weave.split(/[\s,]+/).join("\n")); | |
}; | |
}) | |
: function (element, index, match) { | |
var weave = $(element).attr(DATA_WEAVE); | |
return weave === UNDEFINED | |
? false | |
: match === UNDEFINED | |
? true | |
: RegExp($.map(getargs.call(match[3]), function (widget) { | |
return "^" + widget + "$"; | |
}).join("|"), "m").test(weave.split(/[\s,]+/).join("\n")); | |
}; | |
$.expr[":"][WOVEN] = $.expr.createPseudo | |
? $.expr.createPseudo(function (widgets) { | |
if (widgets !== UNDEFINED) { | |
widgets = RegExp($.map(getargs.call(widgets), function (widget) { | |
return "^" + widget + "@\\d+"; | |
}).join("|"), "m"); | |
} | |
return function (element, context, isXml) { | |
var woven = $(element).attr(DATA_WOVEN); | |
return woven === UNDEFINED | |
? false | |
: widgets === UNDEFINED | |
? true | |
: widgets.test(woven.split(/[\s,]+/).join("\n")); | |
}; | |
}) | |
: function (element, index, match) { | |
var woven = $(element).attr(DATA_WOVEN); | |
return woven === UNDEFINED | |
? false | |
: match === UNDEFINED | |
? true | |
: RegExp($.map(getargs.call(match[3]), function (widget) { | |
return "^" + widget + "@\\d+"; | |
}).join("|"), "m").test(woven.split(/[\s,]+/).join("\n")); | |
}; | |
$.fn[WEAVE] = function weave(/* arg, arg, arg, deferred*/) { | |
var widgets = []; | |
var i = 0; | |
var $elements = $(this); | |
var arg = arguments; | |
var argc = arg.length; | |
// If deferred not a true Deferred, make it so | |
var deferred = argc > 0 && arg[argc - 1][THEN] instanceof FUNCTION | |
? POP.call(arg) | |
: $.Deferred(); | |
$elements | |
// Reduce to only elements that can be woven | |
.filter(SELECTOR_WEAVE) | |
// Iterate | |
.each(function elementIterator(index, element) { | |
// Defer weave | |
$.Deferred(function deferredWeave(dfdWeave) { | |
var $element = $(element); | |
var $data = $element.data(); | |
var weave = $data[WEAVE] = $element.attr(DATA_WEAVE) || ""; | |
var woven = $data[WOVEN] || ($data[WOVEN] = []); | |
var pending = $data[PENDING] || ($data[PENDING] = []); | |
// Link deferred | |
dfdWeave.done(function doneWeave() { | |
$element | |
// Remove DATA_WEAVING | |
.removeAttr(DATA_WEAVING) | |
// Set DATA_WOVEN with full names | |
.attr(DATA_WOVEN, JOIN.call(arguments, " ")); | |
}); | |
// Wait for all pending deferred | |
$WHEN.apply($, pending).then(function donePending() { | |
var re = /[\s,]*([\w_\-\/\.]+)(?:\(([^\)]+)\))?/g; | |
var mark = i; | |
var j = 0; | |
var matches; | |
// Push dfdWeave on pending to signify we're starting a new task | |
PUSH.call(pending, dfdWeave); | |
$element | |
// Make sure to remove DATA_WEAVE (so we don't try processing this again) | |
.removeAttr(DATA_WEAVE) | |
// Set DATA_WEAVING (so that unweave can pick this up) | |
.attr(DATA_WEAVING, weave) | |
// Bind destroy event | |
.bind(DESTROY, onDestroy); | |
// Iterate woven (while RE_WEAVE matches) | |
while ((matches = re.exec(weave)) !== NULL) { | |
// Defer widget | |
$.Deferred(function deferredWidget(dfdWidget) { | |
var _j = j++; // store _j before we increment | |
var k; | |
var l; | |
var kMax; | |
var value; | |
// Add to widgets | |
widgets[i++] = dfdWidget; | |
// Link deferred | |
dfdWidget.then(function doneWidget(widget) { | |
woven[_j] = widget; | |
}, dfdWeave.reject, dfdWeave.notify); | |
// Get widget name | |
var name = matches[1]; | |
// Set initial argv | |
var argv = [ $element, name ]; | |
// Append values from arg to argv | |
for (k = 0, kMax = arg.length, l = argv.length; k < kMax; k++, l++) { | |
argv[l] = arg[k]; | |
} | |
// Get widget args | |
var args = matches[2]; | |
// Any widget arguments | |
if (args !== UNDEFINED) { | |
// Convert args using getargs | |
args = getargs.call(args); | |
// Append typed values from args to argv | |
for (k = 0, kMax = args.length, l = argv.length; k < kMax; k++, l++) { | |
// Get value | |
value = args[k]; | |
// Get value from $data or fall back to pure value | |
argv[l] = value in $data | |
? $data[value] | |
: value; | |
} | |
} | |
// Require module | |
parentRequire([ name ], function required(Widget) { | |
// Defer start | |
$.Deferred(function deferredStart(dfdStart) { | |
// Constructed and initialized instance | |
var widget = Widget.apply(Widget, argv); | |
// Link deferred | |
dfdStart.then(function doneStart() { | |
dfdWidget.resolve(widget); | |
}, dfdWidget.reject, dfdWidget.notify); | |
// Start | |
widget.start(dfdStart); | |
}); | |
}); | |
}); | |
} | |
// Slice out widgets woven for this element | |
$WHEN.apply($, widgets.slice(mark, i)).then(dfdWeave.resolve, dfdWeave.reject, dfdWeave.notify); | |
}, dfdWeave.reject, dfdWeave.notify); | |
}); | |
}); | |
// When all widgets are resolved, resolve original deferred | |
$WHEN.apply($, widgets).then(deferred.resolve, deferred.reject, deferred.notify); | |
return $elements; | |
}; | |
$.fn[UNWEAVE] = function unweave(deferred) { | |
var widgets = []; | |
var i = 0; | |
var $elements = $(this); | |
// Create default deferred if none was passed | |
deferred = deferred || $.Deferred(); | |
$elements | |
// Reduce to only elements that can be unwoven | |
.filter(SELECTOR_UNWEAVE) | |
// Iterate | |
.each(function elementIterator(index, element) { | |
// Defer unweave | |
$.Deferred(function deferredUnweave(dfdUnweave) { | |
var $element = $(element); | |
var $data = $element.data(); | |
var pending = $data[PENDING] || ($data[PENDING] = []); | |
var woven = $data[WOVEN] || []; | |
// Link deferred | |
dfdUnweave.done(function doneUnweave() { | |
$element | |
// Copy weave data to data-weave attribute | |
.attr(DATA_WEAVE, $data[WEAVE]) | |
// Make sure to clean the destroy event handler | |
.unbind(DESTROY, onDestroy); | |
// Remove data fore WEAVE | |
delete $data[WEAVE]; | |
}); | |
// Wait for all pending deferred | |
$WHEN.apply($, pending).done(function donePending() { | |
var mark = i; | |
var widget; | |
// Push dfdUnweave on pending to signify we're starting a new task | |
PUSH.call(pending, dfdUnweave); | |
// Remove WOVEN data | |
delete $data[WOVEN]; | |
$element | |
// Remove DATA_WOVEN attribute | |
.removeAttr(DATA_WOVEN); | |
// Somewhat safe(r) iterator over woven | |
while ((widget = woven.shift()) !== UNDEFINED) { | |
// Defer widget | |
$.Deferred(function deferredWidget(dfdWidget) { | |
// Add to unwoven and pending | |
widgets[i++] = dfdWidget; | |
// $.Deferred stop | |
$.Deferred(function deferredStop(dfdStop) { | |
// Link deferred | |
dfdStop.then(function doneStop() { | |
dfdWidget.resolve(widget); | |
}, dfdWidget.reject, dfdWidget.notify); | |
// Stop | |
widget.stop(dfdStop); | |
}); | |
}); | |
} | |
// Slice out widgets unwoven for this element | |
$WHEN.apply($, widgets.slice(mark, i)).then(dfdUnweave.resolve, dfdUnweave.reject, dfdUnweave.notify); | |
}); | |
}); | |
}); | |
// When all deferred are resolved, resolve original deferred | |
$WHEN.apply($, widgets).then(deferred.resolve, deferred.reject, deferred.notify); | |
return $elements; | |
}; | |
$.fn[WOVEN] = function woven(/* arg, arg */) { | |
var result = []; | |
var widgets = arguments.length > 0 | |
? RegExp($.map(arguments, function (widget) { | |
return "^" + widget + "$"; | |
}).join("|"), "m") | |
: UNDEFINED; | |
$(this).each(function elementIterator(index, element) { | |
if (!$.hasData(element)) { | |
return; | |
} | |
PUSH.apply(result, widgets === UNDEFINED | |
? $.data(element, WOVEN) | |
: $.map($.data(element, WOVEN), function (woven) { | |
return widgets.test(woven.displayName) | |
? woven | |
: UNDEFINED; | |
})); | |
}); | |
return result; | |
}; | |
}); | |
/*! | |
* TroopJS jQuery dimensions plug-in | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*jshint strict:false, smarttabs:true */ | |
/*global define:true */ | |
define('troopjs-jquery/dimensions',[ "jquery" ], function DimensionsModule($) { | |
var NULL = null; | |
var DIMENSIONS = "dimensions"; | |
var RESIZE = "resize." + DIMENSIONS; | |
var W = "w"; | |
var H = "h"; | |
var _W = "_" + W; | |
var _H = "_" + H; | |
/** | |
* Internal comparator used for reverse sorting arrays | |
*/ | |
function reverse(a, b) { | |
return b - a; | |
} | |
/** | |
* Internal onResize handler | |
* @param $event | |
*/ | |
function onResize($event) { | |
var $self = $(this); | |
var width = $self.width(); | |
var height = $self.height(); | |
// Iterate all dimensions | |
$.each($.data(self, DIMENSIONS), function dimensionIterator(namespace, dimension) { | |
var w = dimension[W]; | |
var h = dimension[H]; | |
var _w; | |
var _h; | |
var i; | |
i = w.length; | |
_w = w[i - 1]; | |
while(w[--i] < width) { | |
_w = w[i]; | |
} | |
i = h.length; | |
_h = h[i - 1]; | |
while(h[--i] < height) { | |
_h = h[i]; | |
} | |
// If _w or _h has changed, update and trigger | |
if (_w !== dimension[_W] || _h !== dimension[_H]) { | |
dimension[_W] = _w; | |
dimension[_H] = _h; | |
$self.trigger(DIMENSIONS + "." + namespace, [ _w, _h ]); | |
} | |
}); | |
} | |
$.event.special[DIMENSIONS] = { | |
/** | |
* @param data (Anything) Whatever eventData (optional) was passed in | |
* when binding the event. | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
* @param eventHandle (Function) The actual function that will be bound | |
* to the browser’s native event (this is used internally for the | |
* beforeunload event, you’ll never use it). | |
*/ | |
setup : function onDimensionsSetup(data, namespaces, eventHandle) { | |
$(this) | |
.bind(RESIZE, onResize) | |
.data(DIMENSIONS, {}); | |
}, | |
/** | |
* Do something each time an event handler is bound to a particular element | |
* @param handleObj (Object) | |
*/ | |
add : function onDimensionsAdd(handleObj) { | |
var self = this; | |
var namespace = handleObj.namespace; | |
var dimension = {}; | |
var w = dimension[W] = []; | |
var h = dimension[H] = []; | |
var re = /(w|h)(\d+)/g; | |
var matches; | |
while ((matches = re.exec(namespace)) !== NULL) { | |
dimension[matches[1]].push(parseInt(matches[2], 10)); | |
} | |
w.sort(reverse); | |
h.sort(reverse); | |
$.data(self, DIMENSIONS)[namespace] = dimension; | |
}, | |
/** | |
* Do something each time an event handler is unbound from a particular element | |
* @param handleObj (Object) | |
*/ | |
remove : function onDimensionsRemove(handleObj) { | |
delete $.data(this, DIMENSIONS)[handleObj.namespace]; | |
}, | |
/** | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
*/ | |
teardown : function onDimensionsTeardown(namespaces) { | |
$(this) | |
.removeData(DIMENSIONS) | |
.unbind(RESIZE, onResize); | |
} | |
}; | |
}); | |
/*! | |
* TroopJS jQuery destroy plug-in | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*jshint strict:false, smarttabs:true */ | |
/*global define:true */ | |
define('troopjs-jquery/destroy',[ "jquery" ], function DestroyModule($) { | |
$.event.special.destroy = { | |
remove : function onDestroyRemove(handleObj) { | |
var self = this; | |
handleObj.handler.call(self, $.Event({ | |
"type" : handleObj.type, | |
"data" : handleObj.data, | |
"namespace" : handleObj.namespace, | |
"target" : self | |
})); | |
} | |
}; | |
}); | |
/*! | |
* TroopJS jQuery resize plug-in | |
* | |
* Heavy inspiration from https://github.com/cowboy/jquery-resize.git | |
* | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*jshint strict:false, smarttabs:true */ | |
/*global define:true */ | |
define('troopjs-jquery/resize',[ "jquery" ], function ResizeModule($) { | |
var NULL = null; | |
var RESIZE = "resize"; | |
var W = "w"; | |
var H = "h"; | |
var $ELEMENTS = $([]); | |
var INTERVAL = NULL; | |
/** | |
* Iterator | |
* @param index | |
* @param self | |
*/ | |
function iterator(index, self) { | |
// Get data | |
var $data = $.data(self); | |
// Get reference to $self | |
var $self = $(self); | |
// Get previous width and height | |
var w = $self.width(); | |
var h = $self.height(); | |
// Check if width or height has changed since last check | |
if (w !== $data[W] || h !== $data[H]) { | |
$self.trigger(RESIZE, [$data[W] = w, $data[H] = h]); | |
} | |
} | |
/** | |
* Internal interval | |
*/ | |
function interval() { | |
$ELEMENTS.each(iterator); | |
} | |
$.event.special[RESIZE] = { | |
/** | |
* @param data (Anything) Whatever eventData (optional) was passed in | |
* when binding the event. | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
* @param eventHandle (Function) The actual function that will be bound | |
* to the browser’s native event (this is used internally for the | |
* beforeunload event, you’ll never use it). | |
*/ | |
setup : function hashChangeSetup(data, namespaces, eventHandle) { | |
var self = this; | |
// window has a native resize event, exit fast | |
if ($.isWindow(self)) { | |
return false; | |
} | |
// Store data | |
var $data = $.data(self, RESIZE, {}); | |
// Get reference to $self | |
var $self = $(self); | |
// Initialize data | |
$data[W] = $self.width(); | |
$data[H] = $self.height(); | |
// Add to tracked collection | |
$ELEMENTS = $ELEMENTS.add(self); | |
// If this is the first element, start interval | |
if($ELEMENTS.length === 1) { | |
INTERVAL = setInterval(interval, 100); | |
} | |
}, | |
/** | |
* @param namespaces (Array) An array of namespaces specified when | |
* binding the event. | |
*/ | |
teardown : function onDimensionsTeardown(namespaces) { | |
var self = this; | |
// window has a native resize event, exit fast | |
if ($.isWindow(self)) { | |
return false; | |
} | |
// Remove data | |
$.removeData(self, RESIZE); | |
// Remove from tracked collection | |
$ELEMENTS = $ELEMENTS.not(self); | |
// If this is the last element, stop interval | |
if($ELEMENTS.length === 0 && INTERVAL !== NULL) { | |
clearInterval(INTERVAL); | |
} | |
} | |
}; | |
}); | |
/*! | |
* TroopJS Utils merge module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
define('troopjs-utils/merge',[],function MergeModule() { | |
var ARRAY = Array; | |
var OBJECT = Object; | |
return function merge(source) { | |
var target = this; | |
var key = null; | |
var i; | |
var iMax; | |
var value; | |
var constructor; | |
for (i = 0, iMax = arguments.length; i < iMax; i++) { | |
source = arguments[i]; | |
for (key in source) { | |
value = source[key]; | |
constructor = value.constructor; | |
if (!(key in target)) { | |
target[key] = value; | |
} | |
else if (constructor === ARRAY) { | |
target[key] = target[key].concat(value); | |
} | |
else if (constructor === OBJECT) { | |
merge.call(target[key], value); | |
} | |
else { | |
target[key] = value; | |
} | |
} | |
} | |
return target; | |
}; | |
}); | |
/*! | |
* TroopJS Utils grep component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
define('troopjs-utils/grep',[ "jquery" ], function GrepModule($) { | |
return $.grep; | |
}); | |
/*! | |
* TroopJS Utils tr component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
define('troopjs-utils/tr',[],function TrModule() { | |
var TYPEOF_NUMBER = typeof Number(); | |
return function tr(callback) { | |
var self = this; | |
var result = []; | |
var i; | |
var length = self.length; | |
var key; | |
// Is this an array? Basically, is length a number, is it 0 or is it greater than 0 and that we have index 0 and index length-1 | |
if (typeof length === TYPEOF_NUMBER && length === 0 || length > 0 && 0 in self && length - 1 in self) { | |
for (i = 0; i < length; i++) { | |
result.push(callback.call(self, self[i], i)); | |
} | |
// Otherwise we'll iterate it as an object | |
} else if (self){ | |
for (key in self) { | |
result.push(callback.call(self, self[key], key)); | |
} | |
} | |
return result; | |
}; | |
}); | |
/* | |
* ComposeJS, object composition for JavaScript, featuring | |
* JavaScript-style prototype inheritance and composition, multiple inheritance, | |
* mixin and traits-inspired conflict resolution and composition | |
*/ | |
(function(define){ | |
define('compose',[], function(){ | |
// function for creating instances from a prototype | |
function Create(){ | |
} | |
var delegate = Object.create ? | |
function(proto){ | |
return Object.create(typeof proto == "function" ? proto.prototype : proto || Object.prototype); | |
} : | |
function(proto){ | |
Create.prototype = typeof proto == "function" ? proto.prototype : proto; | |
var instance = new Create(); | |
Create.prototype = null; | |
return instance; | |
}; | |
function validArg(arg){ | |
if(!arg){ | |
throw new Error("Compose arguments must be functions or objects"); | |
} | |
return arg; | |
} | |
// this does the work of combining mixins/prototypes | |
function mixin(instance, args, i){ | |
// use prototype inheritance for first arg | |
var value, argsLength = args.length; | |
for(; i < argsLength; i++){ | |
var arg = args[i]; | |
if(typeof arg == "function"){ | |
// the arg is a function, use the prototype for the properties | |
var prototype = arg.prototype; | |
for(var key in prototype){ | |
value = prototype[key]; | |
var own = prototype.hasOwnProperty(key); | |
if(typeof value == "function" && key in instance && value !== instance[key]){ | |
var existing = instance[key]; | |
if(value == required){ | |
// it is a required value, and we have satisfied it | |
value = existing; | |
} | |
else if(!own){ | |
// if it is own property, it is considered an explicit override | |
// TODO: make faster calls on this, perhaps passing indices and caching | |
if(isInMethodChain(value, key, getBases([].slice.call(args, 0, i), true))){ | |
// this value is in the existing method's override chain, we can use the existing method | |
value = existing; | |
}else if(!isInMethodChain(existing, key, getBases([arg], true))){ | |
// the existing method is not in the current override chain, so we are left with a conflict | |
console.error("Conflicted method " + key + ", final composer must explicitly override with correct method."); | |
} | |
} | |
} | |
if(value && value.install && own && !isInMethodChain(existing, key, getBases([arg], true))){ | |
// apply modifier | |
value.install.call(instance, key); | |
}else{ | |
instance[key] = value; | |
} | |
} | |
}else{ | |
// it is an object, copy properties, looking for modifiers | |
for(var key in validArg(arg)){ | |
var value = arg[key]; | |
if(typeof value == "function"){ | |
if(value.install){ | |
// apply modifier | |
value.install.call(instance, key); | |
continue; | |
} | |
if(key in instance){ | |
if(value == required){ | |
// required requirement met | |
continue; | |
} | |
} | |
} | |
// add it to the instance | |
instance[key] = value; | |
} | |
} | |
} | |
return instance; | |
} | |
// allow for override (by es5 module) | |
Compose._setMixin = function(newMixin){ | |
mixin = newMixin; | |
}; | |
function isInMethodChain(method, name, prototypes){ | |
// searches for a method in the given prototype hierarchy | |
for(var i = 0; i < prototypes.length;i++){ | |
var prototype = prototypes[i]; | |
if(prototype[name] == method){ | |
// found it | |
return true; | |
} | |
} | |
} | |
// Decorator branding | |
function Decorator(install, direct){ | |
function Decorator(){ | |
if(direct){ | |
return direct.apply(this, arguments); | |
} | |
throw new Error("Decorator not applied"); | |
} | |
Decorator.install = install; | |
return Decorator; | |
} | |
Compose.Decorator = Decorator; | |
// aspect applier | |
function aspect(handler){ | |
return function(advice){ | |
return Decorator(function install(key){ | |
var baseMethod = this[key]; | |
(advice = this[key] = baseMethod ? handler(this, baseMethod, advice) : advice).install = install; | |
}, advice); | |
}; | |
}; | |
// around advice, useful for calling super methods too | |
Compose.around = aspect(function(target, base, advice){ | |
return advice.call(target, base); | |
}); | |
Compose.before = aspect(function(target, base, advice){ | |
return function(){ | |
var results = advice.apply(this, arguments); | |
if(results !== stop){ | |
return base.apply(this, results || arguments); | |
} | |
}; | |
}); | |
var stop = Compose.stop = {}; | |
var undefined; | |
Compose.after = aspect(function(target, base, advice){ | |
return function(){ | |
var results = base.apply(this, arguments); | |
var adviceResults = advice.apply(this, arguments); | |
return adviceResults === undefined ? results : adviceResults; | |
}; | |
}); | |
// rename Decorator for calling super methods | |
Compose.from = function(trait, fromKey){ | |
if(fromKey){ | |
return (typeof trait == "function" ? trait.prototype : trait)[fromKey]; | |
} | |
return Decorator(function(key){ | |
if(!(this[key] = (typeof trait == "string" ? this[trait] : | |
(typeof trait == "function" ? trait.prototype : trait)[fromKey || key]))){ | |
throw new Error("Source method " + fromKey + " was not available to be renamed to " + key); | |
} | |
}); | |
}; | |
// Composes an instance | |
Compose.create = function(base){ | |
// create the instance | |
var instance = mixin(delegate(base), arguments, 1); | |
var argsLength = arguments.length; | |
// for go through the arguments and call the constructors (with no args) | |
for(var i = 0; i < argsLength; i++){ | |
var arg = arguments[i]; | |
if(typeof arg == "function"){ | |
instance = arg.call(instance) || instance; | |
} | |
} | |
return instance; | |
} | |
// The required function, just throws an error if not overriden | |
function required(){ | |
throw new Error("This method is required and no implementation has been provided"); | |
}; | |
Compose.required = required; | |
// get the value of |this| for direct function calls for this mode (strict in ES5) | |
function extend(){ | |
var args = [this]; | |
args.push.apply(args, arguments); | |
return Compose.apply(0, args); | |
} | |
// Compose a constructor | |
function Compose(base){ | |
var args = arguments; | |
var prototype = (args.length < 2 && typeof args[0] != "function") ? | |
args[0] : // if there is just a single argument object, just use that as the prototype | |
mixin(delegate(validArg(base)), args, 1); // normally create a delegate to start with | |
function Constructor(){ | |
var instance; | |
if(this instanceof Constructor){ | |
// called with new operator, can proceed as is | |
instance = this; | |
}else{ | |
// we allow for direct calls without a new operator, in this case we need to | |
// create the instance ourself. | |
Create.prototype = prototype; | |
instance = new Create(); | |
} | |
// call all the constructors with the given arguments | |
for(var i = 0; i < constructorsLength; i++){ | |
var constructor = constructors[i]; | |
var result = constructor.apply(instance, arguments); | |
if(typeof result == "object"){ | |
if(result instanceof Constructor){ | |
instance = result; | |
}else{ | |
for(var j in result){ | |
if(result.hasOwnProperty(j)){ | |
instance[j] = result[j]; | |
} | |
} | |
} | |
} | |
} | |
return instance; | |
} | |
// create a function that can retrieve the bases (constructors or prototypes) | |
Constructor._getBases = function(prototype){ | |
return prototype ? prototypes : constructors; | |
}; | |
// now get the prototypes and the constructors | |
var constructors = getBases(args), | |
constructorsLength = constructors.length; | |
if(typeof args[args.length - 1] == "object"){ | |
args[args.length - 1] = prototype; | |
} | |
var prototypes = getBases(args, true); | |
Constructor.extend = extend; | |
if(!Compose.secure){ | |
prototype.constructor = Constructor; | |
} | |
Constructor.prototype = prototype; | |
return Constructor; | |
}; | |
Compose.apply = function(thisObject, args){ | |
// apply to the target | |
return thisObject ? | |
mixin(thisObject, args, 0) : // called with a target object, apply the supplied arguments as mixins to the target object | |
extend.apply.call(Compose, 0, args); // get the Function.prototype apply function, call() it to apply arguments to Compose (the extend doesn't matter, just a handle way to grab apply, since we can't get it off of Compose) | |
}; | |
Compose.call = function(thisObject){ | |
// call() should correspond with apply behavior | |
return mixin(thisObject, arguments, 1); | |
}; | |
function getBases(args, prototype){ | |
// this function registers a set of constructors for a class, eliminating duplicate | |
// constructors that may result from diamond construction for classes (B->A, C->A, D->B&C, then D() should only call A() once) | |
var bases = []; | |
function iterate(args, checkChildren){ | |
outer: | |
for(var i = 0; i < args.length; i++){ | |
var arg = args[i]; | |
var target = prototype && typeof arg == "function" ? | |
arg.prototype : arg; | |
if(prototype || typeof arg == "function"){ | |
var argGetBases = checkChildren && arg._getBases; | |
if(argGetBases){ | |
iterate(argGetBases(prototype)); // don't need to check children for these, this should be pre-flattened | |
}else{ | |
for(var j = 0; j < bases.length; j++){ | |
if(target == bases[j]){ | |
continue outer; | |
} | |
} | |
bases.push(target); | |
} | |
} | |
} | |
} | |
iterate(args, true); | |
return bases; | |
} | |
// returning the export of the module | |
return Compose; | |
}); | |
})(typeof define != "undefined" ? | |
define: // AMD/RequireJS format if available | |
function(deps, factory){ | |
if(typeof module !="undefined"){ | |
module.exports = factory(); // CommonJS environment, like NodeJS | |
// require("./configure"); | |
}else{ | |
Compose = factory(); // raw script, assign to Compose global | |
} | |
}); | |
/*! | |
* TroopJS Utils URI module | |
* | |
* parts of code from parseUri 1.2.2 Copyright Steven Levithan <stevenlevithan.com> | |
* | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*jshint strict:false, smarttabs:true, laxbreak:true, newcap:false, forin:false, loopfunc:true */ | |
/*global define:true */ | |
define('troopjs-utils/uri',[ "compose" ], function URIModule(Compose) { | |
var NULL = null; | |
var ARRAY_PROTO = Array.prototype; | |
var OBJECT_PROTO = Object.prototype; | |
var PUSH = ARRAY_PROTO.push; | |
var SPLIT = String.prototype.split; | |
var TOSTRING = OBJECT_PROTO.toString; | |
var TOSTRING_OBJECT = TOSTRING.call(OBJECT_PROTO); | |
var TOSTRING_ARRAY = TOSTRING.call(ARRAY_PROTO); | |
var TOSTRING_STRING = TOSTRING.call(String.prototype); | |
var TOSTRING_FUNCTION = TOSTRING.call(Function.prototype); | |
var RE_URI = /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?(?:([^?#]*)(?:\?([^#]*))?(?:#(.*))?)/; | |
var PROTOCOL = "protocol"; | |
var AUTHORITY = "authority"; | |
var PATH = "path"; | |
var QUERY = "query"; | |
var ANCHOR = "anchor"; | |
var KEYS = [ "source", | |
PROTOCOL, | |
AUTHORITY, | |
"userInfo", | |
"user", | |
"password", | |
"host", | |
"port", | |
PATH, | |
QUERY, | |
ANCHOR ]; | |
// Store current Compose.secure setting | |
var SECURE = Compose.secure; | |
// Prevent Compose from creating constructor property | |
Compose.secure = true; | |
function Query(arg) { | |
var result = {}; | |
var matches; | |
var key = NULL; | |
var value; | |
var re = /(?:&|^)([^&=]*)=?([^&]*)/g; | |
result.toString = Query.toString; | |
if (TOSTRING.call(arg) === TOSTRING_OBJECT) { | |
for (key in arg) { | |
result[key] = arg[key]; | |
} | |
} else { | |
while ((matches = re.exec(arg)) !== NULL) { | |
key = matches[1]; | |
if (key in result) { | |
value = result[key]; | |
if (TOSTRING.call(value) === TOSTRING_ARRAY) { | |
value[value.length] = matches[2]; | |
} | |
else { | |
result[key] = [ value, matches[2] ]; | |
} | |
} | |
else { | |
result[key] = matches[2]; | |
} | |
} | |
} | |
return result; | |
} | |
Query.toString = function toString() { | |
var self = this; | |
var key = NULL; | |
var value = NULL; | |
var values; | |
var query = []; | |
var i = 0; | |
var j; | |
for (key in self) { | |
if (TOSTRING.call(self[key]) === TOSTRING_FUNCTION) { | |
continue; | |
} | |
query[i++] = key; | |
} | |
query.sort(); | |
while (i--) { | |
key = query[i]; | |
value = self[key]; | |
if (TOSTRING.call(value) === TOSTRING_ARRAY) { | |
values = value.slice(0); | |
values.sort(); | |
j = values.length; | |
while (j--) { | |
value = values[j]; | |
values[j] = value === "" | |
? key | |
: key + "=" + value; | |
} | |
query[i] = values.join("&"); | |
} | |
else { | |
query[i] = value === "" | |
? key | |
: key + "=" + value; | |
} | |
} | |
return query.join("&"); | |
}; | |
// Extend on the instance of array rather than subclass it | |
function Path(arg) { | |
var result = []; | |
result.toString = Path.toString; | |
PUSH.apply(result, TOSTRING.call(arg) === TOSTRING_ARRAY | |
? arg | |
: SPLIT.call(arg, "/")); | |
return result; | |
} | |
Path.toString = function() { | |
return this.join("/"); | |
}; | |
var URI = Compose(function URI(str) { | |
var self = this; | |
var value; | |
var matches; | |
var i; | |
if ((matches = RE_URI.exec(str)) !== NULL) { | |
i = matches.length; | |
while (i--) { | |
value = matches[i]; | |
if (value) { | |
self[KEYS[i]] = value; | |
} | |
} | |
} | |
if (QUERY in self) { | |
self[QUERY] = Query(self[QUERY]); | |
} | |
if (PATH in self) { | |
self[PATH] = Path(self[PATH]); | |
} | |
}); | |
URI.prototype.toString = function () { | |
var self = this; | |
var uri = [ PROTOCOL , "://", AUTHORITY, PATH, "?", QUERY, "#", ANCHOR ]; | |
var i; | |
var key; | |
if (!(PROTOCOL in self)) { | |
uri[0] = uri[1] = ""; | |
} | |
if (!(AUTHORITY in self)) { | |
uri[2] = ""; | |
} | |
if (!(PATH in self)) { | |
uri[3] = ""; | |
} | |
if (!(QUERY in self)) { | |
uri[4] = uri[5] = ""; | |
} | |
if (!(ANCHOR in self)) { | |
uri[6] = uri[7] = ""; | |
} | |
i = uri.length; | |
while (i--) { | |
key = uri[i]; | |
if (key in self) { | |
uri[i] = self[key]; | |
} | |
} | |
return uri.join(""); | |
}; | |
// Restore Compose.secure setting | |
Compose.secure = SECURE; | |
URI.Path = Path; | |
URI.Query = Query; | |
return URI; | |
}); | |
/*! | |
* TroopJS Utils each component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
define('troopjs-utils/each',[ "jquery" ], function EachModule($) { | |
return $.each; | |
}); | |
/*! | |
* TroopJS Utils callbacks component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
define('troopjs-utils/callbacks',[ "jquery" ], function CallbacksModule($) { | |
return $.Callbacks; | |
}); | |
/*! | |
* TroopJS Utils unique component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
define('troopjs-utils/unique',[],function UniqueModule() { | |
return function unique(callback) { | |
var self = this; | |
var length = self.length; | |
var result = []; | |
var value; | |
var i; | |
var j; | |
var k; | |
add: for (i = j = k = 0; i < length; i++, j = 0) { | |
value = self[i]; | |
while(j < k) { | |
if (callback.call(self, value, result[j++]) === true) { | |
continue add; | |
} | |
} | |
result[k++] = value; | |
} | |
return result; | |
}; | |
}); | |
/*! | |
* TroopJS Utils when component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
define('troopjs-utils/when',[ "jquery" ], function WhenModule($) { | |
return $.when; | |
}); | |
/*! | |
* TroopJS Utils deferred component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
define('troopjs-utils/deferred',[ "jquery" ], function DeferredModule($) { | |
return $.Deferred; | |
}); | |
/*! | |
* TroopJS event/emitter module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*jshint strict:false, smarttabs:true, laxbreak:true */ | |
/*global define:true */ | |
define('troopjs-core/event/emitter',[ "compose" ], function EventEmitterModule(Compose) { | |
var UNDEFINED; | |
var TRUE = true; | |
var FALSE = false; | |
var FUNCTION = Function; | |
var MEMORY = "memory"; | |
var CONTEXT = "context"; | |
var CALLBACK = "callback"; | |
var LENGTH = "length"; | |
var HEAD = "head"; | |
var TAIL = "tail"; | |
var NEXT = "next"; | |
var HANDLED = "handled"; | |
var HANDLERS = "handlers"; | |
var ROOT = {}; | |
var COUNT = 0; | |
return Compose(function EventEmitter() { | |
this[HANDLERS] = {}; | |
}, { | |
/** | |
* Subscribe to a event | |
* | |
* @param event Event to subscribe to | |
* @param context (optional) context to scope callbacks to | |
* @param memory (optional) do we want the last value applied to callbacks | |
* @param callback Callback for this event | |
* @returns self | |
*/ | |
on : function on(event /*, context, memory, callback, callback, ..*/) { | |
var self = this; | |
var arg = arguments; | |
var length = arg[LENGTH]; | |
var context = arg[1]; | |
var memory = arg[2]; | |
var callback = arg[3]; | |
var handlers = self[HANDLERS]; | |
var handler; | |
var handled; | |
var head; | |
var tail; | |
var offset; | |
// No context or memory was supplied | |
if (context instanceof FUNCTION) { | |
memory = FALSE; | |
context = ROOT; | |
offset = 1; | |
} | |
// Only memory was supplied | |
else if (context === TRUE || context === FALSE) { | |
memory = context; | |
context = ROOT; | |
offset = 2; | |
} | |
// Context was supplied, but not memory | |
else if (memory instanceof FUNCTION) { | |
memory = FALSE; | |
offset = 2; | |
} | |
// All arguments were supplied | |
else if (callback instanceof FUNCTION){ | |
offset = 3; | |
} | |
// Something is wrong, return fast | |
else { | |
return self; | |
} | |
// Have handlers | |
if (event in handlers) { | |
// Get handlers | |
handlers = handlers[event]; | |
// Create new handler | |
handler = { | |
"callback" : arg[offset++], | |
"context" : context | |
}; | |
// Get tail handler | |
tail = TAIL in handlers | |
// Have tail, update handlers.tail.next to point to handler | |
? handlers[TAIL][NEXT] = handler | |
// Have no tail, update handlers.head to point to handler | |
: handlers[HEAD] = handler; | |
// Iterate handlers from offset | |
while (offset < length) { | |
// Set tail -> tail.next -> handler | |
tail = tail[NEXT] = { | |
"callback" : arg[offset++], | |
"context" : context | |
}; | |
} | |
// Set tail handler | |
handlers[TAIL] = tail; | |
// Want memory and have memory | |
if (memory && MEMORY in handlers) { | |
// Get memory | |
memory = handlers[MEMORY]; | |
// Get handled | |
handled = memory[HANDLED]; | |
// Optimize for arguments | |
if (memory[LENGTH] > 0 ) { | |
// Loop through handlers | |
while(handler) { | |
// Skip to next handler if this handler has already been handled | |
if (handler[HANDLED] === handled) { | |
handler = handler[NEXT]; | |
continue; | |
} | |
// Store handled | |
handler[HANDLED] = handled; | |
// Apply handler callback | |
handler[CALLBACK].apply(handler[CONTEXT], memory); | |
// Update handler | |
handler = handler[NEXT]; | |
} | |
} | |
// Optimize for no arguments | |
else { | |
// Loop through handlers | |
while(handler) { | |
// Skip to next handler if this handler has already been handled | |
if (handler[HANDLED] === handled) { | |
handler = handler[NEXT]; | |
continue; | |
} | |
// Store handled | |
handler[HANDLED] = handled; | |
// Call handler callback | |
handler[CALLBACK].call(handler[CONTEXT]); | |
// Update handler | |
handler = handler[NEXT]; | |
} | |
} | |
} | |
} | |
// No handlers | |
else { | |
// Create head and tail | |
head = tail = { | |
"callback" : arg[offset++], | |
"context" : context | |
}; | |
// Iterate handlers from offset | |
while (offset < length) { | |
// Set tail -> tail.next -> handler | |
tail = tail[NEXT] = { | |
"callback" : arg[offset++], | |
"context" : context | |
}; | |
} | |
// Create event list | |
handlers[event] = { | |
"head" : head, | |
"tail" : tail | |
}; | |
} | |
return self; | |
}, | |
/** | |
* Unsubscribes from event | |
* | |
* @param event Event to unsubscribe from | |
* @param context (optional) context to scope callbacks to | |
* @param callback (optional) Callback to unsubscribe, if none | |
* are provided all callbacks are unsubscribed | |
* @returns self | |
*/ | |
off : function off(event /*, context, callback, callback, ..*/) { | |
var self = this; | |
var arg = arguments; | |
var length = arg[LENGTH]; | |
var context = arg[1]; | |
var callback = arg[2]; | |
var handlers = self[HANDLERS]; | |
var handler; | |
var head; | |
var previous; | |
var offset; | |
// No context or memory was supplied | |
if (context instanceof FUNCTION) { | |
callback = context; | |
context = ROOT; | |
offset = 1; | |
} | |
// All arguments were supplied | |
else if (callback instanceof FUNCTION){ | |
offset = 2; | |
} | |
// Something is wrong, return fast | |
else { | |
return self; | |
} | |
// Fast fail if we don't have subscribers | |
if (!(event in handlers)) { | |
return self; | |
} | |
// Get handlers | |
handlers = handlers[event]; | |
// Get head | |
head = handlers[HEAD]; | |
// Loop over remaining arguments | |
while (offset < length) { | |
// Store callback | |
callback = arg[offset++]; | |
// Get first handler | |
handler = previous = head; | |
// Loop through handlers | |
do { | |
// Check if this handler should be unlinked | |
if (handler[CALLBACK] === callback && handler[CONTEXT] === context) { | |
// Is this the first handler | |
if (handler === head) { | |
// Re-link head and previous, then | |
// continue | |
head = previous = handler[NEXT]; | |
continue; | |
} | |
// Unlink current handler, then continue | |
previous[NEXT] = handler[NEXT]; | |
continue; | |
} | |
// Update previous pointer | |
previous = handler; | |
} while ((handler = handler[NEXT]) !== UNDEFINED); | |
} | |
// Update head and tail | |
if (head && previous) { | |
handlers[HEAD] = head; | |
handlers[TAIL] = previous; | |
} | |
else { | |
delete handlers[HEAD]; | |
delete handlers[TAIL]; | |
} | |
return self; | |
}, | |
/** | |
* Emit an event | |
* | |
* @param event Event to emit | |
* @param arg (optional) Argument | |
* @returns self | |
*/ | |
emit : function emit(event /*, arg, arg, ..*/) { | |
var self = this; | |
var arg = arguments; | |
var handlers = self[HANDLERS]; | |
var handler; | |
// Store handled | |
var handled = arg[HANDLED] = COUNT++; | |
// Have handlers | |
if (event in handlers) { | |
// Get handlers | |
handlers = handlers[event]; | |
// Remember arguments | |
handlers[MEMORY] = arg; | |
// Get first handler | |
handler = handlers[HEAD]; | |
// Optimize for arguments | |
if (arg[LENGTH] > 0) { | |
// Loop through handlers | |
while(handler) { | |
// Skip to next handler if this handler has already been handled | |
if (handler[HANDLED] === handled) { | |
handler = handler[NEXT]; | |
continue; | |
} | |
// Update handled | |
handler[HANDLED] = handled; | |
// Apply handler callback | |
handler[CALLBACK].apply(handler[CONTEXT], arg); | |
// Update handler | |
handler = handler[NEXT]; | |
} | |
} | |
// Optimize for no arguments | |
else { | |
// Loop through handlers | |
while(handler) { | |
// Skip to next handler if this handler has already been handled | |
if (handler[HANDLED] === handled) { | |
handler = handler[NEXT]; | |
continue; | |
} | |
// Update handled | |
handler[HANDLED] = handled; | |
// Call handler callback | |
handler[CALLBACK].call(handler[CONTEXT]); | |
// Update handler | |
handler = handler[NEXT]; | |
} | |
} | |
} | |
// No handlers | |
else if (arg[LENGTH] > 0){ | |
// Create handlers and store with event | |
handlers[event] = handlers = {}; | |
// Remember arguments | |
handlers[MEMORY] = arg; | |
} | |
return this; | |
} | |
}); | |
}); | |
/*! | |
* TroopJS base component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*jshint strict:false, smarttabs:true */ | |
/*global define:true */ | |
define('troopjs-core/component/base',[ "../event/emitter", "config" ], function ComponentModule(Emitter, config) { | |
var COUNT = 0; | |
var INSTANCE_COUNT = "instanceCount"; | |
var Component = Emitter.extend(function Component() { | |
this[INSTANCE_COUNT] = COUNT++; | |
}, { | |
displayName : "core/component", | |
/** | |
* Application configuration | |
*/ | |
config : config | |
}); | |
/** | |
* Generates string representation of this object | |
* @returns Combination displayName and instanceCount | |
*/ | |
Component.prototype.toString = function () { | |
var self = this; | |
return self.displayName + "@" + self[INSTANCE_COUNT]; | |
}; | |
return Component; | |
}); | |
/*! | |
* TroopJS pubsub/hub module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*jshint strict:false, smarttabs:true */ | |
/*global define:true */ | |
define('troopjs-core/pubsub/hub',[ "compose", "../component/base" ], function HubModule(Compose, Component) { | |
var from = Compose.from; | |
return Compose.create(Component, { | |
displayName: "core/pubsub/hub", | |
subscribe : from(Component, "on"), | |
unsubscribe : from(Component, "off"), | |
publish : from(Component, "emit") | |
}); | |
}); | |
/*! | |
* TroopJS gadget component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*jshint strict:false, smarttabs:true, newcap:false, forin:false, loopfunc:true */ | |
/*global define:true */ | |
define('troopjs-core/component/gadget',[ "compose", "./base", "troopjs-utils/deferred", "../pubsub/hub" ], function GadgetModule(Compose, Component, Deferred, hub) { | |
var UNDEFINED; | |
var NULL = null; | |
var FUNCTION = Function; | |
var RE_HUB = /^hub(?::(\w+))?\/(.+)/; | |
var RE_SIG = /^sig\/(.+)/; | |
var PUBLISH = hub.publish; | |
var SUBSCRIBE = hub.subscribe; | |
var UNSUBSCRIBE = hub.unsubscribe; | |
var MEMORY = "memory"; | |
var SUBSCRIPTIONS = "subscriptions"; | |
return Component.extend(function Gadget() { | |
var self = this; | |
var bases = self.constructor._getBases(true); | |
var base; | |
var callbacks; | |
var callback; | |
var i; | |
var j; | |
var jMax; | |
var signals = {}; | |
var signal; | |
var matches; | |
var key = null; | |
// Iterate base chain (while there's a prototype) | |
for (i = bases.length - 1; i >= 0; i--) { | |
base = bases[i]; | |
add: for (key in base) { | |
// Get value | |
callback = base[key]; | |
// Continue if value is not a function | |
if (!(callback instanceof FUNCTION)) { | |
continue; | |
} | |
// Match signature in key | |
matches = RE_SIG.exec(key); | |
if (matches !== NULL) { | |
// Get signal | |
signal = matches[1]; | |
// Have we stored any callbacks for this signal? | |
if (signal in signals) { | |
// Get callbacks (for this signal) | |
callbacks = signals[signal]; | |
// Reset counters | |
j = jMax = callbacks.length; | |
// Loop callbacks, continue add if we've already added this callback | |
while (j--) { | |
if (callback === callbacks[j]) { | |
continue add; | |
} | |
} | |
// Add callback to callbacks chain | |
callbacks[jMax] = callback; | |
} | |
else { | |
// First callback | |
signals[signal] = [ callback ]; | |
} | |
} | |
} | |
} | |
// Extend self | |
Compose.call(self, { | |
signal : function onSignal(signal, deferred) { | |
var _self = this; | |
var _callbacks; | |
var _j; | |
var head = deferred; | |
// Only trigger if we have callbacks for this signal | |
if (signal in signals) { | |
// Get callbacks | |
_callbacks = signals[signal]; | |
// Reset counter | |
_j = _callbacks.length; | |
// Build deferred chain from end to 1 | |
while (--_j) { | |
// Create new deferred | |
head = Deferred(function (dfd) { | |
// Store callback and deferred as they will have changed by the time we exec | |
var _callback = _callbacks[_j]; | |
var _deferred = head; | |
// Add done handler | |
dfd.done(function done() { | |
_callback.call(_self, signal, _deferred); | |
}); | |
}); | |
} | |
// Execute first sCallback, use head deferred | |
_callbacks[0].call(_self, signal, head); | |
} | |
else if (deferred) { | |
deferred.resolve(); | |
} | |
return _self; | |
} | |
}); | |
}, { | |
displayName : "core/component/gadget", | |
"sig/initialize" : function initialize(signal, deferred) { | |
var self = this; | |
var subscriptions = self[SUBSCRIPTIONS] = []; | |
var key = NULL; | |
var value; | |
var matches; | |
var topic; | |
// Loop over each property in gadget | |
for (key in self) { | |
// Get value | |
value = self[key]; | |
// Continue if value is not a function | |
if (!(value instanceof FUNCTION)) { | |
continue; | |
} | |
// Match signature in key | |
matches = RE_HUB.exec(key); | |
if (matches !== NULL) { | |
// Get topic | |
topic = matches[2]; | |
// Subscribe | |
hub.subscribe(topic, self, matches[1] === MEMORY, value); | |
// Store in subscriptions | |
subscriptions[subscriptions.length] = [topic, self, value]; | |
// NULL value | |
self[key] = NULL; | |
} | |
} | |
if (deferred) { | |
deferred.resolve(); | |
} | |
return self; | |
}, | |
"sig/finalize" : function finalize(signal, deferred) { | |
var self = this; | |
var subscriptions = self[SUBSCRIPTIONS]; | |
var subscription; | |
// Loop over subscriptions | |
while ((subscription = subscriptions.shift()) !== UNDEFINED) { | |
hub.unsubscribe(subscription[0], subscription[1], subscription[2]); | |
} | |
if (deferred) { | |
deferred.resolve(); | |
} | |
return self; | |
}, | |
/** | |
* Calls hub.publish in self context | |
* @returns self | |
*/ | |
publish : function publish() { | |
var self = this; | |
PUBLISH.apply(hub, arguments); | |
return self; | |
}, | |
/** | |
* Calls hub.subscribe in self context | |
* @returns self | |
*/ | |
subscribe : function subscribe() { | |
var self = this; | |
SUBSCRIBE.apply(hub, arguments); | |
return self; | |
}, | |
/** | |
* Calls hub.unsubscribe in self context | |
* @returns self | |
*/ | |
unsubscribe : function unsubscribe() { | |
var self = this; | |
UNSUBSCRIBE.apply(hub, arguments); | |
return self; | |
}, | |
start : function start(deferred) { | |
var self = this; | |
deferred = deferred || Deferred(); | |
Deferred(function deferredStart(dfdStart) { | |
dfdStart.then(deferred.resolve, deferred.reject, deferred.notify); | |
Deferred(function deferredInitialize(dfdInitialize) { | |
dfdInitialize.then(function doneInitialize() { | |
self.signal("start", dfdStart); | |
}, dfdStart.reject, dfdStart.notify); | |
self.signal("initialize", dfdInitialize); | |
}); | |
}); | |
return self; | |
}, | |
stop : function stop(deferred) { | |
var self = this; | |
deferred = deferred || Deferred(); | |
Deferred(function deferredFinalize(dfdFinalize) { | |
dfdFinalize.then(deferred.resolve, deferred.reject, deferred.notify); | |
Deferred(function deferredStop(dfdStop) { | |
dfdStop.then(function doneStop() { | |
self.signal("finalize", dfdFinalize); | |
}, dfdFinalize.reject, dfdFinalize.notify); | |
self.signal("stop", dfdStop); | |
}); | |
}); | |
return self; | |
} | |
}); | |
}); | |
/*! | |
* TroopJS service component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
define('troopjs-core/component/service',[ "./gadget" ], function ServiceModule(Gadget) { | |
return Gadget.extend({ | |
displayName : "core/component/service" | |
}); | |
}); | |
/*! | |
* TroopJS widget component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*jshint strict:false, smarttabs:true, newcap:false */ | |
/*global define:true */ | |
define('troopjs-core/component/widget',[ "./gadget", "jquery", "troopjs-utils/deferred" ], function WidgetModule(Gadget, $, Deferred) { | |
var UNDEFINED; | |
var NULL = null; | |
var FUNCTION = Function; | |
var ARRAY_PROTO = Array.prototype; | |
var SHIFT = ARRAY_PROTO.shift; | |
var UNSHIFT = ARRAY_PROTO.unshift; | |
var $TRIGGER = $.fn.trigger; | |
var $ONE = $.fn.one; | |
var $BIND = $.fn.bind; | |
var $UNBIND = $.fn.unbind; | |
var RE = /^dom(?::(\w+))?\/([^\.]+(?:\.(.+))?)/; | |
var REFRESH = "widget/refresh"; | |
var $ELEMENT = "$element"; | |
var $PROXIES = "$proxies"; | |
var ONE = "one"; | |
var THEN = "then"; | |
var ATTR_WEAVE = "[data-weave]"; | |
var ATTR_WOVEN = "[data-woven]"; | |
/** | |
* Creates a proxy of the inner method 'handlerProxy' with the 'topic', 'widget' and handler parameters set | |
* @param topic event topic | |
* @param widget target widget | |
* @param handler target handler | |
* @returns {Function} proxied handler | |
*/ | |
function eventProxy(topic, widget, handler) { | |
/** | |
* Creates a proxy of the outer method 'handler' that first adds 'topic' to the arguments passed | |
* @returns result of proxied hanlder invocation | |
*/ | |
return function handlerProxy() { | |
// Add topic to front of arguments | |
UNSHIFT.call(arguments, topic); | |
// Apply with shifted arguments to handler | |
return handler.apply(widget, arguments); | |
}; | |
} | |
/** | |
* Creates a proxy of the inner method 'render' with the '$fn' parameter set | |
* @param $fn jQuery method | |
* @returns {Function} proxied render | |
*/ | |
function renderProxy($fn) { | |
/** | |
* Renders contents into element | |
* @param contents (Function | String) Template/String to render | |
* @param data (Object) If contents is a template - template data (optional) | |
* @param deferred (Deferred) Deferred (optional) | |
* @returns self | |
*/ | |
function render(/* contents, data, ..., deferred */) { | |
var self = this; | |
var $element = self[$ELEMENT]; | |
var arg = arguments; | |
// Shift contents from first argument | |
var contents = SHIFT.call(arg); | |
// Assume deferred is the last argument | |
var deferred = arg[arg.length - 1]; | |
// If deferred not a true Deferred, make it so | |
if (deferred === UNDEFINED || !(deferred[THEN] instanceof FUNCTION)) { | |
deferred = Deferred(); | |
} | |
// Defer render (as weaving it may need to load async) | |
Deferred(function deferredRender(dfdRender) { | |
// Link deferred | |
dfdRender.then(function renderDone() { | |
// Trigger refresh | |
$element.trigger(REFRESH, arguments); | |
// Resolve outer deferred | |
deferred.resolve(); | |
}, deferred.reject, deferred.notify); | |
// Notify that we're about to render | |
dfdRender.notify("beforeRender", self); | |
// Call render with contents (or result of contents if it's a function) | |
$fn.call($element, contents instanceof FUNCTION ? contents.apply(self, arg) : contents); | |
// Notify that we're rendered | |
dfdRender.notify("afterRender", self); | |
// Weave element | |
$element.find(ATTR_WEAVE).weave(dfdRender); | |
}); | |
return self; | |
} | |
return render; | |
} | |
return Gadget.extend(function Widget($element, displayName) { | |
var self = this; | |
self[$ELEMENT] = $element; | |
if (displayName) { | |
self.displayName = displayName; | |
} | |
}, { | |
displayName : "core/component/widget", | |
"sig/initialize" : function initialize(signal, deferred) { | |
var self = this; | |
var $element = self[$ELEMENT]; | |
var $proxies = self[$PROXIES] = []; | |
var key = NULL; | |
var value; | |
var matches; | |
var topic; | |
// Loop over each property in widget | |
for (key in self) { | |
// Get value | |
value = self[key]; | |
// Continue if value is not a function | |
if (!(value instanceof FUNCTION)) { | |
continue; | |
} | |
// Match signature in key | |
matches = RE.exec(key); | |
if (matches !== NULL) { | |
// Get topic | |
topic = matches[2]; | |
// Replace value with a scoped proxy | |
value = eventProxy(topic, self, value); | |
// Either ONE or BIND element | |
(matches[2] === ONE ? $ONE : $BIND).call($element, topic, self, value); | |
// Store in $proxies | |
$proxies[$proxies.length] = [topic, value]; | |
// NULL value | |
self[key] = NULL; | |
} | |
} | |
if (deferred) { | |
deferred.resolve(); | |
} | |
return self; | |
}, | |
"sig/finalize" : function finalize(signal, deferred) { | |
var self = this; | |
var $element = self[$ELEMENT]; | |
var $proxies = self[$PROXIES]; | |
var $proxy; | |
// Loop over subscriptions | |
while (($proxy = $proxies.shift()) !== UNDEFINED) { | |
$element.unbind($proxy[0], $proxy[1]); | |
} | |
delete self[$ELEMENT]; | |
if (deferred) { | |
deferred.resolve(); | |
} | |
return self; | |
}, | |
/** | |
* Weaves all children of $element | |
* @param deferred (Deferred) Deferred (optional) | |
* @returns self | |
*/ | |
weave : function weave(deferred) { | |
var self = this; | |
self[$ELEMENT].find(ATTR_WEAVE).weave(deferred); | |
return self; | |
}, | |
/** | |
* Unweaves all children of $element _and_ self | |
* @param deferred (Deferred) Deferred (optional) | |
* @returns self | |
*/ | |
unweave : function unweave(deferred) { | |
var self = this; | |
self[$ELEMENT].find(ATTR_WOVEN).andSelf().unweave(deferred); | |
return this; | |
}, | |
/** | |
* Binds event from $element, exactly once | |
* @returns self | |
*/ | |
one : function one() { | |
var self = this; | |
$ONE.apply(self[$ELEMENT], arguments); | |
return self; | |
}, | |
/** | |
* Binds event to $element | |
* @returns self | |
*/ | |
bind : function bind() { | |
var self = this; | |
$BIND.apply(self[$ELEMENT], arguments); | |
return self; | |
}, | |
/** | |
* Unbinds event from $element | |
* @returns self | |
*/ | |
unbind : function unbind() { | |
var self = this; | |
$UNBIND.apply(self[$ELEMENT], arguments); | |
return self; | |
}, | |
/** | |
* Triggers event on $element | |
* @returns self | |
*/ | |
trigger : function trigger() { | |
var self = this; | |
$TRIGGER.apply(self[$ELEMENT], arguments); | |
return self; | |
}, | |
/** | |
* Renders content and inserts it before $element | |
*/ | |
before : renderProxy($.fn.before), | |
/** | |
* Renders content and inserts it after $element | |
*/ | |
after : renderProxy($.fn.after), | |
/** | |
* Renders content and replaces $element contents | |
*/ | |
html : renderProxy($.fn.html), | |
/** | |
* Renders content and replaces $element contents | |
*/ | |
text : renderProxy($.fn.text), | |
/** | |
* Renders content and appends it to $element | |
*/ | |
append : renderProxy($.fn.append), | |
/** | |
* Renders content and prepends it to $element | |
*/ | |
prepend : renderProxy($.fn.prepend), | |
/** | |
* Empties widget | |
* @param deferred (Deferred) Deferred (optional) | |
* @returns self | |
*/ | |
empty : function empty(deferred) { | |
var self = this; | |
// Ensure we have deferred | |
deferred = deferred || Deferred(); | |
// Create deferred for emptying | |
Deferred(function emptyDeferred(dfdEmpty) { | |
// Link deferred | |
dfdEmpty.then(deferred.resolve, deferred.reject, deferred.notify); | |
// Get element | |
var $element = self[$ELEMENT]; | |
// Detach contents | |
var $contents = $element.contents().detach(); | |
// Trigger refresh | |
$element.trigger(REFRESH, self); | |
// Use timeout in order to yield | |
setTimeout(function emptyTimeout() { | |
// Get DOM elements | |
var contents = $contents.get(); | |
// Remove elements from DOM | |
$contents.remove(); | |
// Resolve deferred | |
dfdEmpty.resolve(contents); | |
}, 0); | |
}); | |
return self; | |
} | |
}); | |
}); | |
/*! | |
* TroopJS dimensions/service module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
define('troopjs-core/dimensions/service',[ "../component/service" ], function DimensionsServiceModule(Service) { | |
var DIMENSIONS = "dimensions"; | |
var $ELEMENT = "$element"; | |
function onDimensions($event, w, h) { | |
$event.data.publish(DIMENSIONS, w, h); | |
} | |
return Service.extend(function DimensionsService($element, dimensions) { | |
var self = this; | |
self[$ELEMENT] = $element; | |
self[DIMENSIONS] = dimensions; | |
}, { | |
displayName : "core/dimensions/service", | |
"sig/initialize" : function initialize(signal, deferred) { | |
var self = this; | |
self[$ELEMENT].bind(DIMENSIONS + "." + self[DIMENSIONS], self, onDimensions); | |
if (deferred) { | |
deferred.resolve(); | |
} | |
}, | |
"sig/start" : function start(signal, deferred) { | |
var self = this; | |
self[$ELEMENT].trigger("resize." + DIMENSIONS); | |
if (deferred) { | |
deferred.resolve(); | |
} | |
}, | |
"sig/finalize" : function finalize(signal, deferred) { | |
var self = this; | |
self[$ELEMENT].unbind(DIMENSIONS + "." + self[DIMENSIONS], onDimensions); | |
if (deferred) { | |
deferred.resolve(); | |
} | |
} | |
}); | |
}); | |
/*! | |
* TroopJS store/base module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
define('troopjs-core/store/base',[ "compose", "../component/gadget" ], function StoreModule(Compose, Gadget) { | |
var STORAGE = "storage"; | |
return Gadget.extend({ | |
storage : Compose.required, | |
set : function set(key, value, deferred) { | |
// JSON encoded 'value' then store as 'key' | |
this[STORAGE].setItem(key, JSON.stringify(value)); | |
// Resolve deferred | |
if (deferred) { | |
deferred.resolve(value); | |
} | |
}, | |
get : function get(key, deferred) { | |
// Get value from 'key', parse JSON | |
var value = JSON.parse(this[STORAGE].getItem(key)); | |
// Resolve deferred | |
if (deferred) { | |
deferred.resolve(value); | |
} | |
}, | |
remove : function remove(key, deferred) { | |
// Remove key | |
this[STORAGE].removeItem(key); | |
// Resolve deferred | |
if (deferred) { | |
deferred.resolve(); | |
} | |
}, | |
clear : function clear(deferred) { | |
// Clear | |
this[STORAGE].clear(); | |
// Resolve deferred | |
if (deferred) { | |
deferred.resolve(); | |
} | |
} | |
}); | |
}); | |
/*! | |
* TroopJS store/session module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
define('troopjs-core/store/session',[ "compose", "./base" ], function StoreSessionModule(Compose, Store) { | |
return Compose.create(Store, { | |
displayName : "core/store/session", | |
storage: window.sessionStorage | |
}); | |
}); | |
/*! | |
* TroopJS store/local module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
define('troopjs-core/store/local',[ "compose", "./base" ], function StoreLocalModule(Compose, Store) { | |
return Compose.create(Store, { | |
displayName : "core/store/local", | |
storage : window.localStorage | |
}); | |
}); | |
/*! | |
* TroopJS route/router module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
define('troopjs-core/route/router',[ "../component/service", "troopjs-utils/uri" ], function RouterModule(Service, URI) { | |
var HASHCHANGE = "hashchange"; | |
var $ELEMENT = "$element"; | |
var ROUTE = "route"; | |
var RE = /^#/; | |
function onHashChange($event) { | |
var self = $event.data; | |
// Create URI | |
var uri = URI($event.target.location.hash.replace(RE, "")); | |
// Convert to string | |
var route = uri.toString(); | |
// Did anything change? | |
if (route !== self[ROUTE]) { | |
// Store new value | |
self[ROUTE] = route; | |
// Publish route | |
self.publish(ROUTE, uri); | |
} | |
} | |
return Service.extend(function RouterService($element) { | |
this[$ELEMENT] = $element; | |
}, { | |
displayName : "core/route/router", | |
"sig/initialize" : function initialize(signal, deferred) { | |
var self = this; | |
self[$ELEMENT].bind(HASHCHANGE, self, onHashChange); | |
if (deferred) { | |
deferred.resolve(); | |
} | |
return self; | |
}, | |
"sig/start" : function start(signal, deferred) { | |
var self = this; | |
self[$ELEMENT].trigger(HASHCHANGE); | |
if (deferred) { | |
deferred.resolve(); | |
} | |
return self; | |
}, | |
"sig/finalize" : function finalize(signal, deferred) { | |
var self = this; | |
self[$ELEMENT].unbind(HASHCHANGE, onHashChange); | |
if (deferred) { | |
deferred.resolve(); | |
} | |
return self; | |
} | |
}); | |
}); | |
/*! | |
* TroopJS widget/placeholder component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*jshint strict:false, smarttabs:true, laxbreak:true */ | |
/*global define:true */ | |
define('troopjs-core/widget/placeholder',[ "../component/widget", "troopjs-utils/deferred", "require" ], function WidgetPlaceholderModule(Widget, Deferred, parentRequire) { | |
var FUNCTION = Function; | |
var POP = Array.prototype.pop; | |
var HOLDING = "holding"; | |
var DATA_HOLDING = "data-" + HOLDING; | |
var $ELEMENT = "$element"; | |
var TARGET = "target"; | |
var THEN = "then"; | |
function release(/* arg, arg, arg, deferred*/) { | |
var self = this; | |
var arg = arguments; | |
var argc = arg.length; | |
// If deferred not a true Deferred, make it so | |
var deferred = argc > 0 && arg[argc - 1][THEN] instanceof FUNCTION | |
? POP.call(arg) | |
: Deferred(); | |
Deferred(function deferredRelease(dfdRelease) { | |
var i; | |
var iMax; | |
var name; | |
var argv; | |
// We're already holding something, resolve with cache | |
if (HOLDING in self) { | |
dfdRelease | |
.done(deferred.resolve) | |
.resolve(self[HOLDING]); | |
} | |
else { | |
// Add done handler to release | |
dfdRelease.then([ function doneRelease(widget) { | |
// Set DATA_HOLDING attribute | |
self[$ELEMENT].attr(DATA_HOLDING, widget); | |
// Store widget | |
self[HOLDING] = widget; | |
}, deferred.resolve ], deferred.reject, deferred.notify); | |
// Get widget name | |
name = self[TARGET]; | |
// Set initial argv | |
argv = [ self[$ELEMENT], name ]; | |
// Append values from arg to argv | |
for (i = 0, iMax = arg.length; i < iMax; i++) { | |
argv[i + 2] = arg[i]; | |
} | |
// Require widget by name | |
parentRequire([ name ], function required(Widget) { | |
// Defer require | |
Deferred(function deferredStart(dfdRequire) { | |
// Constructed and initialized instance | |
var widget = Widget | |
.apply(Widget, argv); | |
// Link deferred | |
dfdRequire.then(function doneStart() { | |
dfdRelease.resolve(widget); | |
}, dfdRelease.reject, dfdRelease.notify); | |
// Start | |
widget.start(dfdRequire); | |
}); | |
}); | |
} | |
}); | |
return self; | |
} | |
function hold(deferred) { | |
var self = this; | |
deferred = deferred || Deferred(); | |
Deferred(function deferredHold(dfdHold) { | |
var widget; | |
// Link deferred | |
dfdHold.then(deferred.resolve, deferred.reject, deferred.notify); | |
// Check that we are holding | |
if (HOLDING in self) { | |
// Get what we're holding | |
widget = self[HOLDING]; | |
// Cleanup | |
delete self[HOLDING]; | |
// Remove DATA_HOLDING attribute | |
self[$ELEMENT].removeAttr(DATA_HOLDING); | |
// Stop | |
widget.stop(dfdHold); | |
} | |
else { | |
dfdHold.resolve(); | |
} | |
}); | |
return self; | |
} | |
return Widget.extend(function WidgetPlaceholder($element, name, target) { | |
this[TARGET] = target; | |
}, { | |
displayName : "core/widget/placeholder", | |
"sig/finalize" : function finalize(signal, deferred) { | |
this.hold(deferred); | |
}, | |
release : release, | |
hold : hold | |
}); | |
}); | |
/*! | |
* TroopJS route/placeholder module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
define('troopjs-core/route/placeholder',[ "../widget/placeholder" ], function RoutePlaceholderModule(Placeholder) { | |
var NULL = null; | |
var ROUTE = "route"; | |
return Placeholder.extend(function RoutePlaceholderWidget($element, name) { | |
this[ROUTE] = RegExp($element.data("route")); | |
}, { | |
"displayName" : "core/route/placeholder", | |
"hub:memory/route" : function onRoute(topic, uri) { | |
var self = this; | |
var matches = self[ROUTE].exec(uri.path); | |
if (matches !== NULL) { | |
self.release.apply(self, matches.slice(1)); | |
} | |
else { | |
self.hold(); | |
} | |
} | |
}); | |
}); | |
/*! | |
* TroopJS widget/application component | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
define('troopjs-core/widget/application',[ "../component/widget", "troopjs-utils/deferred" ], function ApplicationModule(Widget, Deferred) { | |
return Widget.extend({ | |
displayName : "core/widget/application", | |
"sig/start" : function start(signal, deferred) { | |
this.weave(deferred); | |
}, | |
"sig/stop" : function stop(signal, deferred) { | |
this.unweave(deferred); | |
} | |
}); | |
}); | |
/*! | |
* TroopJS pubsub/topic module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
/*jshint strict:false, smarttabs:true, laxbreak:true */ | |
/*global define:true */ | |
define('troopjs-core/pubsub/topic',[ "../component/base", "troopjs-utils/unique" ], function TopicModule(Component, unique) { | |
var TOSTRING = Object.prototype.toString; | |
var TOSTRING_ARRAY = TOSTRING.call(Array.prototype); | |
function comparator (a, b) { | |
return a.publisherInstanceCount === b.publisherInstanceCount; | |
} | |
var Topic = Component.extend(function Topic(topic, publisher, parent) { | |
var self = this; | |
self.topic = topic; | |
self.publisher = publisher; | |
self.parent = parent; | |
self.publisherInstanceCount = publisher.instanceCount; | |
}, { | |
displayName : "core/pubsub/topic", | |
/** | |
* Traces topic origin to root | |
* @returns String representation of all topics traced down to root | |
*/ | |
trace : function trace() { | |
var current = this; | |
var constructor = current.constructor; | |
var parent; | |
var item; | |
var stack = ""; | |
var i; | |
var u; | |
var iMax; | |
while (current) { | |
if (TOSTRING.call(current) === TOSTRING_ARRAY) { | |
u = unique.call(current, comparator); | |
for (i = 0, iMax = u.length; i < iMax; i++) { | |
item = u[i]; | |
u[i] = item.constructor === constructor | |
? item.trace() | |
: item.topic; | |
} | |
stack += u.join(","); | |
break; | |
} | |
parent = current.parent; | |
stack += parent | |
? current.publisher + ":" | |
: current.publisher; | |
current = parent; | |
} | |
return stack; | |
} | |
}); | |
/** | |
* Generates string representation of this object | |
* @returns Instance topic | |
*/ | |
Topic.prototype.toString = function () { | |
return this.topic; | |
}; | |
return Topic; | |
}); | |
/*! | |
* TroopJS remote/ajax module | |
* @license TroopJS Copyright 2012, Mikael Karon <[email protected]> | |
* Released under the MIT license. | |
*/ | |
define('troopjs-core/remote/ajax',[ "../component/service", "../pubsub/topic", "jquery", "troopjs-utils/merge" ], function AjaxModule(Service, Topic, $, merge) { | |
return Service.extend({ | |
displayName : "core/remote/ajax", | |
"hub/ajax" : function request(topic, settings, deferred) { | |
// Request | |
$.ajax(merge.call({ | |
"headers": { | |
"x-request-id": new Date().getTime(), | |
"x-components": topic instanceof Topic ? topic.trace() : topic | |
} | |
}, settings)).then(deferred.resolve, deferred.reject, deferred.notify); | |
} | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* TroopJS Bundle - 1.0.7-31-g8d0ee03-dirty | |
* http://troopjs.com/ | |
* Copyright (c) 2013 Mikael Karon <[email protected]> | |
* Licensed MIT | |
*/ | |
define("troopjs-requirejs/template",[],function(){function f(e){function l(e,n,r){return t[f]=n?'" +'+r+'+ "':'";'+r+'o += "',"<%"+String(f++)+"%>"}function c(e,n){return t[n]}function h(e,t){return a[t]||t}var t=[],f=0;return('function template(data) { var o = "'+e.replace(n,"").replace(r,l).replace(s,h).replace(i,c)+'"; return o; }').replace(o,u)}var t={node:function(){var e=require.nodeRequire("fs");return function(n,r){r(e.readFileSync(n,"utf8"))}},browser:function(){var e=["Msxml2.XMLHTTP","Microsoft.XMLHTTP","Msxml2.XMLHTTP.4.0"],t,n,r;if(typeof XMLHttpRequest!="undefined")n=XMLHttpRequest;else{for(r=0;r<3;r++){t=e[r];try{new ActiveXObject(t),n=function(){return new ActiveXObject(t)};break}catch(i){}}if(!n)throw new Error("XHR: XMLHttpRequest not available")}return function(t,r){var i=new n;i.open("GET",t,!0),i.onreadystatechange=function(e){i.readyState===4&&r(i.responseText)},i.send(null)}},rhino:function(){var e="utf-8",t=java.lang.System.getProperty("line.separator");return function(r,i){var s=new java.io.File(r),o=new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(s),e)),u=new java.lang.StringBuffer,a,f="";try{a=o.readLine(),a&&a.length()&&a.charAt(0)===65279&&(a=a.substring(1)),u.append(a);while((a=o.readLine())!==null)u.append(t),u.append(a);f=String(u.toString())}finally{o.close()}i(f)}},borked:function(){return function(){throw new Error("Environment unsupported.")}}},n=/^[\n\t\r]+|[\n\t\r]+$/g,r=/<%(=)?([\S\s]*?)%>/g,i=/<%(\d+)%>/gm,s=/(["\n\t\r])/gm,o=/o \+= "";| \+ ""/gm,u="",a={'"':'\\"',"\n":"\\n"," ":"\\t","\r":"\\r"},l={},c=t[typeof process!="undefined"&&process.versions&&!!process.versions.node?"node":typeof window!="undefined"&&window.navigator&&window.document||typeof importScripts!="undefined"?"browser":typeof Packages!="undefined"?"rhino":"borked"]();return{load:function(e,t,n,r){var i=t.toUrl(e);c(i,function(s){try{s="define(function() { return "+f(s,e,i,r.template)+"; })"}catch(o){throw o.message="In "+i+", "+o.message,o}r.isBuild?l[e]=s:s+="\n//@ sourceURL='"+i+"'",n.fromText(e,s),t([e],function(e){n(e)})})},write:function(e,t,n){l.hasOwnProperty(t)&&n.asModule(e+"!"+t,l[t])}}}),define("troopjs-jquery/hashchange",["jquery"],function(t){function a(e){var t=s.exec(e.location.href);return t&&t[1]?decodeURIComponent(t[1]):""}function f(e){var t=this,n;t.element=n=e.createElement("iframe"),n.src="about:blank",n.style.display="none"}var n="interval",r="hashchange",i="on"+r,s=/#(.*)$/,o=/\?/,u=0;f.prototype={getElement:function(){return this.element},getHash:function(){return this.element.contentWindow.frameHash},update:function(e){var t=this,n=t.element.contentWindow.document;if(t.getHash()===e)return;n.open(),n.write("<html><head><title>' + document.title + '</title><script type='text/javascript'>var frameHash='"+e+"';</script></head><body> </body></html>"),n.close()}},t.event.special[r]={setup:function(s,l,c){var h=this;if(i in h)return!1;if(!t.isWindow(h))throw new Error("Unable to bind 'hashchange' to a non-window object");var p=t(h),d=a(h),v=h.location;p.data(n,h.setInterval(u?function(){var t=h.document,n=v.protocol==="file:",i=new f(t);return t.body.appendChild(i.getElement()),i.update(d),function(){var t=d,s,u=a(h),f=i.getHash();f!==d&&f!==u?(s=decodeURIComponent(f),d!==s&&(d=s,i.update(d),p.trigger(r,[s,t])),v.hash="#"+encodeURI(n?f.replace(o,"%3F"):f)):u!==d&&(s=decodeURIComponent(u),d!==s&&(d=s,p.trigger(r,[s,t])))}}():function(){var t=d,n,i=a(h);i!==d&&(n=decodeURIComponent(i),d!==n&&(d=n,p.trigger(r,[n,t])))},25))},teardown:function(r){var s=this;if(i in s)return!1;s.clearInterval(t.data(s,n))}}}),define("troopjs-utils/getargs",[],function(){var t=Array.prototype.push,n=String.prototype.substring,r=/^(?:false|true)$/i,i=/^true$/i,s=/^\d+$/;return function(){var o=this,u=[],a,f,l,c,h,p,d=!1;for(f=l=c=0,a=o.length;c<a;c++){h=o.charAt(c);switch(h){case'"':case"'":d===h?(d=!1,t.call(u,n.call(o,f,l))):d=h,f=l=c+1;break;case",":if(d){l=c+1;break}f!==l&&(p=n.call(o,f,l),r.test(p)?p=i.test(p):s.test(p)&&(p=+p),t.call(u,p)),f=l=c+1;break;case" ":case" ":if(d){l=c+1;break}f===l&&(f=l=c+1);break;default:l=c+1}}return f!==l&&(p=n.call(o,f,l),r.test(p)?p=i.test(p):s.test(p)&&(p=+p),t.call(u,p)),u}}),define("troopjs-jquery/action",["jquery","troopjs-utils/getargs"],function(t,n){function c(e,t){return e?e+"."+u:s}function h(e){var n=t(this),r=o.call(arguments,1),s=a in e?e[a].type:u,f=e[u];e.type=u+"/"+f+"."+s,n.trigger(e,r),e.result!==i&&(e.type=u+"/"+f+"!",n.trigger(e,r),e.result!==i&&(e.type=u+"."+s,n.trigger(e,r)))}function p(e){var i=t(e.target).closest("[data-action]");if(i.length===0)return;var o=i.data(),a=f.exec(o[u]);if(a===s)return;var c=a[1],h=a[2],p=a[3];if(h!==r&&!RegExp(h.split(l).join("|")).test(e.type))return;var d=p!==r?n.call(p):[];t.each(d,function(t,n){n in o&&(d[t]=o[n])}),i.trigger(t.Event(e,{type:u+"!",action:c}),d),e.stopPropagation()}var r,i=!1,s=null,o=Array.prototype.slice,u="action",a="originalEvent",f=/^([\w\d\s_\-\/]+)(?:\.([\w\.]+))?(?:\((.*)\))?$/,l=/\.+/;t.event.special[u]={setup:function(n,r,i){t(this).bind(u,n,h)},add:function(n){var r=t.map(n.namespace.split(l),c);r.length!==0&&t(this).bind(r.join(" "),p)},remove:function(n){var r=t.map(n.namespace.split(l),c);r.length!==0&&t(this).unbind(r.join(" "),p)},teardown:function(n){t(this).unbind(u,h)}},t.fn[u]=function(n){return t(this).trigger({type:u+"!",action:n},o.call(arguments,1))}}),define("troopjs-jquery/destroy",["jquery"],function(t){t.event.special.destroy={remove:function(n){var r=this;n.handler.call(r,t.Event({type:n.type,data:n.data,namespace:n.namespace,target:r}))}}}),define("troopjs-jquery/weave",["require","jquery","troopjs-utils/getargs","./destroy"],function(t,n,r){function T(){n(this).unweave()}var i,s=null,o=Array,u=o.prototype,a=u.join,f=u.push,l=n.when,c=n.Deferred,h="weave",p="unweave",d="woven",v="weaving",m="pending",g="destroy",y="data-",b=y+h,w=y+d,E=y+v,S="["+b+"]",x="["+E+"],["+w+"]";n.expr[":"][h]=n.expr.createPseudo?n.expr.createPseudo(function(e){return e!==i&&(e=RegExp(n.map(r.call(e),function(e){return"^"+e+"$"}).join("|"),"m")),function(t){var r=n(t).attr(b);return r===i?!1:e===i?!0:e.test(r.split(/[\s,]+/).join("\n"))}}):function(e,t,s){var o=n(e).attr(b);return o===i?!1:s===i?!0:RegExp(n.map(r.call(s[3]),function(e){return"^"+e+"$"}).join("|"),"m").test(o.split(/[\s,]+/).join("\n"))},n.expr[":"][d]=n.expr.createPseudo?n.expr.createPseudo(function(e){return e!==i&&(e=RegExp(n.map(r.call(e),function(e){return"^"+e+"@\\d+"}).join("|"),"m")),function(t){var r=n(t).attr(w);return r===i?!1:e===i?!0:e.test(r.split(/[\s,]+/).join("\n"))}}):function(e,t,s){var o=n(e).attr(w);return o===i?!1:s===i?!0:RegExp(n.map(r.call(s[3]),function(e){return"^"+e+"@\\d+"}).join("|"),"m").test(o.split(/[\s,]+/).join("\n"))},n.fn[h]=function(){var o=[],u=0,p=n(this),v=arguments;return p.filter(S).each(function(p,y){c(function(p){var S=n(y),x=S.data(),N=x[h]=S.attr(b)||"",C=x[d]||(x[d]=[]),k=x[m]||(x[m]=[]);p.done(function(){S.removeAttr(E).attr(w,a.call(arguments," "))}),l.apply(n,k).then(function(){var a=/[\s,]*([\w_\-\/\.]+)(?:\(([^\)]+)\))?/g,h=u,d=0,m;f.call(k,p),S.removeAttr(b).attr(E,N).bind(g,T);while((m=a.exec(N))!==s)c(function(n){var s=d++,a,f,l,c;o[u++]=n,n.then(function(t){C[s]=t},p.reject,p.notify);var h=m[1],g=[S,h];for(a=0,l=v.length,f=g.length;a<l;a++,f++)g[f]=v[a];var y=m[2];if(y!==i){y=r.call(y);for(a=0,l=y.length,f=g.length;a<l;a++,f++)c=y[a],g[f]=c in x?x[c]:c}t([h],function(t){var r=t.apply(t,g);r.start().then(function(){n.resolve(r)},n.reject,n.notify)})});l.apply(n,o.slice(h,u)).then(p.resolve,p.reject,p.notify)},p.reject,p.notify)})}),c(function(t){l.apply(n,o).then(function(){t.resolve(arguments)},t.reject,t.progress)}).promise()},n.fn[p]=function(){var t=[],r=0,s=n(this);return s.filter(x).each(function(s,o){c(function(s){var u=n(o),a=u.data(),p=a[m]||(a[m]=[]),v=a[d]||[];s.done(function(){u.attr(b,a[h]).unbind(g,T),delete a[h]}),l.apply(n,p).done(function(){var o=r,h;f.call(p,s),delete a[d],u.removeAttr(w);while((h=v.shift())!==i)c(function(n){t[r++]=h.stop().then(function(){n.resolve(h)},n.reject,n.notify)});l.apply(n,t.slice(o,r)).then(s.resolve,s.reject,s.notify)})})}),c(function(r){l.apply(n,t).then(function(){r.resolve(arguments)},r.reject,r.progress)}).promise()},n.fn[d]=function(){var t=[],r=arguments.length>0?RegExp(n.map(arguments,function(e){return"^"+e+"$"}).join("|"),"m"):i;return n(this).each(function(s,o){if(!n.hasData(o))return;f.apply(t,r===i?n.data(o,d):n.map(n.data(o,d),function(e){return r.test(e.displayName)?e:i}))}),t}}),define("troopjs-jquery/dimensions",["jquery"],function(t){function f(e,t){return t-e}function l(e){var n=this,i=t(n),f=i.width(),l=i.height();t.each(t.data(n,r),function(t,n){var c=n[s],h=n[o],p,d,v;v=c.length,p=c[v-1];while(c[--v]<f)p=c[v];v=h.length,d=h[v-1];while(h[--v]<l)d=h[v];if(p!==n[u]||d!==n[a])n[u]=p,n[a]=d,i.trigger(r+"."+t,[p,d])})}var n=null,r="dimensions",i="resize."+r,s="w",o="h",u="_"+s,a="_"+o;t.event.special[r]={setup:function(n,s,o){t(this).bind(i,l).data(r,{})},add:function(i){var u=this,a=i.namespace,l={},c=l[s]=[],h=l[o]=[],p=/(w|h)(\d+)/g,d;while((d=p.exec(a))!==n)l[d[1]].push(parseInt(d[2],10));c.sort(f),h.sort(f),t.data(u,r)[a]=l},remove:function(n){delete t.data(this,r)[n.namespace]},teardown:function(n){t(this).removeData(r).unbind(i,l)}}}),define("troopjs-jquery/resize",["jquery"],function(t){function a(e,n){var o=t.data(n),u=t(n),a=u.width(),f=u.height();(a!==o[i]||f!==o[s])&&u.trigger(r,[o[i]=a,o[s]=f])}function f(){o.each(a)}var n=null,r="resize",i="w",s="h",o=t([]),u=n;t.event.special[r]={setup:function(n,a,l){var c=this;if(t.isWindow(c))return!1;var h=t.data(c,r,{}),p=t(c);h[i]=p.width(),h[s]=p.height(),o=o.add(c),o.length===1&&(u=setInterval(f,100))},teardown:function(i){var s=this;if(t.isWindow(s))return!1;t.removeData(s,r),o=o.not(s),o.length===0&&u!==n&&clearInterval(u)}}}),define("troopjs-utils/merge",[],function(){var t=Array,n=Object;return function r(e){var i=this,s=null,o,u,a,f;for(o=0,u=arguments.length;o<u;o++){e=arguments[o];for(s in e)a=e[s],f=a.constructor,s in i?f===t?i[s]=i[s].concat(a):f===n?r.call(i[s],a):i[s]=a:i[s]=a}return i}}),define("troopjs-utils/tr",[],function(){var t=typeof Number();return function(n){var r=this,i=[],s,o=r.length,u;if(typeof o===t&&o===0||o>0&&0 in r&&o-1 in r)for(s=0;s<o;s++)i.push(n.call(r,r[s],s));else if(r)for(u in r)i.push(n.call(r,r[u],u));return i}}),function(e){e("compose/compose",[],function(){function e(){}function n(e){if(!e)throw new Error("Compose arguments must be functions or objects");return e}function r(e,t,r){var s,o=t.length;for(;r<o;r++){var u=t[r];if(typeof u=="function"){var a=u.prototype;for(var l in a){s=a[l];var c=a.hasOwnProperty(l);if(typeof s=="function"&&l in e&&s!==e[l]){var p=e[l];s==f?s=p:c||(i(s,l,h([].slice.call(t,0,r),!0))?s=p:i(p,l,h([u],!0))||console.error("Conflicted method "+l+", final composer must explicitly override with correct method."))}s&&s.install&&c&&!i(p,l,h([u],!0))?s.install.call(e,l):e[l]=s}}else for(var l in n(u)){var s=u[l];if(typeof s=="function"){if(s.install){s.install.call(e,l);continue}if(l in e&&s==f)continue}e[l]=s}}return e}function i(e,t,n){for(var r=0;r<n.length;r++){var i=n[r];if(i[t]==e)return!0}}function s(e,t){function n(){if(t)return t.apply(this,arguments);throw new Error("Decorator not applied")}return n.install=e,n}function o(e){return function(t){return s(function n(r){var i=this[r];(t=this[r]=i?e(this,i,t):t).install=n},t)}}function f(){throw new Error("This method is required and no implementation has been provided")}function l(){var e=[this];return e.push.apply(e,arguments),c.apply(0,e)}function c(i){function u(){var t;this instanceof u?t=this:(e.prototype=o,t=new e);for(var n=0;n<f;n++){var r=a[n],i=r.apply(t,arguments);if(typeof i=="object")if(i instanceof u)t=i;else for(var s in i)i.hasOwnProperty(s)&&(t[s]=i[s])}return t}var s=arguments,o=s.length<2&&typeof s[0]!="function"?s[0]:r(t(n(i)),s,1);u._getBases=function(e){return e?p:a};var a=h(s),f=a.length;typeof s[s.length-1]=="object"&&(s[s.length-1]=o);var p=h(s,!0);return u.extend=l,c.secure||(o.constructor=u),u.prototype=o,u}function h(e,t){function r(e,i){e:for(var s=0;s<e.length;s++){var o=e[s],u=t&&typeof o=="function"?o.prototype:o;if(t||typeof o=="function"){var a=i&&o._getBases;if(a)r(a(t));else{for(var f=0;f<n.length;f++)if(u==n[f])continue e;n.push(u)}}}}var n=[];return r(e,!0),n}var t=Object.create?function(e){return Object.create(typeof e=="function"?e.prototype:e||Object.prototype)}:function(t){e.prototype=typeof t=="function"?t.prototype:t;var n=new e;return e.prototype=null,n};c._setMixin=function(e){r=e},c.Decorator=s,c.around=o(function(e,t,n){return n.call(e,t)}),c.before=o(function(e,t,n){return function(){var e=n.apply(this,arguments);if(e!==u)return t.apply(this,e||arguments)}});var u=c.stop={},a;return c.after=o(function(e,t,n){return function(){var e=t.apply(this,arguments),r=n.apply(this,arguments);return r===a?e:r}}),c.from=function(e,t){return t?(typeof e=="function"?e.prototype:e)[t]:s(function(n){if(!(this[n]=typeof e=="string"?this[e]:(typeof e=="function"?e.prototype:e)[t||n]))throw new Error("Source method "+t+" was not available to be renamed to "+n)})},c.create=function(e){var n=r(t(e),arguments,1),i=arguments.length;for(var s=0;s<i;s++){var o=arguments[s];typeof o=="function"&&(n=o.call(n)||n)}return n},c.required=f,c.apply=function(e,t){return e?r(e,t,0):l.apply.call(c,0,t)},c.call=function(e){return r(e,arguments,1)},c})}(typeof define!="undefined"?define:function(e,t){typeof module!="undefined"?module.exports=t():Compose=t()}),define("compose",["compose/compose"],function(e){return e}),define("troopjs-utils/uri",["compose"],function(t){function b(e){var t={},r,i=n,s,o=/(?:&|^)([^&=]*)=?([^&]*)/g;t.toString=b.toString;if(u.call(e)===a)for(i in e)t[i]=e[i];else while((r=o.exec(e))!==n)i=r[1],i in t?(s=t[i],u.call(s)===f?s[s.length]=r[2]:t[i]=[s,r[2]]):t[i]=r[2];return t}function w(e){var t=[];return t.toString=w.toString,s.apply(t,u.call(e)===f?e:o.call(e,"/")),t}var n=null,r=Array.prototype,i=Object.prototype,s=r.push,o=String.prototype.split,u=i.toString,a=u.call(i),f=u.call(r),l=u.call(Function.prototype),c=/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?(?:([^?#]*)(?:\?([^#]*))?(?:#(.*))?)/,h="protocol",p="authority",d="path",v="query",m="anchor",g=["source",h,p,"userInfo","user","password","host","port",d,v,m],y=t.secure;t.secure=!0,b.toString=function S(){var e=this,t,n,r,i=[],s=0,o;for(t in e){if(u.call(e[t])===l)continue;i[s++]=t}i.sort();while(s--){t=i[s],n=e[t];if(u.call(n)===f){r=n.slice(0),r.sort(),o=r.length;while(o--)n=r[o],r[o]=n===""?t:t+"="+n;i[s]=r.join("&")}else i[s]=n===""?t:t+"="+n}return i.join("&")},w.toString=function(){return this.join("/")};var E=t(function(t){var r=this,i,s,o;if((s=c.exec(t))!==n){o=s.length;while(o--)i=s[o],i&&(r[g[o]]=i)}v in r&&(r[v]=b(r[v])),d in r&&(r[d]=w(r[d]))});return E.prototype.toString=function(){var e=this,t=[h,"://",p,d,"?",v,"#",m],n,r;h in e||(t[0]=t[1]=""),p in e||(t[2]=""),d in e||(t[3]=""),v in e||(t[4]=t[5]=""),m in e||(t[6]=t[7]=""),n=t.length;while(n--)r=t[n],r in e&&(t[n]=e[r]);return t.join("")},t.secure=y,E.Path=w,E.Query=b,E}),define("troopjs-utils/unique",[],function(){return function(t){var n=this,r=n.length,i=[],s,o,u,a;e:for(o=u=a=0;o<r;o++,u=0){s=n[o];while(u<a)if(t.call(n,s,i[u++])===!0)continue e;i[a++]=s}return i}}),function(e){e("when/when",[],function(){function r(e,t,n,r){return i(e).then(t,n,r)}function i(e){var t,n;return e instanceof o?t=e:l(e)?(n=f(),e.then(function(e){n.resolve(e)},function(e){n.reject(e)},function(e){n.progress(e)}),t=n.promise):t=u(e),t}function s(e){return r(e,a)}function o(e){this.then=e}function u(e){var t=new o(function(t){try{return i(t?t(e):e)}catch(n){return a(n)}});return t}function a(e){var t=new o(function(t,n){try{return n?i(n(e)):a(e)}catch(r){return a(r)}});return t}function f(){function h(e,t,n){return u(e,t,n)}function p(e){return c(e)}function d(e){return c(a(e))}function v(e){return l(e)}var e,t,r,s,u,l,c;return t=new o(h),e={then:h,resolve:p,reject:d,progress:v,promise:t,resolver:{resolve:p,reject:d,progress:v}},r=[],s=[],u=function(e,t,n){var i,o;return i=f(),o=typeof n=="function"?function(e){try{i.progress(n(e))}catch(t){i.progress(t)}}:function(e){i.progress(e)},r.push(function(n){n.then(e,t).then(i.resolve,i.reject,o)}),s.push(o),i.promise},l=function(e){return y(s,e),e},c=function(e){return e=i(e),u=e.then,c=i,l=w,y(r,e),s=r=n,e},e}function l(e){return e&&typeof e.then=="function"}function c(e,t,n,i,s){return b(2,arguments),r(e,function(e){function g(e){p(e)}function y(e){h(e)}var o,u,a,l,c,h,p,d,v,m;v=e.length>>>0,o=Math.max(0,Math.min(t,v)),a=[],u=v-o+1,l=[],c=f();if(!o)c.resolve(a);else{d=c.progress,p=function(e){l.push(e),--u||(h=p=w,c.reject(l))},h=function(e){a.push(e),--o||(h=p=w,c.resolve(a))};for(m=0;m<v;++m)m in e&&r(e[m],y,g,d)}return c.then(n,i,s)})}function h(e,t,n,r){function i(e){return t?t(e[0]):e[0]}return c(e,1,i,n,r)}function p(e,t,n,r){return b(1,arguments),v(e,E).then(t,n,r)}function d(){return v(arguments,E)}function v(e,t){return r(e,function(e){var n,i,s,o,u,a;s=i=e.length>>>0,n=[],a=f();if(!s)a.resolve(n);else{o=function(i,o){r(i,t).then(function(e){n[o]=e,--s||a.resolve(n)},a.reject)};for(u=0;u<i;u++)u in e?o(e[u],u):--s}return a.promise})}function m(n,i){var s=t.call(arguments,1);return r(n,function(t){var n;return n=t.length,s[0]=function(e,t,s){return r(e,function(e){return r(t,function(t){return i(e,t,s,n)})})},e.apply(t,s)})}function g(e,t,n){var i=arguments.length>2;return r(e,function(e){return e=i?n:e,t.resolve(e),e},function(e){return t.reject(e),a(e)},t.progress)}function y(e,t){var n,r=0;while(n=e[r++])n(t)}function b(e,t){var n,r=t.length;while(r>e){n=t[--r];if(n!=null&&typeof n!="function")throw new Error("arg "+r+" must be a function")}}function w(){}function E(e){return e}var e,t,n;return r.defer=f,r.resolve=i,r.reject=s,r.join=d,r.all=p,r.map=v,r.reduce=m,r.any=h,r.some=c,r.chain=g,r.isPromise=l,o.prototype={always:function(e,t){return this.then(e,e,t)},otherwise:function(e){return this.then(n,e)},yield:function(e){return this.then(function(){return e})},spread:function(e){return this.then(function(t){return p(t,function(t){return e.apply(n,t)})})}},t=[].slice,e=[].reduce||function(e){var t,n,r,i,s;s=0,t=Object(this),i=t.length>>>0,n=arguments;if(n.length<=1)for(;;){if(s in t){r=t[s++];break}if(++s>=i)throw new TypeError}else r=n[1];for(;s<i;++s)s in t&&(r=e(r,t[s],s,t));return r},r})}(typeof define=="function"&&define.amd?define:function(e){typeof exports=="object"?module.exports=e():this.when=e()}),define("when",["when/when"],function(e){return e}),define("troopjs-core/event/emitter",["compose","when"],function(t,n){var r,i=Function,s="memory",o="context",u="callback",a="length",f="head",l="tail",c="next",h="handled",p="handlers";return t(function(){this[p]={}},{on:function(t,n,r){var s=this,d=arguments,v=s[p],m,g,y,b=d[a],w=2;if(r instanceof i){if(t in v){v=v[t],m={},m[u]=d[w++],m[o]=n,y=l in v?v[l][c]=m:v[f]=m;while(w<b)y=y[c]=m={},m[u]=d[w++],m[o]=n;v[l]=y}else{g=y=m={},m[u]=d[w++],m[o]=n;while(w<b)y=y[c]=m={},m[u]=d[w++],m[o]=n;v=v[t]={},v[f]=g,v[l]=y,v[h]=0}return s}throw new Error("no callback(s) supplied")},off:function(t,n,i){var s=this,h=arguments,d=s[p],v,m,g,y=h[a],b=2;if(t in d){d=d[t];if(f in d){m=d[f];while(b<y){i=h[b++],v=g=m;do{if(v[u]===i&&(n===r||v[o]===n)){if(v===m){m=g=v[c];continue}g[c]=v[c];continue}g=v}while((v=v[c])!==r)}return m&&g?(d[f]=m,d[l]=g):(delete d[f],delete d[l]),s}return s}return s},reemit:function(t,i,l){var d=this,v=arguments,m=d[p],g,y,b,w=v[a],E=2;if(t in m){m=m[t];if(s in m){if(f in m){b=m[f],y=m[h]+1;while(E<w){l=v[E++],g=b;do{if(g[u]===l&&(i===r||g[o]===i))continue;g[h]=y}while((g=g[c])!==r)}return d.emit.apply(d,m[s])}return n.resolve(m[s])}}return n.resolve()},emit:function(t){function v(e){i=e||i;while(l[h]===d)if(!(l=l[c]))return a[s]=i,n.resolve(i);return l[h]=d,n(l[u].apply(l[o],i),v)}var r=this,i=arguments,a=r[p],l,d;if(t in a){a=a[t],d=++a[h];if(f in a){l=a[f];try{return v(i)}catch(m){return n.reject(m)}}}else a[t]=a={},a[h]=0;return a[s]=i,n.resolve(i)}})}),define("troopjs-core/component/base",["../event/emitter"],function(t){var n=0,r="instanceCount",i=t.extend(function(){this[r]=n++},{instanceCount:n,displayName:"core/component"});return i.prototype.toString=function(){var e=this;return e.displayName+"@"+e[r]},i}),define("troopjs-core/pubsub/hub",["compose","../component/base"],function(t,n){var r=t.from;return t.create(n,{displayName:"core/pubsub/hub",subscribe:r(n,"on"),unsubscribe:r(n,"off"),publish:r(n,"emit"),republish:r(n,"reemit")})}),define("troopjs-core/component/gadget",["./base","when","../pubsub/hub"],function(t,n,r){var i,s=null,o=Function,u=Array.prototype,a=u.slice,f=u.splice,l=u.unshift,c=/^hub(?::(\w+))?\/(.+)/,h=/^sig(?::(\w+))?\/(.+)/,p=r.publish,d=r.republish,v=r.subscribe,m=r.unsubscribe,g="features",y="signals",b="subscriptions";return t.extend(function(){var t=this,n=t.constructor._getBases(!0),r,i,u,a=n.length,f,l,c=t[y]={},p,d,v;while(r=n[--a])e:for(v in r){u=r[v];if(!(u instanceof o))continue;if((d=h.exec(v))===s)continue;p=d[2];if(p in c){i=c[p],f=l=i.length;while(f--)if(u===i[f])continue e;i[l]=u}else c[p]=[u]}},{displayName:"core/component/gadget","sig/initialize":function(){var t=this,n,i=t[b]=[],u,a,f,l;for(u in t){a=t[u];if(!(a instanceof o))continue;if((f=c.exec(u))===s)continue;l=f[2],v.call(r,l,t,a),i[i.length]=n=[l,t,a],n[g]=f[1],t[u]=s}},"sig/start":function(){var t=this,s=t[b],o,u=s.length,a=[];while((o=s[--u])!==i){if(o[g]!=="memory")continue;a.push(d.call(r,o[0],o[1],o[2]))}return n.map(a,function(e){return e})},"sig/finalize":function(){var t=this,n=t[b],s;while((s=n.shift())!==i)m.call(r,s[0],s[1],s[2])},signal:function(t){function f(e){return i=e||i,o>u?n(s[u++].apply(r,i),f):n.resolve(i)}var r=this,i=a.call(arguments),s=r[y][t],o=s?s.length:0,u=0;try{return f()}catch(l){return n.reject(l)}},publish:function(){return p.apply(r,arguments)},subscribe:function(){var t=this,n=arguments;return f.call(n,1,0,t),v.apply(r,n),t},unsubscribe:function(){var t=this,n=arguments;return f.call(n,1,0,t),m.apply(r,n),t},start:function(){var t=this,n=t.signal,r=arguments;return l.call(r,"initialize"),n.apply(t,r).then(function(){return r[0]="start",n.apply(t,r)})},stop:function(){var t=this,n=t.signal,r=arguments;return l.call(r,"stop"),n.apply(t,r).then(function(){return r[0]="finalize",n.apply(t,r)})}})}),define("troopjs-core/component/service",["./gadget"],function(t){return t.extend({displayName:"core/component/service"})}),define("troopjs-data/query/component",["troopjs-core/component/base"],function(t){var n,r=!0,i=!1,s=Object,o=Array,u="constructor",a="length",f="op",l="!",c=".",h=",",p="|",d="text",v="raw",m="resolved",g="id",y="expires",b="collapsed",w="_ast",E="_query",S=/("|')(.*?)\1/,x="$2",T=/!(.*[!,|.\s]+.*)/,N="!'$1'";return t.extend(function(t){var r=this;t!==n&&(r[E]=t)},{displayName:"data/query/component",parse:function(t){var r=this;delete r[w],t=r[E]=t||r[E]||"";var i,s,o,u,m,g,y=[];for(i=u=0,s=t[a];i<s;i++){o=t.charAt(i);switch(o){case'"':case"'":m=m===o?n:o;break;case l:if(m!==n)break;g={},g[f]=o;break;case c:case h:if(m!==n)break;g!==n&&(g[v]=(g[d]=t.substring(u,i)).replace(S,x),y.push(g)),g={},g[f]=o,u=i+1;break;case p:case" ":case" ":case"\r":case"\n":if(m!==n)break;g!==n&&(g[v]=(g[d]=t.substring(u,i)).replace(S,x),y.push(g)),g=n,u=i+1}}return g!==n&&(g[v]=(g[d]=t.substring(u,s)).replace(S,x),y.push(g)),r[w]=y,r},reduce:function(t){var p=this,E=0|(new Date).getTime()/1e3;w in p||p.parse();var S=p[w],x=[],C,k,L,A,O,M,_,D,P=i;for(C=0,A=S[a];C<A;C++){O=S[C];switch(O[f]){case l:_=O,M=O[v],M in t?(D=t[M],O[m]=D[b]!==r&&!(y in D)||D[y]>E):(D=n,O[m]=i);break;case c:M=O[v];if(D&&M in D){D=D[M],L=D[u];if(L===o){O[m]=r;for(k=D[a];k-->0;){L=D[k];if(L[u]===s&&g in L&&(L[b]===r||y in L)&&!(L[y]>E)){O[m]=i;break}continue}}else L===s&&g in D?(O[f]=l,O[d]=(O[v]=D[g]).replace(T,N),O[m]=D[b]!==r&&!(y in D)||D[y]>E):O[m]=r}else D=n,O[m]=i;break;case h:M=_[v],D=t[M],O[f]=l,O[d]=_[d],O[v]=M,O[m]=_[m]}}while(A-->0){O=S[A];switch(O[f]){case l:(P||O[m]!==r)&&x.unshift(O),P=i;break;case c:x.unshift(O),P=r}}return p[w]=x,p},ast:function(){var t=this;return w in t||t.parse(),t[w]},rewrite:function(){var t=this;w in t||t.parse();var n=t[w],r="",i,s,o;for(s=0,i=n[a];s<i;s++){o=n[s];switch(o[f]){case l:r+=s===0?o[d]:p+o[d];break;case c:r+=c+o[d]}}return r}})}),define("troopjs-core/pubsub/topic",["../component/base","troopjs-utils/unique"],function(t,n){function s(e,t){return e.publisherInstanceCount===t.publisherInstanceCount}var r=Object.prototype.toString,i=r.call(Array.prototype),o=t.extend(function(t,n,r){var i=this;i.topic=t,i.publisher=n,i.parent=r,i.publisherInstanceCount=n.instanceCount},{displayName:"core/pubsub/topic",trace:function(){var t=this,o=t.constructor,u,a,f="",l,c,h;while(t){if(r.call(t)===i){c=n.call(t,s);for(l=0,h=c.length;l<h;l++)a=c[l],c[l]=a.constructor===o?a.trace():a.topic;f+=c.join(",");break}u=t.parent,f+=u?t.publisher+":":t.publisher,t=u}return f}});return o.prototype.toString=function(){return this.topic},o}),define("troopjs-data/query/service",["module","troopjs-core/component/service","./component","troopjs-core/pubsub/topic","when","troopjs-utils/merge"],function(t,n,r,i,s,o){var u=Array.prototype,a=u.slice,f=u.concat,l=u.push,c="length",h="batches",p="interval",d="cache",v="topic",m="queries",g="resolved",y="raw",b="id",w="q",E=t.config(),S=n.extend(function(e){var t=this;t[h]=[],t[d]=e},{displayName:"data/query/service","sig/start":function(){var t=this,n=t[d];t[p]=p in t?t[p]:setInterval(function(){function s(){var e=[],n=[],s,u;for(u=r[c];u--;)s=r[u],l.call(n,s[v]),l.apply(e,s[w]);return t.publish(i("ajax",t,n),o.call({data:{q:e.join("|")}},E))}function u(e){var t,i,s,o,u;n.put(e);for(o=r[c];o--;){t=r[o],i=t[m],s=t[b];for(u=i[c];u--;)u in s&&(i[u]=n[s[u]]);t.resolve(i)}}function a(){var e,t;for(t=r[c];t--;)e=r[t],e.reject(e[m])}var r=t[h];if(r[c]===0)return;return t[h]=[],s().then(u,a)},200)},"sig/stop":function(){var t=this;p in t&&(clearInterval(t[p]),delete t[p])},"hub/query":function(t){var n=this,i=n[h],o=n[d],p=[],E=[],S,x,T,N,C,k,L=s.defer();try{C=f.apply(u,a.call(arguments,1));for(x=0,N=C[c];x<N;x++){k=r(C[x]),S=k.ast(),S[c]>0&&(E[x]=S[0][y]),S=k.reduce(o).ast();for(T=S[c];T-->0;)if(!S[T][g]){l.call(p,k.rewrite());break}}if(p[c]===0){for(x=0;x<N;x++)x in E&&(C[x]=o[E[x]]);L.resolve(C)}else L[v]=t,L[m]=C,L[b]=E,L[w]=p,i.push(L)}catch(A){L.reject(A)}return L.promise}});return S.config=function(t){return o.call(E,t)},S}),define("troopjs-data/cache/component",["troopjs-core/component/gadget"],function(t){function E(e,t,u){var a=this,l,S,x,T,N,C,k,L,A,O,M=a[f],_,D;e:{if(!(m in e)){l=e;break e}S=e[m];if(S in a){l=a[S];break e}l=a[S]=e,l[b]=u}if(t===o)for(x=0,T=e[v];x<T;x++)D=e[x],t=D===i||D===n?n:D[d],l[x]=t===s||t===o&&D[v]!==0?E.call(a,D,t,u):D;else if(t===s)for(_ in e){if(_===m||_===w&&l[w]===r)continue;D=e[_],t=D===i||D===n?n:D[d],l[_]=t===s||t===o&&D[v]!==0?E.call(a,D,t,u):D}t:{if(S===i)break t;N=0|u+(l[g]>>>0);n:{if(!(y in l))break n;C=l[y];if(C===N)break t;C in M&&delete M[C][S]}r:{l[y]=N;if(N in M){M[N][S]=l;break r}(O=M[N]={})[p]=N,O[S]=l;if(M[c]===n){M[c]=O;break r}for(L=k=M[c];(A=L[h])!==n&&A[p]<N;L=A);if(L===k&&L[p]>N){O[h]=L,M[c]=O;break r}O[h]=L[h],L[h]=O}}return l}var n,r=!1,i=null,s=Object,o=Array,u=1e3,a="interval",f="generations",l="age",c="head",h="next",p="expires",d="constructor",v="length",m="id",g="maxAge",y="expires",b="indexed",w="collapsed";return t.extend(function(e){var t=this;t[l]=e||60*u,t[f]={}},{displayName:"data/cache/component","sig/start":function(){var t=this,r=t[f];t[a]=a in t?t[a]:setInterval(function(){var i=0|(new Date).getTime()/u,s,o;o=r[c];if(o===n)return;do{if(o[p]>i)break;for(s in o){if(s===p||s===h||s===f)continue;delete t[s]}delete r[o[p]]}while(o=o[h]);r[c]=o},t[l])},"sig/stop":function(){var t=this;a in t&&(clearInterval(t[a]),delete t[a])},"sig/finalize":function(){var t=this,n;for(n in t){if(!(t[n][d]===s&&m in t[n]))continue;delete t[n]}},put:function(t){var r=this,a=t===i||t===n?n:t[d];return a===s||a===o&&t[v]!==0?E.call(r,t,a,0|(new Date).getTime()/u):t}})}),define("troopjs-browser/ajax/service",["troopjs-core/component/service","jquery","troopjs-utils/merge"],function(t,n,r){var i="trace";return t.extend({displayName:"browser/ajax/service","hub/ajax":function(t,s){return n.ajax(r.call({headers:{"x-request-id":(new Date).getTime(),"x-components":t[i]instanceof Function?t[i]():t}},s))}})}),define("troopjs-browser/component/widget",["troopjs-core/component/gadget","jquery","when","troopjs-jquery/weave","troopjs-jquery/action"],function(t,n,r){function S(e,t,n){return function(){return f.call(arguments,e),n.apply(t,arguments)}}function x(e){function t(){var t=this,n=arguments,r=a.call(n);return e.call(t[m],r instanceof o?r.apply(t,n):r),t.weave().then(function(n){return t.trigger(v,n),n})}return t}var i,s=null,o=Function,u=Array.prototype,a=u.shift,f=u.unshift,l=n.fn.trigger,c=n.fn.one,h=n.fn.bind,p=n.fn.unbind,d=/^dom(?::(\w+))?\/([^\.]+(?:\.(.+))?)/,v="widget/refresh",m="$element",g="$proxies",y="one",b="features",w="[data-weave]",E="[data-woven]";return t.extend(function(t,n){var r=this;r[m]=t,n&&(r.displayName=n)},{displayName:"browser/component/widget","sig/initialize":function(){var t=this,n=t[m],r=t[g]=[],i,u,a,f,l;for(u in t){a=t[u];if(!(a instanceof o))continue;f=d.exec(u),f!==s&&(l=f[2],a=S(l,t,a),(f[2]===y?c:h).call(n,l,t,a),r[r.length]=i=[l,a],i[b]=f[1],t[u]=s)}},"sig/finalize":function(){var t=this,n=t[m],r=t[g],s;while((s=r.shift())!==i)n.unbind(s[0],s[1]);delete t[m]},weave:function(){return this[m].find(w).weave()},unweave:function(){return this[m].find(E).addBack().unweave()},one:function(){var t=this;return c.apply(t[m],arguments),t},bind:function(){var t=this;return h.apply(t[m],arguments),t},unbind:function(){var t=this;return p.apply(t[m],arguments),t},trigger:function(){var t=this;return l.apply(t[m],arguments),t},before:x(n.fn.before),after:x(n.fn.after),html:x(n.fn.html),text:x(n.fn.text),append:x(n.fn.append),prepend:x(n.fn.prepend),empty:function(){var t=this,n=r.defer(),i=t[m],s=i.contents().detach();return t.trigger(v,t),setTimeout(function(){var t=s.get();s.remove(),n.resolve(t)},0),n.promise}})}),define("troopjs-browser/dimensions/widget",["../component/widget","troopjs-jquery/dimensions","troopjs-jquery/resize"],function(t){function r(e,t,n){var r=e.data;r.publish(r.displayName,t,n,e)}var n="dimensions";return t.extend(function(t,r,i){this[n]=i},{displayName:"browser/dimensions/widget","sig/initialize":function(t){var i=this;i.bind(n+"."+i[n],i,r)},"sig/start":function(){this.trigger("resize."+n)},"sig/finalize":function(){var t=this;t.unbind(n+"."+t[n],r)}})}),define("troopjs-browser/store/base",["compose","troopjs-core/component/gadget","when"],function(t,n,r){var i="storage";return n.extend({storage:t.required,set:function(t,n){return r(this[i].setItem(t,JSON.stringify(n)))},get:function(t){return r(JSON.parse(this[i].getItem(t)))},remove:function(t){return r(this[i].removeItem(t))},clear:function(){return r(this[i].clear())}})}),define("troopjs-browser/store/session",["compose","./base"],function(t,n){return t.create(n,{displayName:"browser/store/session",storage:window.sessionStorage})}),define("troopjs-browser/store/local",["compose","./base"],function(t,n){return t.create(n,{displayName:"browser/store/local",storage:window.localStorage})}),define("troopjs-browser/route/widget",["../component/widget","troopjs-utils/uri","troopjs-jquery/hashchange"],function(t,n){function o(e){var t=e.data,r=n(e.target.location.hash.replace(s,"")),o=r.toString();o!==t[i]&&(t[i]=o,t.publish(t.displayName,r,e))}var r="hashchange",i="route",s=/^#/;return t.extend({"sig/initialize":function(){var t=this;t.bind(r,t,o)},"sig/start":function(){this.trigger(r)},"sig/finalize":function(){this.unbind(r,o)}})}),define("troopjs-browser/application/widget",["module","../component/widget","when"],function(t,n,r){function o(e){function a(t){return n=t||n,o>u?r(s[u++].signal(e),a):r.resolve(n)}var t=this,n=arguments,s=t[i],o=s?s.length:0,u=0;return a()}var i="children",s=Array.prototype.slice;return n.extend(function(t,n,r){this[i]=r},{displayName:"browser/application/widget","sig/initialize":o,"sig/start":function(){var t=this,n=t.weave,r=arguments;return o.apply(t,r).then(function(){return n.apply(t,s.call(r,1))})},"sig/stop":function(){var t=this,n=t.unweave,r=arguments;return n.apply(t,s.call(r,1)).then(function(){return o.apply(t,r)})},"sig/finalize":o})}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* TroopJS Bundle - 1.0.7-42-g27b1122-dirty | |
* http://troopjs.com/ | |
* Copyright (c) 2013 Mikael Karon <[email protected]> | |
* Licensed MIT | |
*/ | |
define("troopjs-requirejs/template",[],function(){function f(e){function l(e,n,r){return t[f]=n?'" +'+r+'+ "':'";'+r+'o += "',"<%"+String(f++)+"%>"}function c(e,n){return t[n]}function h(e,t){return a[t]||t}var t=[],f=0;return('function template(data) { var o = "'+e.replace(n,"").replace(r,l).replace(s,h).replace(i,c)+'"; return o; }').replace(o,u)}var t={node:function(){var e=require.nodeRequire("fs");return function(n,r){var i=e.readFileSync(n,"utf8");i.indexOf("")===0&&(i=i.substring(1)),r(i)}},browser:function(){var e=["Msxml2.XMLHTTP","Microsoft.XMLHTTP","Msxml2.XMLHTTP.4.0"],t,n,r;if(typeof XMLHttpRequest!="undefined")n=XMLHttpRequest;else{for(r=0;r<3;r++){t=e[r];try{new ActiveXObject(t),n=function(){return new ActiveXObject(t)};break}catch(i){}}if(!n)throw new Error("XHR: XMLHttpRequest not available")}return function(t,r){var i=new n;i.open("GET",t,!0),i.onreadystatechange=function(e){i.readyState===4&&r(i.responseText)},i.send(null)}},rhino:function(){var e="utf-8",t=java.lang.System.getProperty("line.separator");return function(r,i){var s=new java.io.File(r),o=new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(s),e)),u=new java.lang.StringBuffer,a,f="";try{a=o.readLine(),a&&a.length()&&a.charAt(0)===65279&&(a=a.substring(1)),u.append(a);while((a=o.readLine())!==null)u.append(t),u.append(a);f=String(u.toString())}finally{o.close()}i(f)}},borked:function(){return function(){throw new Error("Environment unsupported.")}}},n=/^[\n\t\r]+|[\n\t\r]+$/g,r=/<%(=)?([\S\s]*?)%>/g,i=/<%(\d+)%>/gm,s=/(["\n\t\r])/gm,o=/o \+= "";| \+ ""/gm,u="",a={'"':'\\"',"\n":"\\n"," ":"\\t","\r":"\\r"},l={},c=t[typeof process!="undefined"&&process.versions&&!!process.versions.node?"node":typeof window!="undefined"&&window.navigator&&window.document||typeof importScripts!="undefined"?"browser":typeof Packages!="undefined"?"rhino":"borked"]();return{load:function(e,t,n,r){var i=t.toUrl(e);c(i,function(s){try{s="define(function() { return "+f(s,e,i,r.template)+"; })"}catch(o){throw o.message="In "+i+", "+o.message,o}r.isBuild?l[e]=s:s+="\n//@ sourceURL='"+i+"'",n.fromText(e,s),t([e],function(e){n(e)})})},write:function(e,t,n){l.hasOwnProperty(t)&&n.asModule(e+"!"+t,l[t])}}}),define("troopjs-jquery/hashchange",["jquery"],function(t){function a(e){var t=s.exec(e.location.href);return t&&t[1]?decodeURIComponent(t[1]):""}function f(e){var t=this,n;t.element=n=e.createElement("iframe"),n.src="about:blank",n.style.display="none"}var n="interval",r="hashchange",i="on"+r,s=/#(.*)$/,o=/\?/,u=0;f.prototype={getElement:function(){return this.element},getHash:function(){return this.element.contentWindow.frameHash},update:function(e){var t=this,n=t.element.contentWindow.document;if(t.getHash()===e)return;n.open(),n.write("<html><head><title>' + document.title + '</title><script type='text/javascript'>var frameHash='"+e+"';</script></head><body> </body></html>"),n.close()}},t.event.special[r]={setup:function(s,l,c){var h=this;if(i in h)return!1;if(!t.isWindow(h))throw new Error("Unable to bind 'hashchange' to a non-window object");var p=t(h),d=a(h),v=h.location;p.data(n,h.setInterval(u?function(){var t=h.document,n=v.protocol==="file:",i=new f(t);return t.body.appendChild(i.getElement()),i.update(d),function(){var t=d,s,u=a(h),f=i.getHash();f!==d&&f!==u?(s=decodeURIComponent(f),d!==s&&(d=s,i.update(d),p.trigger(r,[s,t])),v.hash="#"+encodeURI(n?f.replace(o,"%3F"):f)):u!==d&&(s=decodeURIComponent(u),d!==s&&(d=s,p.trigger(r,[s,t])))}}():function(){var t=d,n,i=a(h);i!==d&&(n=decodeURIComponent(i),d!==n&&(d=n,p.trigger(r,[n,t])))},25))},teardown:function(r){var s=this;if(i in s)return!1;s.clearInterval(t.data(s,n))}}}),define("troopjs-utils/getargs",[],function(){var t=Array.prototype.push,n=String.prototype.substring,r=/^(?:false|true)$/i,i=/^true$/i,s=/^\d+$/;return function(){var o=this,u=[],a,f,l,c,h,p,d=!1;for(f=l=c=0,a=o.length;c<a;c++){h=o.charAt(c);switch(h){case'"':case"'":d===h?(d=!1,t.call(u,n.call(o,f,l))):d=h,f=l=c+1;break;case",":if(d){l=c+1;break}f!==l&&(p=n.call(o,f,l),r.test(p)?p=i.test(p):s.test(p)&&(p=+p),t.call(u,p)),f=l=c+1;break;case" ":case" ":if(d){l=c+1;break}f===l&&(f=l=c+1);break;default:l=c+1}}return f!==l&&(p=n.call(o,f,l),r.test(p)?p=i.test(p):s.test(p)&&(p=+p),t.call(u,p)),u}}),define("troopjs-jquery/action",["jquery","troopjs-utils/getargs"],function(t,n){function c(e,t){return e?e+"."+u:s}function h(e){var n=t(this),r=o.call(arguments,1),s=a in e?e[a].type:u,f=e[u];e.type=u+"/"+f+"."+s,n.trigger(e,r),e.result!==i&&(e.type=u+"/"+f+"!",n.trigger(e,r),e.result!==i&&(e.type=u+"."+s,n.trigger(e,r)))}function p(e){var i=t(e.target).closest("[data-action]");if(i.length===0)return;var o=i.data(),a=f.exec(o[u]);if(a===s)return;var c=a[1],h=a[2],p=a[3];if(h!==r&&!RegExp(h.split(l).join("|")).test(e.type))return;var d=p!==r?n.call(p):[];t.each(d,function(t,n){n in o&&(d[t]=o[n])}),i.trigger(t.Event(e,{type:u+"!",action:c}),d),e.stopPropagation()}var r,i=!1,s=null,o=Array.prototype.slice,u="action",a="originalEvent",f=/^([\w\d\s_\-\/]+)(?:\.([\w\.]+))?(?:\((.*)\))?$/,l=/\.+/;t.event.special[u]={setup:function(n,r,i){t(this).bind(u,n,h)},add:function(n){var r=t.map(n.namespace.split(l),c);r.length!==0&&t(this).bind(r.join(" "),p)},remove:function(n){var r=t.map(n.namespace.split(l),c);r.length!==0&&t(this).unbind(r.join(" "),p)},teardown:function(n){t(this).unbind(u,h)}},t.fn[u]=function(n){return t(this).trigger({type:u+"!",action:n},o.call(arguments,1))}}),define("troopjs-jquery/destroy",["jquery"],function(t){t.event.special.destroy={remove:function(n){var r=this;n.handler.call(r,t.Event({type:n.type,data:n.data,namespace:n.namespace,target:r}))}}}),define("troopjs-jquery/weave",["require","jquery","troopjs-utils/getargs","./destroy"],function(t,n,r){function T(){n(this).unweave()}var i,s=null,o=Array,u=o.prototype,a=u.join,f=u.push,l=n.when,c=n.Deferred,h="weave",p="unweave",d="woven",v="weaving",m="pending",g="destroy",y="data-",b=y+h,w=y+d,E=y+v,S="["+b+"]",x="["+E+"],["+w+"]";n.expr[":"][h]=n.expr.createPseudo?n.expr.createPseudo(function(e){return e!==i&&(e=RegExp(n.map(r.call(e),function(e){return"^"+e+"$"}).join("|"),"m")),function(t){var r=n(t).attr(b);return r===i?!1:e===i?!0:e.test(r.split(/[\s,]+/).join("\n"))}}):function(e,t,s){var o=n(e).attr(b);return o===i?!1:s===i?!0:RegExp(n.map(r.call(s[3]),function(e){return"^"+e+"$"}).join("|"),"m").test(o.split(/[\s,]+/).join("\n"))},n.expr[":"][d]=n.expr.createPseudo?n.expr.createPseudo(function(e){return e!==i&&(e=RegExp(n.map(r.call(e),function(e){return"^"+e+"@\\d+"}).join("|"),"m")),function(t){var r=n(t).attr(w);return r===i?!1:e===i?!0:e.test(r.split(/[\s,]+/).join("\n"))}}):function(e,t,s){var o=n(e).attr(w);return o===i?!1:s===i?!0:RegExp(n.map(r.call(s[3]),function(e){return"^"+e+"@\\d+"}).join("|"),"m").test(o.split(/[\s,]+/).join("\n"))},n.fn[h]=function(){var o=[],u=0,p=n(this),v=arguments;return p.filter(S).each(function(p,y){c(function(p){var S=n(y),x=S.data(),N=x[h]=S.attr(b)||"",C=x[d]||(x[d]=[]),k=x[m]||(x[m]=[]);p.done(function(){S.removeAttr(E).attr(w,a.call(arguments," "))}),l.apply(n,k).then(function(){var a=/[\s,]*([\w_\-\/\.]+)(?:\(([^\)]+)\))?/g,h=u,d=0,m;f.call(k,p),S.removeAttr(b).attr(E,N).bind(g,T);while((m=a.exec(N))!==s)c(function(n){var s=d++,a,f,l,c;o[u++]=n,n.then(function(t){C[s]=t},p.reject,p.notify);var h=m[1],g=[S,h];for(a=0,l=v.length,f=g.length;a<l;a++,f++)g[f]=v[a];var y=m[2];if(y!==i){y=r.call(y);for(a=0,l=y.length,f=g.length;a<l;a++,f++)c=y[a],g[f]=c in x?x[c]:c}t([h],function(t){var r=t.apply(t,g);r.start().then(function(){n.resolve(r)},n.reject,n.notify)})});l.apply(n,o.slice(h,u)).then(p.resolve,p.reject,p.notify)},p.reject,p.notify)})}),c(function(t){l.apply(n,o).then(function(){t.resolve(arguments)},t.reject,t.progress)}).promise()},n.fn[p]=function(){var t=[],r=0,s=n(this);return s.filter(x).each(function(s,o){c(function(s){var u=n(o),a=u.data(),p=a[m]||(a[m]=[]),v=a[d]||[];s.done(function(){u.attr(b,a[h]).unbind(g,T),delete a[h]}),l.apply(n,p).done(function(){var o=r,h;f.call(p,s),delete a[d],u.removeAttr(w);while((h=v.shift())!==i)c(function(n){t[r++]=h.stop().then(function(){n.resolve(h)},n.reject,n.notify)});l.apply(n,t.slice(o,r)).then(s.resolve,s.reject,s.notify)})})}),c(function(r){l.apply(n,t).then(function(){r.resolve(arguments)},r.reject,r.progress)}).promise()},n.fn[d]=function(){var t=[],r=arguments.length>0?RegExp(n.map(arguments,function(e){return"^"+e+"$"}).join("|"),"m"):i;return n(this).each(function(s,o){if(!n.hasData(o))return;f.apply(t,r===i?n.data(o,d):n.map(n.data(o,d),function(e){return r.test(e.displayName)?e:i}))}),t}}),define("troopjs-jquery/dimensions",["jquery"],function(t){function f(e,t){return t-e}function l(e){var n=this,i=t(n),f=i.width(),l=i.height();t.each(t.data(n,r),function(t,n){var c=n[s],h=n[o],p,d,v;v=c.length,p=c[v-1];while(c[--v]<f)p=c[v];v=h.length,d=h[v-1];while(h[--v]<l)d=h[v];if(p!==n[u]||d!==n[a])n[u]=p,n[a]=d,i.trigger(r+"."+t,[p,d])})}var n=null,r="dimensions",i="resize."+r,s="w",o="h",u="_"+s,a="_"+o;t.event.special[r]={setup:function(n,s,o){t(this).bind(i,l).data(r,{})},add:function(i){var u=this,a=i.namespace,l={},c=l[s]=[],h=l[o]=[],p=/(w|h)(\d+)/g,d;while((d=p.exec(a))!==n)l[d[1]].push(parseInt(d[2],10));c.sort(f),h.sort(f),t.data(u,r)[a]=l},remove:function(n){delete t.data(this,r)[n.namespace]},teardown:function(n){t(this).removeData(r).unbind(i,l)}}}),define("troopjs-jquery/resize",["jquery"],function(t){function a(e,n){var o=t.data(n),u=t(n),a=u.width(),f=u.height();(a!==o[i]||f!==o[s])&&u.trigger(r,[o[i]=a,o[s]=f])}function f(){o.each(a)}var n=null,r="resize",i="w",s="h",o=t([]),u=n;t.event.special[r]={setup:function(n,a,l){var c=this;if(t.isWindow(c))return!1;var h=t.data(c,r,{}),p=t(c);h[i]=p.width(),h[s]=p.height(),o=o.add(c),o.length===1&&(u=setInterval(f,100))},teardown:function(i){var s=this;if(t.isWindow(s))return!1;t.removeData(s,r),o=o.not(s),o.length===0&&u!==n&&clearInterval(u)}}}),define("troopjs-utils/merge",[],function(){var t=Array,n=Object;return function r(e){var i=this,s=null,o,u,a,f;for(o=0,u=arguments.length;o<u;o++){e=arguments[o];for(s in e)a=e[s],f=a.constructor,s in i?f===t?i[s]=i[s].concat(a):f===n?r.call(i[s],a):i[s]=a:i[s]=a}return i}}),define("troopjs-utils/tr",[],function(){var t=typeof Number();return function(n){var r=this,i=[],s,o=r.length,u;if(typeof o===t&&o===0||o>0&&0 in r&&o-1 in r)for(s=0;s<o;s++)i.push(n.call(r,r[s],s));else if(r)for(u in r)i.push(n.call(r,r[u],u));return i}}),function(e){e("compose/compose",[],function(){function e(){}function n(e){if(!e)throw new Error("Compose arguments must be functions or objects");return e}function r(e,t,r){var s,o=t.length;for(;r<o;r++){var u=t[r];if(typeof u=="function"){var a=u.prototype;for(var l in a){s=a[l];var c=a.hasOwnProperty(l);if(typeof s=="function"&&l in e&&s!==e[l]){var p=e[l];s==f?s=p:c||(i(s,l,h([].slice.call(t,0,r),!0))?s=p:i(p,l,h([u],!0))||console.error("Conflicted method "+l+", final composer must explicitly override with correct method."))}s&&s.install&&c&&!i(p,l,h([u],!0))?s.install.call(e,l):e[l]=s}}else for(var l in n(u)){var s=u[l];if(typeof s=="function"){if(s.install){s.install.call(e,l);continue}if(l in e&&s==f)continue}e[l]=s}}return e}function i(e,t,n){for(var r=0;r<n.length;r++){var i=n[r];if(i[t]==e)return!0}}function s(e,t){function n(){if(t)return t.apply(this,arguments);throw new Error("Decorator not applied")}return n.install=e,n}function o(e){return function(t){return s(function n(r){var i=this[r];(t=this[r]=i?e(this,i,t):t).install=n},t)}}function f(){throw new Error("This method is required and no implementation has been provided")}function l(){var e=[this];return e.push.apply(e,arguments),c.apply(0,e)}function c(i){function u(){var t;this instanceof u?t=this:(e.prototype=o,t=new e);for(var n=0;n<f;n++){var r=a[n],i=r.apply(t,arguments);if(typeof i=="object")if(i instanceof u)t=i;else for(var s in i)i.hasOwnProperty(s)&&(t[s]=i[s])}return t}var s=arguments,o=s.length<2&&typeof s[0]!="function"?s[0]:r(t(n(i)),s,1);u._getBases=function(e){return e?p:a};var a=h(s),f=a.length;typeof s[s.length-1]=="object"&&(s[s.length-1]=o);var p=h(s,!0);return u.extend=l,c.secure||(o.constructor=u),u.prototype=o,u}function h(e,t){function r(e,i){e:for(var s=0;s<e.length;s++){var o=e[s],u=t&&typeof o=="function"?o.prototype:o;if(t||typeof o=="function"){var a=i&&o._getBases;if(a)r(a(t));else{for(var f=0;f<n.length;f++)if(u==n[f])continue e;n.push(u)}}}}var n=[];return r(e,!0),n}var t=Object.create?function(e){return Object.create(typeof e=="function"?e.prototype:e||Object.prototype)}:function(t){e.prototype=typeof t=="function"?t.prototype:t;var n=new e;return e.prototype=null,n};c._setMixin=function(e){r=e},c.Decorator=s,c.around=o(function(e,t,n){return n.call(e,t)}),c.before=o(function(e,t,n){return function(){var e=n.apply(this,arguments);if(e!==u)return t.apply(this,e||arguments)}});var u=c.stop={},a;return c.after=o(function(e,t,n){return function(){var e=t.apply(this,arguments),r=n.apply(this,arguments);return r===a?e:r}}),c.from=function(e,t){return t?(typeof e=="function"?e.prototype:e)[t]:s(function(n){if(!(this[n]=typeof e=="string"?this[e]:(typeof e=="function"?e.prototype:e)[t||n]))throw new Error("Source method "+t+" was not available to be renamed to "+n)})},c.create=function(e){var n=r(t(e),arguments,1),i=arguments.length;for(var s=0;s<i;s++){var o=arguments[s];typeof o=="function"&&(n=o.call(n)||n)}return n},c.required=f,c.apply=function(e,t){return e?r(e,t,0):l.apply.call(c,0,t)},c.call=function(e){return r(e,arguments,1)},c})}(typeof define!="undefined"?define:function(e,t){typeof module!="undefined"?module.exports=t():Compose=t()}),define("compose",["compose/compose"],function(e){return e}),define("troopjs-utils/uri",["compose"],function(t){function b(e){var t={},r,i=n,s,o=/(?:&|^)([^&=]*)=?([^&]*)/g;t.toString=b.toString;if(u.call(e)===a)for(i in e)t[i]=e[i];else while((r=o.exec(e))!==n)i=r[1],i in t?(s=t[i],u.call(s)===f?s[s.length]=r[2]:t[i]=[s,r[2]]):t[i]=r[2];return t}function w(e){var t=[];return t.toString=w.toString,s.apply(t,u.call(e)===f?e:o.call(e,"/")),t}var n=null,r=Array.prototype,i=Object.prototype,s=r.push,o=String.prototype.split,u=i.toString,a=u.call(i),f=u.call(r),l=u.call(Function.prototype),c=/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?(?:([^?#]*)(?:\?([^#]*))?(?:#(.*))?)/,h="protocol",p="authority",d="path",v="query",m="anchor",g=["source",h,p,"userInfo","user","password","host","port",d,v,m],y=t.secure;t.secure=!0,b.toString=function S(){var e=this,t,n,r,i=[],s=0,o;for(t in e){if(u.call(e[t])===l)continue;i[s++]=t}i.sort();while(s--){t=i[s],n=e[t];if(u.call(n)===f){r=n.slice(0),r.sort(),o=r.length;while(o--)n=r[o],r[o]=n===""?t:t+"="+n;i[s]=r.join("&")}else i[s]=n===""?t:t+"="+n}return i.join("&")},w.toString=function(){return this.join("/")};var E=t(function(t){var r=this,i,s,o;if((s=c.exec(t))!==n){o=s.length;while(o--)i=s[o],i&&(r[g[o]]=i)}v in r&&(r[v]=b(r[v])),d in r&&(r[d]=w(r[d]))});return E.prototype.toString=function(){var e=this,t=[h,"://",p,d,"?",v,"#",m],n,r;h in e||(t[0]=t[1]=""),p in e||(t[2]=""),d in e||(t[3]=""),v in e||(t[4]=t[5]=""),m in e||(t[6]=t[7]=""),n=t.length;while(n--)r=t[n],r in e&&(t[n]=e[r]);return t.join("")},t.secure=y,E.Path=w,E.Query=b,E}),define("troopjs-utils/unique",[],function(){return function(t){var n=this,r=n.length,i=[],s,o,u,a;e:for(o=u=a=0;o<r;o++,u=0){s=n[o];while(u<a)if(t.call(n,s,i[u++])===!0)continue e;i[a++]=s}return i}}),function(e){e("when/when",[],function(){function r(e,t,n,r){return i(e).then(t,n,r)}function i(e){var t,n;return e instanceof o?t=e:l(e)?(n=f(),e.then(function(e){n.resolve(e)},function(e){n.reject(e)},function(e){n.progress(e)}),t=n.promise):t=u(e),t}function s(e){return r(e,a)}function o(e){this.then=e}function u(e){var t=new o(function(t){try{return i(t?t(e):e)}catch(n){return a(n)}});return t}function a(e){var t=new o(function(t,n){try{return n?i(n(e)):a(e)}catch(r){return a(r)}});return t}function f(){function h(e,t,n){return u(e,t,n)}function p(e){return c(e)}function d(e){return c(a(e))}function v(e){return l(e)}var e,t,r,s,u,l,c;return t=new o(h),e={then:h,resolve:p,reject:d,progress:v,promise:t,resolver:{resolve:p,reject:d,progress:v}},r=[],s=[],u=function(e,t,n){var i,o;return i=f(),o=typeof n=="function"?function(e){try{i.progress(n(e))}catch(t){i.progress(t)}}:function(e){i.progress(e)},r.push(function(n){n.then(e,t).then(i.resolve,i.reject,o)}),s.push(o),i.promise},l=function(e){return y(s,e),e},c=function(e){return e=i(e),u=e.then,c=i,l=w,y(r,e),s=r=n,e},e}function l(e){return e&&typeof e.then=="function"}function c(e,t,n,i,s){return b(2,arguments),r(e,function(e){function g(e){p(e)}function y(e){h(e)}var o,u,a,l,c,h,p,d,v,m;v=e.length>>>0,o=Math.max(0,Math.min(t,v)),a=[],u=v-o+1,l=[],c=f();if(!o)c.resolve(a);else{d=c.progress,p=function(e){l.push(e),--u||(h=p=w,c.reject(l))},h=function(e){a.push(e),--o||(h=p=w,c.resolve(a))};for(m=0;m<v;++m)m in e&&r(e[m],y,g,d)}return c.then(n,i,s)})}function h(e,t,n,r){function i(e){return t?t(e[0]):e[0]}return c(e,1,i,n,r)}function p(e,t,n,r){return b(1,arguments),v(e,E).then(t,n,r)}function d(){return v(arguments,E)}function v(e,t){return r(e,function(e){var n,i,s,o,u,a;s=i=e.length>>>0,n=[],a=f();if(!s)a.resolve(n);else{o=function(i,o){r(i,t).then(function(e){n[o]=e,--s||a.resolve(n)},a.reject)};for(u=0;u<i;u++)u in e?o(e[u],u):--s}return a.promise})}function m(n,i){var s=t.call(arguments,1);return r(n,function(t){var n;return n=t.length,s[0]=function(e,t,s){return r(e,function(e){return r(t,function(t){return i(e,t,s,n)})})},e.apply(t,s)})}function g(e,t,n){var i=arguments.length>2;return r(e,function(e){return e=i?n:e,t.resolve(e),e},function(e){return t.reject(e),a(e)},t.progress)}function y(e,t){var n,r=0;while(n=e[r++])n(t)}function b(e,t){var n,r=t.length;while(r>e){n=t[--r];if(n!=null&&typeof n!="function")throw new Error("arg "+r+" must be a function")}}function w(){}function E(e){return e}var e,t,n;return r.defer=f,r.resolve=i,r.reject=s,r.join=d,r.all=p,r.map=v,r.reduce=m,r.any=h,r.some=c,r.chain=g,r.isPromise=l,o.prototype={always:function(e,t){return this.then(e,e,t)},otherwise:function(e){return this.then(n,e)},yield:function(e){return this.then(function(){return e})},spread:function(e){return this.then(function(t){return p(t,function(t){return e.apply(n,t)})})}},t=[].slice,e=[].reduce||function(e){var t,n,r,i,s;s=0,t=Object(this),i=t.length>>>0,n=arguments;if(n.length<=1)for(;;){if(s in t){r=t[s++];break}if(++s>=i)throw new TypeError}else r=n[1];for(;s<i;++s)s in t&&(r=e(r,t[s],s,t));return r},r})}(typeof define=="function"&&define.amd?define:function(e){typeof exports=="object"?module.exports=e():this.when=e()}),define("when",["when/when"],function(e){return e}),define("troopjs-core/event/emitter",["compose","when"],function(t,n){var r="memory",i="context",s="callback",o="length",u="head",a="tail",f="next",l="handled",c="handlers";return t(function(){this[c]={}},{on:function(t,n,r){var h=this,p=arguments,d=h[c],v,m,g,y=p[o],b=2;if(t in d){d=d[t],v={},v[s]=p[b++],v[i]=n,g=a in d?d[a][f]=v:d[u]=v;while(b<y)g=g[f]=v={},v[s]=p[b++],v[i]=n;d[a]=g}else{m=g=v={},v[s]=p[b++],v[i]=n;while(b<y)g=g[f]=v={},v[s]=p[b++],v[i]=n;d=d[t]={},d[u]=m,d[a]=g,d[l]=0}return h},off:function(t,n,r){var l=this,h=arguments,p=l[c],d,v,m,g=h[o],y;if(t in p){p=p[t];if(u in p){d=p[u];e:do{if(d[i]===n){if(g===2)continue;for(y=2;y<g;y++)if(d[s]===h[y])continue e}v?m=m[f]=d:v=m=d}while(d=d[f]);return v&&m?(p[u]=v,p[a]=m,delete m[f]):(delete p[u],delete p[a]),l}return l}return l},reemit:function(t,a,h){var p=this,d=arguments,v=p[c],m,g,y=d[o],b;if(t in v){v=v[t];if(r in v){if(u in v){m=v[u],g=v[l]+1;e:do{if(m[i]===a){if(y===2)continue;for(b=2;b<y;b++)if(m[s]===d[b])continue e}m[l]=g}while(m=m[f]);return p.emit.apply(p,v[r])}return n.resolve(v[r])}}return n.resolve()},emit:function(t){function v(e){a=e||a;while(p[l]===d)if(!(p=p[f]))return h[r]=a,n.resolve(a);return p[l]=d,n(p[s].apply(p[i],a),v)}var o=this,a=arguments,h=o[c],p,d;if(t in h){h=h[t],d=++h[l];if(u in h){p=h[u];try{return v(a)}catch(m){return n.reject(m)}}}else h[t]=h={},h[l]=0;return h[r]=a,n.resolve(a)}})}),define("troopjs-core/component/base",["../event/emitter","when"],function(t,n){var r=null,i=Array.prototype,s=i.unshift,o=i.slice,u="instanceCount",a="length",f="features",l="context",c="value",h="properties",p="sig",d=/^(\w+)(?::(\w+))?\/(.+)/,v=0,m=t.extend(function(){var t=this,n=t.constructor._getBases(!0),i,s=n[a],o=t[h]={},p,m,g,y,b;while(i=n[--s])for(g in i){if(!i.hasOwnProperty(g))continue;if((m=d.exec(g))===r)continue;y=m[1],y=y in o?o[y]:o[y]={},b=m[3],b=b in y?y[b]:y[b]=[],p=b[b[a]]={},p[f]=m[2],p[l]=i,p[c]=i[g]}t[u]=v++},{instanceCount:v,displayName:"core/component",signal:function(t){function l(e){return i=e||i,u>f?n(s[f++][c].apply(r,i),l):n.resolve(i)}var r=this,i=o.call(arguments),s=r[h][p][t],u=s?s[a]:0,f=0;try{return l()}catch(d){return n.reject(d)}},start:function(){var t=this,n=t.signal,r=arguments;return s.call(r,"initialize"),n.apply(t,r).then(function(){return r[0]="start",n.apply(t,r)})},stop:function(){var t=this,n=t.signal,r=arguments;return s.call(r,"stop"),n.apply(t,r).then(function(){return r[0]="finalize",n.apply(t,r)})}});return m.prototype.toString=function(){var e=this;return e.displayName+"@"+e[u]},m}),define("troopjs-core/pubsub/hub",["compose","../component/base"],function(t,n){var r=t.from;return t.create(n,{displayName:"core/pubsub/hub",subscribe:r(n,"on"),unsubscribe:r(n,"off"),publish:r(n,"emit"),republish:r(n,"reemit")})}),define("troopjs-core/component/gadget",["./base","when","../pubsub/hub"],function(t,n,r){var i=Array.prototype.splice,s=r.publish,o=r.republish,u=r.subscribe,a=r.unsubscribe,f="hub",l="length",c="features",h="value",p="properties";return t.extend({displayName:"core/component/gadget","sig/initialize":function(){var t=this,n=t[p][f],i,s,o,a,c,d;for(o in n){i=n[o],s=[o,t];for(a=0,d=i[l],c=s[l];a<d;a++)s[c++]=i[a][h];c>2&&u.apply(r,s)}},"sig/start":function(){var t=this,i=t[p][f],s=[],u,a,d,v,m,g,y;for(v in i){u=i[v],d=[v,t];for(m=0,y=u[l],g=d[l];m<y;m++)a=u[m],a[c]==="memory"&&(d[g++]=a[h]);g>2&&(s[s[l]]=o.apply(r,d))}return n.all(s)},"sig/finalize":function(){var t=this,n=t[p][f],i,s,o,u,c,d;for(o in n){i=n[o],s=[o,t];for(u=0,d=i[l],c=s[l];u<d;u++)s[c++]=i[u][h];c>2&&a.apply(r,s)}},publish:function(){return s.apply(r,arguments)},subscribe:function(){var t=this,n=arguments;return i.call(n,1,0,t),u.apply(r,n),t},unsubscribe:function(){var t=this,n=arguments;return i.call(n,1,0,t),a.apply(r,n),t}})}),define("troopjs-core/component/service",["./gadget"],function(t){return t.extend({displayName:"core/component/service"})}),define("troopjs-data/query/component",["troopjs-core/component/base"],function(t){var n,r=!0,i=!1,s=Object,o=Array,u="constructor",a="length",f="op",l="!",c=".",h=",",p="|",d="text",v="raw",m="resolved",g="id",y="expires",b="collapsed",w="_ast",E="_query",S=/("|')(.*?)\1/,x="$2",T=/!(.*[!,|.\s]+.*)/,N="!'$1'";return t.extend(function(t){var r=this;t!==n&&(r[E]=t)},{displayName:"data/query/component",parse:function(t){var r=this;delete r[w],t=r[E]=t||r[E]||"";var i,s,o,u,m,g,y=[];for(i=u=0,s=t[a];i<s;i++){o=t.charAt(i);switch(o){case'"':case"'":m=m===o?n:o;break;case l:if(m!==n)break;g={},g[f]=o;break;case c:case h:if(m!==n)break;g!==n&&(g[v]=(g[d]=t.substring(u,i)).replace(S,x),y.push(g)),g={},g[f]=o,u=i+1;break;case p:case" ":case" ":case"\r":case"\n":if(m!==n)break;g!==n&&(g[v]=(g[d]=t.substring(u,i)).replace(S,x),y.push(g)),g=n,u=i+1}}return g!==n&&(g[v]=(g[d]=t.substring(u,s)).replace(S,x),y.push(g)),r[w]=y,r},reduce:function(t){var p=this,E=0|(new Date).getTime()/1e3;w in p||p.parse();var S=p[w],x=[],C,k,L,A,O,M,_,D,P=i;for(C=0,A=S[a];C<A;C++){O=S[C];switch(O[f]){case l:_=O,M=O[v],M in t?(D=t[M],O[m]=D[b]!==r&&!(y in D)||D[y]>E):(D=n,O[m]=i);break;case c:M=O[v];if(D&&M in D){D=D[M],L=D[u];if(L===o){O[m]=r;for(k=D[a];k-->0;){L=D[k];if(L[u]===s&&g in L&&(L[b]===r||y in L)&&!(L[y]>E)){O[m]=i;break}continue}}else L===s&&g in D?(O[f]=l,O[d]=(O[v]=D[g]).replace(T,N),O[m]=D[b]!==r&&!(y in D)||D[y]>E):O[m]=r}else D=n,O[m]=i;break;case h:M=_[v],D=t[M],O[f]=l,O[d]=_[d],O[v]=M,O[m]=_[m]}}while(A-->0){O=S[A];switch(O[f]){case l:(P||O[m]!==r)&&x.unshift(O),P=i;break;case c:x.unshift(O),P=r}}return p[w]=x,p},ast:function(){var t=this;return w in t||t.parse(),t[w]},rewrite:function(){var t=this;w in t||t.parse();var n=t[w],r="",i,s,o;for(s=0,i=n[a];s<i;s++){o=n[s];switch(o[f]){case l:r+=s===0?o[d]:p+o[d];break;case c:r+=c+o[d]}}return r}})}),define("troopjs-core/pubsub/topic",["../component/base","troopjs-utils/unique"],function(t,n){function s(e,t){return e.publisherInstanceCount===t.publisherInstanceCount}var r=Object.prototype.toString,i=r.call(Array.prototype),o=t.extend(function(t,n,r){var i=this;i.topic=t,i.publisher=n,i.parent=r,i.publisherInstanceCount=n.instanceCount},{displayName:"core/pubsub/topic",trace:function(){var t=this,o=t.constructor,u,a,f="",l,c,h;while(t){if(r.call(t)===i){c=n.call(t,s);for(l=0,h=c.length;l<h;l++)a=c[l],c[l]=a.constructor===o?a.trace():a.topic;f+=c.join(",");break}u=t.parent,f+=u?t.publisher+":":t.publisher,t=u}return f}});return o.prototype.toString=function(){return this.topic},o}),define("troopjs-data/query/service",["module","troopjs-core/component/service","./component","troopjs-core/pubsub/topic","when","troopjs-utils/merge"],function(t,n,r,i,s,o){var u=Array.prototype,a=u.slice,f=u.concat,l=u.push,c="length",h="batches",p="interval",d="cache",v="topic",m="queries",g="resolved",y="raw",b="id",w="q",E=t.config(),S=n.extend(function(e){var t=this;t[h]=[],t[d]=e},{displayName:"data/query/service","sig/start":function(){var t=this,n=t[d];t[p]=p in t?t[p]:setInterval(function(){function s(){var e=[],n=[],s,u;for(u=r[c];u--;)s=r[u],l.call(n,s[v]),l.apply(e,s[w]);return t.publish(i("ajax",t,n),o.call({data:{q:e.join("|")}},E))}function u(e){var t,i,s,o,u;n.put(e);for(o=r[c];o--;){t=r[o],i=t[m],s=t[b];for(u=i[c];u--;)u in s&&(i[u]=n[s[u]]);t.resolve(i)}}function a(){var e,t;for(t=r[c];t--;)e=r[t],e.reject(e[m])}var r=t[h];if(r[c]===0)return;return t[h]=[],s().then(u,a)},200)},"sig/stop":function(){var t=this;p in t&&(clearInterval(t[p]),delete t[p])},"hub/query":function(t){var n=this,i=n[h],o=n[d],p=[],E=[],S,x,T,N,C,k,L=s.defer();try{C=f.apply(u,a.call(arguments,1));for(x=0,N=C[c];x<N;x++){k=r(C[x]),S=k.ast(),S[c]>0&&(E[x]=S[0][y]),S=k.reduce(o).ast();for(T=S[c];T-->0;)if(!S[T][g]){l.call(p,k.rewrite());break}}if(p[c]===0){for(x=0;x<N;x++)x in E&&(C[x]=o[E[x]]);L.resolve(C)}else L[v]=t,L[m]=C,L[b]=E,L[w]=p,i.push(L)}catch(A){L.reject(A)}return L.promise}});return S.config=function(t){return o.call(E,t)},S}),define("troopjs-data/cache/component",["troopjs-core/component/gadget"],function(t){function E(e,t,u){var a=this,l,S,x,T,N,C,k,L,A,O,M=a[f],_,D;e:{if(!(m in e)){l=e;break e}S=e[m];if(S in a){l=a[S];break e}l=a[S]=e,l[b]=u}if(t===o)for(x=0,T=e[v];x<T;x++)D=e[x],t=D===i||D===n?n:D[d],l[x]=t===s||t===o&&D[v]!==0?E.call(a,D,t,u):D;else if(t===s)for(_ in e){if(_===m||_===w&&l[w]===r)continue;D=e[_],t=D===i||D===n?n:D[d],l[_]=t===s||t===o&&D[v]!==0?E.call(a,D,t,u):D}t:{if(S===i)break t;N=0|u+(l[g]>>>0);n:{if(!(y in l))break n;C=l[y];if(C===N)break t;C in M&&delete M[C][S]}r:{l[y]=N;if(N in M){M[N][S]=l;break r}(O=M[N]={})[p]=N,O[S]=l;if(M[c]===n){M[c]=O;break r}for(L=k=M[c];(A=L[h])!==n&&A[p]<N;L=A);if(L===k&&L[p]>N){O[h]=L,M[c]=O;break r}O[h]=L[h],L[h]=O}}return l}var n,r=!1,i=null,s=Object,o=Array,u=1e3,a="interval",f="generations",l="age",c="head",h="next",p="expires",d="constructor",v="length",m="id",g="maxAge",y="expires",b="indexed",w="collapsed";return t.extend(function(e){var t=this;t[l]=e||60*u,t[f]={}},{displayName:"data/cache/component","sig/start":function(){var t=this,r=t[f];t[a]=a in t?t[a]:setInterval(function(){var i=0|(new Date).getTime()/u,s,o;o=r[c];if(o===n)return;do{if(o[p]>i)break;for(s in o){if(s===p||s===h||s===f)continue;delete t[s]}delete r[o[p]]}while(o=o[h]);r[c]=o},t[l])},"sig/stop":function(){var t=this;a in t&&(clearInterval(t[a]),delete t[a])},"sig/finalize":function(){var t=this,n;for(n in t){if(!(t[n][d]===s&&m in t[n]))continue;delete t[n]}},put:function(t){var r=this,a=t===i||t===n?n:t[d];return a===s||a===o&&t[v]!==0?E.call(r,t,a,0|(new Date).getTime()/u):t}})}),define("troopjs-browser/ajax/service",["troopjs-core/component/service","jquery","troopjs-utils/merge"],function(t,n,r){var i="trace";return t.extend({displayName:"browser/ajax/service","hub/ajax":function(t,s){return n.ajax(r.call({headers:{"x-request-id":(new Date).getTime(),"x-components":t[i]instanceof Function?t[i]():t}},s))}})}),define("troopjs-browser/component/widget",["troopjs-core/component/gadget","jquery","troopjs-jquery/weave","troopjs-jquery/action"],function(t,n){function w(e,t,n){return function(){return a.call(arguments,e),n.apply(t,arguments)}}function E(e){function t(){var t=this,n=arguments,r=u.call(n);return e.call(t[d],r instanceof s?r.apply(t,n):r),t.weave()}return t}var r,i=null,s=Function,o=Array.prototype,u=o.shift,a=o.unshift,f=n.fn.trigger,l=n.fn.one,c=n.fn.bind,h=n.fn.unbind,p=/^dom(?::(\w+))?\/([^\.]+(?:\.(.+))?)/,d="$element",v="$proxies",m="one",g="features",y="[data-weave]",b="[data-woven]";return t.extend(function(t,n){var r=this;r[d]=t,n&&(r.displayName=n)},{displayName:"browser/component/widget","sig/initialize":function(){var t=this,n=t[d],r=t[v]=[],o,u,a,f,h;for(u in t){a=t[u];if(!(a instanceof s))continue;f=p.exec(u),f!==i&&(h=f[2],a=w(h,t,a),(f[2]===m?l:c).call(n,h,t,a),r[r.length]=o=[h,a],o[g]=f[1],t[u]=i)}},"sig/finalize":function(){var t=this,n=t[d],i=t[v],s;while((s=i.shift())!==r)n.unbind(s[0],s[1]);delete t[d]},weave:function(){return this[d].find(y).weave()},unweave:function(){return this[d].find(b).addBack().unweave()},one:function(){var t=this;return l.apply(t[d],arguments),t},bind:function(){var t=this;return c.apply(t[d],arguments),t},unbind:function(){var t=this;return h.apply(t[d],arguments),t},trigger:function(){var t=this;return f.apply(t[d],arguments),t},before:E(n.fn.before),after:E(n.fn.after),html:E(n.fn.html),text:E(n.fn.text),append:E(n.fn.append),prepend:E(n.fn.prepend)})}),define("troopjs-browser/dimensions/widget",["../component/widget","troopjs-jquery/dimensions","troopjs-jquery/resize"],function(t){function r(e,t,n){var r=e.data;r.publish(r.displayName,t,n,e)}var n="dimensions";return t.extend(function(t,r,i){this[n]=i},{displayName:"browser/dimensions/widget","sig/initialize":function(t){var i=this;i.bind(n+"."+i[n],i,r)},"sig/start":function(){this.trigger("resize."+n)},"sig/finalize":function(){var t=this;t.unbind(n+"."+t[n],r)}})}),define("troopjs-browser/store/base",["compose","troopjs-core/component/gadget","when"],function(t,n,r){var i="storage";return n.extend({storage:t.required,set:function(t,n){return r(this[i].setItem(t,JSON.stringify(n)))},get:function(t){return r(JSON.parse(this[i].getItem(t)))},remove:function(t){return r(this[i].removeItem(t))},clear:function(){return r(this[i].clear())}})}),define("troopjs-browser/store/session",["compose","./base"],function(t,n){return t.create(n,{displayName:"browser/store/session",storage:window.sessionStorage})}),define("troopjs-browser/store/local",["compose","./base"],function(t,n){return t.create(n,{displayName:"browser/store/local",storage:window.localStorage})}),define("troopjs-browser/route/widget",["../component/widget","troopjs-utils/uri","troopjs-jquery/hashchange"],function(t,n){function o(e){var t=e.data,r=n(e.target.location.hash.replace(s,"")),o=r.toString();o!==t[i]&&(t[i]=o,t.publish(t.displayName,r,e))}var r="hashchange",i="route",s=/^#/;return t.extend({displayName:"browser/route/widget","sig/initialize":function(){var t=this;t.bind(r,t,o)},"sig/start":function(){this.trigger(r)},"sig/finalize":function(){this.unbind(r,o)}})}),define("troopjs-browser/application/widget",["module","../component/widget","when"],function(t,n,r){function o(e){function a(t){return n=t||n,o>u?r(s[u++].signal(e),a):r.resolve(n)}var t=this,n=arguments,s=t[i],o=s?s.length:0,u=0;return a()}var i="children",s=Array.prototype.slice;return n.extend(function(t,n,r){this[i]=r},{displayName:"browser/application/widget","sig/initialize":o,"sig/start":function(){var t=this,n=t.weave,r=arguments;return o.apply(t,r).then(function(){return n.apply(t,s.call(r,1))})},"sig/stop":function(){var t=this,n=t.unweave,r=arguments;return n.apply(t,s.call(r,1)).then(function(){return o.apply(t,r)})},"sig/finalize":o})}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* TroopJS Bundle - 1.0.7-0-gf886cba | |
* http://troopjs.com/ | |
* Copyright (c) 2012 Mikael Karon <[email protected]> | |
* Licensed MIT | |
*/ | |
define("troopjs-requirejs/template",[],function(){function f(e){function l(e,n,r){return t[f]=n?'" +'+r+'+ "':'";'+r+'o += "',"<%"+String(f++)+"%>"}function c(e,n){return t[n]}function h(e,t){return a[t]||t}var t=[],f=0;return('function template(data) { var o = "'+e.replace(n,"").replace(r,l).replace(s,h).replace(i,c)+'"; return o; }').replace(o,u)}var t={node:function(){var e=require.nodeRequire("fs");return function(n,r){r(e.readFileSync(n,"utf8"))}},browser:function(){var e=["Msxml2.XMLHTTP","Microsoft.XMLHTTP","Msxml2.XMLHTTP.4.0"],t,n,r;if(typeof XMLHttpRequest!="undefined")n=XMLHttpRequest;else{for(r=0;r<3;r++){t=e[r];try{new ActiveXObject(t),n=function(){return new ActiveXObject(t)};break}catch(i){}}if(!n)throw new Error("XHR: XMLHttpRequest not available")}return function(t,r){var i=new n;i.open("GET",t,!0),i.onreadystatechange=function(e){i.readyState===4&&r(i.responseText)},i.send(null)}},rhino:function(){var e="utf-8",t=java.lang.System.getProperty("line.separator");return function(r,i){var s=new java.io.File(r),o=new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(s),e)),u=new java.lang.StringBuffer,a,f="";try{a=o.readLine(),a&&a.length()&&a.charAt(0)===65279&&(a=a.substring(1)),u.append(a);while((a=o.readLine())!==null)u.append(t),u.append(a);f=String(u.toString())}finally{o.close()}i(f)}},borked:function(){return function(){throw new Error("Environment unsupported.")}}},n=/^[\n\t\r]+|[\n\t\r]+$/g,r=/<%(=)?([\S\s]*?)%>/g,i=/<%(\d+)%>/gm,s=/(["\n\t\r])/gm,o=/o \+= "";| \+ ""/gm,u="",a={'"':'\\"',"\n":"\\n"," ":"\\t","\r":"\\r"},l={},c=t[typeof process!="undefined"&&process.versions&&!!process.versions.node?"node":typeof window!="undefined"&&window.navigator&&window.document||typeof importScripts!="undefined"?"browser":typeof Packages!="undefined"?"rhino":"borked"]();return{load:function(e,t,n,r){var i=t.toUrl(e);c(i,function(s){try{s="define(function() { return "+f(s,e,i,r.template)+"; })"}catch(o){throw o.message="In "+i+", "+o.message,o}r.isBuild?l[e]=s:s+="\n//@ sourceURL='"+i+"'",n.fromText(e,s),t([e],function(e){n(e)})})},write:function(e,t,n){l.hasOwnProperty(t)&&n.asModule(e+"!"+t,l[t])}}}),define("troopjs-jquery/hashchange",["jquery"],function(t){function a(e){var t=s.exec(e.location.href);return t&&t[1]?decodeURIComponent(t[1]):""}function f(e){var t=this,n;t.element=n=e.createElement("iframe"),n.src="about:blank",n.style.display="none"}var n="interval",r="hashchange",i="on"+r,s=/#(.*)$/,o=/\?/,u=0;f.prototype={getElement:function(){return this.element},getHash:function(){return this.element.contentWindow.frameHash},update:function(e){var t=this,n=t.element.contentWindow.document;if(t.getHash()===e)return;n.open(),n.write("<html><head><title>' + document.title + '</title><script type='text/javascript'>var frameHash='"+e+"';</script></head><body> </body></html>"),n.close()}},t.event.special[r]={setup:function(s,l,c){var h=this;if(i in h)return!1;if(!t.isWindow(h))throw new Error("Unable to bind 'hashchange' to a non-window object");var p=t(h),d=a(h),v=h.location;p.data(n,h.setInterval(u?function(){var t=h.document,n=v.protocol==="file:",i=new f(t);return t.body.appendChild(i.getElement()),i.update(d),function(){var t=d,s,u=a(h),f=i.getHash();f!==d&&f!==u?(s=decodeURIComponent(f),d!==s&&(d=s,i.update(d),p.trigger(r,[s,t])),v.hash="#"+encodeURI(n?f.replace(o,"%3F"):f)):u!==d&&(s=decodeURIComponent(u),d!==s&&(d=s,p.trigger(r,[s,t])))}}():function(){var t=d,n,i=a(h);i!==d&&(n=decodeURIComponent(i),d!==n&&(d=n,p.trigger(r,[n,t])))},25))},teardown:function(r){var s=this;if(i in s)return!1;s.clearInterval(t.data(s,n))}}}),define("troopjs-utils/getargs",[],function(){var t=Array.prototype.push,n=String.prototype.substring,r=/^(?:false|true)$/i,i=/^true$/i,s=/^\d+$/;return function(){var o=this,u=[],a,f,l,c,h,p,d=!1;for(f=l=c=0,a=o.length;c<a;c++){h=o.charAt(c);switch(h){case'"':case"'":d===h?(d=!1,t.call(u,n.call(o,f,l))):d=h,f=l=c+1;break;case",":if(d){l=c+1;break}f!==l&&(p=n.call(o,f,l),r.test(p)?p=i.test(p):s.test(p)&&(p=+p),t.call(u,p)),f=l=c+1;break;case" ":case" ":if(d){l=c+1;break}f===l&&(f=l=c+1);break;default:l=c+1}}return f!==l&&(p=n.call(o,f,l),r.test(p)?p=i.test(p):s.test(p)&&(p=+p),t.call(u,p)),u}}),define("troopjs-jquery/action",["jquery","troopjs-utils/getargs"],function(t,n){function c(e,t){return e?e+"."+u:s}function h(e){var n=t(this),r=o.call(arguments,1),s=a in e?e[a].type:u,f=e[u];e.type=u+"/"+f+"."+s,n.trigger(e,r),e.result!==i&&(e.type=u+"/"+f+"!",n.trigger(e,r),e.result!==i&&(e.type=u+"."+s,n.trigger(e,r)))}function p(e){var i=t(e.target).closest("[data-action]");if(i.length===0)return;var o=i.data(),a=f.exec(o[u]);if(a===s)return;var c=a[1],h=a[2],p=a[3];if(h!==r&&!RegExp(h.split(l).join("|")).test(e.type))return;var d=p!==r?n.call(p):[];t.each(d,function(t,n){n in o&&(d[t]=o[n])}),i.trigger(t.Event(e,{type:u+"!",action:c}),d),e.stopPropagation()}var r,i=!1,s=null,o=Array.prototype.slice,u="action",a="originalEvent",f=/^([\w\d\s_\-\/]+)(?:\.([\w\.]+))?(?:\((.*)\))?$/,l=/\.+/;t.event.special[u]={setup:function(n,r,i){t(this).bind(u,n,h)},add:function(n){var r=t.map(n.namespace.split(l),c);r.length!==0&&t(this).bind(r.join(" "),p)},remove:function(n){var r=t.map(n.namespace.split(l),c);r.length!==0&&t(this).unbind(r.join(" "),p)},teardown:function(n){t(this).unbind(u,h)}},t.fn[u]=function(n){return t(this).trigger({type:u+"!",action:n},o.call(arguments,1))}}),define("troopjs-jquery/weave",["jquery","troopjs-utils/getargs","require"],function(t,n,r){function C(){t(this).unweave()}var i,s=null,o=Array,u=Function,a=o.prototype,f=a.join,l=a.push,c=a.pop,h=t.when,p="then",d="weave",v="unweave",m="woven",g="weaving",y="pending",b="destroy",w="data-",E=w+d,S=w+m,x=w+g,T="["+E+"]",N="["+x+"],["+S+"]";t.expr[":"][d]=t.expr.createPseudo?t.expr.createPseudo(function(e){return e!==i&&(e=RegExp(t.map(n.call(e),function(e){return"^"+e+"$"}).join("|"),"m")),function(n,r,s){var o=t(n).attr(E);return o===i?!1:e===i?!0:e.test(o.split(/[\s,]+/).join("\n"))}}):function(e,r,s){var o=t(e).attr(E);return o===i?!1:s===i?!0:RegExp(t.map(n.call(s[3]),function(e){return"^"+e+"$"}).join("|"),"m").test(o.split(/[\s,]+/).join("\n"))},t.expr[":"][m]=t.expr.createPseudo?t.expr.createPseudo(function(e){return e!==i&&(e=RegExp(t.map(n.call(e),function(e){return"^"+e+"@\\d+"}).join("|"),"m")),function(n,r,s){var o=t(n).attr(S);return o===i?!1:e===i?!0:e.test(o.split(/[\s,]+/).join("\n"))}}):function(e,r,s){var o=t(e).attr(S);return o===i?!1:s===i?!0:RegExp(t.map(n.call(s[3]),function(e){return"^"+e+"@\\d+"}).join("|"),"m").test(o.split(/[\s,]+/).join("\n"))},t.fn[d]=function(){var o=[],a=0,v=t(this),g=arguments,w=g.length,N=w>0&&g[w-1][p]instanceof u?c.call(g):t.Deferred();return v.filter(T).each(function(u,c){t.Deferred(function(u){var p=t(c),v=p.data(),w=v[d]=p.attr(E)||"",T=v[m]||(v[m]=[]),N=v[y]||(v[y]=[]);u.done(function(){p.removeAttr(x).attr(S,f.call(arguments," "))}),h.apply(t,N).then(function(){var f=/[\s,]*([\w_\-\/\.]+)(?:\(([^\)]+)\))?/g,c=a,d=0,m;l.call(N,u),p.removeAttr(E).attr(x,w).bind(b,C);while((m=f.exec(w))!==s)t.Deferred(function(s){var f=d++,l,c,h,y;o[a++]=s,s.then(function(t){T[f]=t},u.reject,u.notify);var b=m[1],w=[p,b];for(l=0,h=g.length,c=w.length;l<h;l++,c++)w[c]=g[l];var E=m[2];if(E!==i){E=n.call(E);for(l=0,h=E.length,c=w.length;l<h;l++,c++)y=E[l],w[c]=y in v?v[y]:y}r([b],function(n){t.Deferred(function(t){var r=n.apply(n,w);t.then(function(){s.resolve(r)},s.reject,s.notify),r.start(t)})})});h.apply(t,o.slice(c,a)).then(u.resolve,u.reject,u.notify)},u.reject,u.notify)})}),h.apply(t,o).then(N.resolve,N.reject,N.notify),v},t.fn[v]=function(n){var r=[],s=0,o=t(this);return n=n||t.Deferred(),o.filter(N).each(function(n,o){t.Deferred(function(n){var u=t(o),a=u.data(),f=a[y]||(a[y]=[]),c=a[m]||[];n.done(function(){u.attr(E,a[d]).unbind(b,C),delete a[d]}),h.apply(t,f).done(function(){var o=s,p;l.call(f,n),delete a[m],u.removeAttr(S);while((p=c.shift())!==i)t.Deferred(function(n){r[s++]=n,t.Deferred(function(t){t.then(function(){n.resolve(p)},n.reject,n.notify),p.stop(t)})});h.apply(t,r.slice(o,s)).then(n.resolve,n.reject,n.notify)})})}),h.apply(t,r).then(n.resolve,n.reject,n.notify),o},t.fn[m]=function(){var n=[],r=arguments.length>0?RegExp(t.map(arguments,function(e){return"^"+e+"$"}).join("|"),"m"):i;return t(this).each(function(s,o){if(!t.hasData(o))return;l.apply(n,r===i?t.data(o,m):t.map(t.data(o,m),function(e){return r.test(e.displayName)?e:i}))}),n}}),define("troopjs-jquery/dimensions",["jquery"],function(t){function f(e,t){return t-e}function l(e){var n=t(this),i=n.width(),f=n.height();t.each(t.data(self,r),function(t,l){var c=l[s],h=l[o],p,d,v;v=c.length,p=c[v-1];while(c[--v]<i)p=c[v];v=h.length,d=h[v-1];while(h[--v]<f)d=h[v];if(p!==l[u]||d!==l[a])l[u]=p,l[a]=d,n.trigger(r+"."+t,[p,d])})}var n=null,r="dimensions",i="resize."+r,s="w",o="h",u="_"+s,a="_"+o;t.event.special[r]={setup:function(n,s,o){t(this).bind(i,l).data(r,{})},add:function(i){var u=this,a=i.namespace,l={},c=l[s]=[],h=l[o]=[],p=/(w|h)(\d+)/g,d;while((d=p.exec(a))!==n)l[d[1]].push(parseInt(d[2],10));c.sort(f),h.sort(f),t.data(u,r)[a]=l},remove:function(n){delete t.data(this,r)[n.namespace]},teardown:function(n){t(this).removeData(r).unbind(i,l)}}}),define("troopjs-jquery/destroy",["jquery"],function(t){t.event.special.destroy={remove:function(n){var r=this;n.handler.call(r,t.Event({type:n.type,data:n.data,namespace:n.namespace,target:r}))}}}),define("troopjs-jquery/resize",["jquery"],function(t){function a(e,n){var o=t.data(n),u=t(n),a=u.width(),f=u.height();(a!==o[i]||f!==o[s])&&u.trigger(r,[o[i]=a,o[s]=f])}function f(){o.each(a)}var n=null,r="resize",i="w",s="h",o=t([]),u=n;t.event.special[r]={setup:function(n,a,l){var c=this;if(t.isWindow(c))return!1;var h=t.data(c,r,{}),p=t(c);h[i]=p.width(),h[s]=p.height(),o=o.add(c),o.length===1&&(u=setInterval(f,100))},teardown:function(i){var s=this;if(t.isWindow(s))return!1;t.removeData(s,r),o=o.not(s),o.length===0&&u!==n&&clearInterval(u)}}}),define("troopjs-utils/merge",[],function(){var t=Array,n=Object;return function r(e){var i=this,s=null,o,u,a,f;for(o=0,u=arguments.length;o<u;o++){e=arguments[o];for(s in e)a=e[s],f=a.constructor,s in i?f===t?i[s]=i[s].concat(a):f===n?r.call(i[s],a):i[s]=a:i[s]=a}return i}}),define("troopjs-utils/grep",["jquery"],function(t){return t.grep}),define("troopjs-utils/tr",[],function(){var t=typeof Number();return function(n){var r=this,i=[],s,o=r.length,u;if(typeof o===t&&o===0||o>0&&0 in r&&o-1 in r)for(s=0;s<o;s++)i.push(n.call(r,r[s],s));else if(r)for(u in r)i.push(n.call(r,r[u],u));return i}}),function(e){e("compose",[],function(){function e(){}function n(e){if(!e)throw new Error("Compose arguments must be functions or objects");return e}function r(e,t,r){var s,o=t.length;for(;r<o;r++){var u=t[r];if(typeof u=="function"){var a=u.prototype;for(var l in a){s=a[l];var c=a.hasOwnProperty(l);if(typeof s=="function"&&l in e&&s!==e[l]){var p=e[l];s==f?s=p:c||(i(s,l,h([].slice.call(t,0,r),!0))?s=p:i(p,l,h([u],!0))||console.error("Conflicted method "+l+", final composer must explicitly override with correct method."))}s&&s.install&&c&&!i(p,l,h([u],!0))?s.install.call(e,l):e[l]=s}}else for(var l in n(u)){var s=u[l];if(typeof s=="function"){if(s.install){s.install.call(e,l);continue}if(l in e&&s==f)continue}e[l]=s}}return e}function i(e,t,n){for(var r=0;r<n.length;r++){var i=n[r];if(i[t]==e)return!0}}function s(e,t){function n(){if(t)return t.apply(this,arguments);throw new Error("Decorator not applied")}return n.install=e,n}function o(e){return function(t){return s(function n(r){var i=this[r];(t=this[r]=i?e(this,i,t):t).install=n},t)}}function f(){throw new Error("This method is required and no implementation has been provided")}function l(){var e=[this];return e.push.apply(e,arguments),c.apply(0,e)}function c(i){function u(){var t;this instanceof u?t=this:(e.prototype=o,t=new e);for(var n=0;n<f;n++){var r=a[n],i=r.apply(t,arguments);if(typeof i=="object")if(i instanceof u)t=i;else for(var s in i)i.hasOwnProperty(s)&&(t[s]=i[s])}return t}var s=arguments,o=s.length<2&&typeof s[0]!="function"?s[0]:r(t(n(i)),s,1);u._getBases=function(e){return e?p:a};var a=h(s),f=a.length;typeof s[s.length-1]=="object"&&(s[s.length-1]=o);var p=h(s,!0);return u.extend=l,c.secure||(o.constructor=u),u.prototype=o,u}function h(e,t){function r(e,i){e:for(var s=0;s<e.length;s++){var o=e[s],u=t&&typeof o=="function"?o.prototype:o;if(t||typeof o=="function"){var a=i&&o._getBases;if(a)r(a(t));else{for(var f=0;f<n.length;f++)if(u==n[f])continue e;n.push(u)}}}}var n=[];return r(e,!0),n}var t=Object.create?function(e){return Object.create(typeof e=="function"?e.prototype:e||Object.prototype)}:function(t){e.prototype=typeof t=="function"?t.prototype:t;var n=new e;return e.prototype=null,n};c._setMixin=function(e){r=e},c.Decorator=s,c.around=o(function(e,t,n){return n.call(e,t)}),c.before=o(function(e,t,n){return function(){var e=n.apply(this,arguments);if(e!==u)return t.apply(this,e||arguments)}});var u=c.stop={},a;return c.after=o(function(e,t,n){return function(){var e=t.apply(this,arguments),r=n.apply(this,arguments);return r===a?e:r}}),c.from=function(e,t){return t?(typeof e=="function"?e.prototype:e)[t]:s(function(n){if(!(this[n]=typeof e=="string"?this[e]:(typeof e=="function"?e.prototype:e)[t||n]))throw new Error("Source method "+t+" was not available to be renamed to "+n)})},c.create=function(e){var n=r(t(e),arguments,1),i=arguments.length;for(var s=0;s<i;s++){var o=arguments[s];typeof o=="function"&&(n=o.call(n)||n)}return n},c.required=f,c.apply=function(e,t){return e?r(e,t,0):l.apply.call(c,0,t)},c.call=function(e){return r(e,arguments,1)},c})}(typeof define!="undefined"?define:function(e,t){typeof module!="undefined"?module.exports=t():Compose=t()}),define("troopjs-utils/uri",["compose"],function(t){function w(e){var t={},r,i=n,s,o=/(?:&|^)([^&=]*)=?([^&]*)/g;t.toString=w.toString;if(u.call(e)===a)for(i in e)t[i]=e[i];else while((r=o.exec(e))!==n)i=r[1],i in t?(s=t[i],u.call(s)===f?s[s.length]=r[2]:t[i]=[s,r[2]]):t[i]=r[2];return t}function E(e){var t=[];return t.toString=E.toString,s.apply(t,u.call(e)===f?e:o.call(e,"/")),t}var n=null,r=Array.prototype,i=Object.prototype,s=r.push,o=String.prototype.split,u=i.toString,a=u.call(i),f=u.call(r),l=u.call(String.prototype),c=u.call(Function.prototype),h=/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?(?:([^?#]*)(?:\?([^#]*))?(?:#(.*))?)/,p="protocol",d="authority",v="path",m="query",g="anchor",y=["source",p,d,"userInfo","user","password","host","port",v,m,g],b=t.secure;t.secure=!0,w.toString=function x(){var e=this,t=n,r=n,i,s=[],o=0,a;for(t in e){if(u.call(e[t])===c)continue;s[o++]=t}s.sort();while(o--){t=s[o],r=e[t];if(u.call(r)===f){i=r.slice(0),i.sort(),a=i.length;while(a--)r=i[a],i[a]=r===""?t:t+"="+r;s[o]=i.join("&")}else s[o]=r===""?t:t+"="+r}return s.join("&")},E.toString=function(){return this.join("/")};var S=t(function(t){var r=this,i,s,o;if((s=h.exec(t))!==n){o=s.length;while(o--)i=s[o],i&&(r[y[o]]=i)}m in r&&(r[m]=w(r[m])),v in r&&(r[v]=E(r[v]))});return S.prototype.toString=function(){var e=this,t=[p,"://",d,v,"?",m,"#",g],n,r;p in e||(t[0]=t[1]=""),d in e||(t[2]=""),v in e||(t[3]=""),m in e||(t[4]=t[5]=""),g in e||(t[6]=t[7]=""),n=t.length;while(n--)r=t[n],r in e&&(t[n]=e[r]);return t.join("")},t.secure=b,S.Path=E,S.Query=w,S}),define("troopjs-utils/each",["jquery"],function(t){return t.each}),define("troopjs-utils/callbacks",["jquery"],function(t){return t.Callbacks}),define("troopjs-utils/unique",[],function(){return function(t){var n=this,r=n.length,i=[],s,o,u,a;e:for(o=u=a=0;o<r;o++,u=0){s=n[o];while(u<a)if(t.call(n,s,i[u++])===!0)continue e;i[a++]=s}return i}}),define("troopjs-utils/when",["jquery"],function(t){return t.when}),define("troopjs-utils/deferred",["jquery"],function(t){return t.Deferred}),define("troopjs-core/event/emitter",["compose"],function(t){var n,r=!0,i=!1,s=Function,o="memory",u="context",a="callback",f="length",l="head",c="tail",h="next",p="handled",d="handlers",v={},m=0;return t(function(){this[d]={}},{on:function(t){var n=this,m=arguments,g=m[f],y=m[1],b=m[2],w=m[3],E=n[d],S,x,T,N,C;if(y instanceof s)b=i,y=v,C=1;else if(y===r||y===i)b=y,y=v,C=2;else if(b instanceof s)b=i,C=2;else{if(!(w instanceof s))return n;C=3}if(t in E){E=E[t],S={callback:m[C++],context:y},N=c in E?E[c][h]=S:E[l]=S;while(C<g)N=N[h]={callback:m[C++],context:y};E[c]=N;if(b&&o in E){b=E[o],x=b[p];if(b[f]>0)while(S){if(S[p]===x){S=S[h];continue}S[p]=x,S[a].apply(S[u],b),S=S[h]}else while(S){if(S[p]===x){S=S[h];continue}S[p]=x,S[a].call(S[u]),S=S[h]}}}else{T=N={callback:m[C++],context:y};while(C<g)N=N[h]={callback:m[C++],context:y};E[t]={head:T,tail:N}}return n},off:function(t){var r=this,i=arguments,o=i[f],p=i[1],m=i[2],g=r[d],y,b,w,E;if(p instanceof s)m=p,p=v,E=1;else{if(!(m instanceof s))return r;E=2}if(t in g){g=g[t],b=g[l];while(E<o){m=i[E++],y=w=b;do{if(y[a]===m&&y[u]===p){if(y===b){b=w=y[h];continue}w[h]=y[h];continue}w=y}while((y=y[h])!==n)}return b&&w?(g[l]=b,g[c]=w):(delete g[l],delete g[c]),r}return r},emit:function(t){var n=this,r=arguments,i=n[d],s,c=r[p]=m++;if(t in i){i=i[t],i[o]=r,s=i[l];if(r[f]>0)while(s){if(s[p]===c){s=s[h];continue}s[p]=c,s[a].apply(s[u],r),s=s[h]}else while(s){if(s[p]===c){s=s[h];continue}s[p]=c,s[a].call(s[u]),s=s[h]}}else r[f]>0&&(i[t]=i={},i[o]=r);return this}})}),define("troopjs-core/component/base",["../event/emitter","config"],function(t,n){var r=0,i="instanceCount",s=t.extend(function(){this[i]=r++},{displayName:"core/component",config:n});return s.prototype.toString=function(){var e=this;return e.displayName+"@"+e[i]},s}),define("troopjs-core/pubsub/hub",["compose","../component/base"],function(t,n){var r=t.from;return t.create(n,{displayName:"core/pubsub/hub",subscribe:r(n,"on"),unsubscribe:r(n,"off"),publish:r(n,"emit")})}),define("troopjs-core/component/gadget",["compose","./base","troopjs-utils/deferred","../pubsub/hub"],function(t,n,r,i){var s,o=null,u=Function,a=/^hub(?::(\w+))?\/(.+)/,f=/^sig\/(.+)/,l=i.publish,c=i.subscribe,h=i.unsubscribe,p="memory",d="subscriptions";return n.extend(function(){var n=this,i=n.constructor._getBases(!0),s,a,l,c,h,p,d={},v,m,g=null;for(c=i.length-1;c>=0;c--){s=i[c];e:for(g in s){l=s[g];if(!(l instanceof u))continue;m=f.exec(g);if(m!==o){v=m[1];if(v in d){a=d[v],h=p=a.length;while(h--)if(l===a[h])continue e;a[p]=l}else d[v]=[l]}}}t.call(n,{signal:function(t,n){var i=this,s,o,u=n;if(t in d){s=d[t],o=s.length;while(--o)u=r(function(e){var n=s[o],r=u;e.done(function(){n.call(i,t,r)})});s[0].call(i,t,u)}else n&&n.resolve();return i}})},{displayName:"core/component/gadget","sig/initialize":function(t,n){var r=this,s=r[d]=[],f=o,l,c,h;for(f in r){l=r[f];if(!(l instanceof u))continue;c=a.exec(f),c!==o&&(h=c[2],i.subscribe(h,r,c[1]===p,l),s[s.length]=[h,r,l],r[f]=o)}return n&&n.resolve(),r},"sig/finalize":function(t,n){var r=this,o=r[d],u;while((u=o.shift())!==s)i.unsubscribe(u[0],u[1],u[2]);return n&&n.resolve(),r},publish:function(){var t=this;return l.apply(i,arguments),t},subscribe:function(){var t=this;return c.apply(i,arguments),t},unsubscribe:function(){var t=this;return h.apply(i,arguments),t},start:function(t){var n=this;return t=t||r(),r(function(i){i.then(t.resolve,t.reject,t.notify),r(function(t){t.then(function(){n.signal("start",i)},i.reject,i.notify),n.signal("initialize",t)})}),n},stop:function(t){var n=this;return t=t||r(),r(function(i){i.then(t.resolve,t.reject,t.notify),r(function(t){t.then(function(){n.signal("finalize",i)},i.reject,i.notify),n.signal("stop",t)})}),n}})}),define("troopjs-core/component/service",["./gadget"],function(t){return t.extend({displayName:"core/component/service"})}),define("troopjs-core/component/widget",["./gadget","jquery","troopjs-utils/deferred"],function(t,n,r){function S(e,t,n){return function(){return f.call(arguments,e),n.apply(t,arguments)}}function x(e){function t(){var t=this,n=t[m],s=arguments,u=a.call(s),f=s[s.length-1];if(f===i||!(f[b]instanceof o))f=r();return r(function(i){i.then(function(){n.trigger(v,arguments),f.resolve()},f.reject,f.notify),i.notify("beforeRender",t),e.call(n,u instanceof o?u.apply(t,s):u),i.notify("afterRender",t),n.find(w).weave(i)}),t}return t}var i,s=null,o=Function,u=Array.prototype,a=u.shift,f=u.unshift,l=n.fn.trigger,c=n.fn.one,h=n.fn.bind,p=n.fn.unbind,d=/^dom(?::(\w+))?\/([^\.]+(?:\.(.+))?)/,v="widget/refresh",m="$element",g="$proxies",y="one",b="then",w="[data-weave]",E="[data-woven]";return t.extend(function(t,n){var r=this;r[m]=t,n&&(r.displayName=n)},{displayName:"core/component/widget","sig/initialize":function(t,n){var r=this,i=r[m],u=r[g]=[],a=s,f,l,p;for(a in r){f=r[a];if(!(f instanceof o))continue;l=d.exec(a),l!==s&&(p=l[2],f=S(p,r,f),(l[2]===y?c:h).call(i,p,r,f),u[u.length]=[p,f],r[a]=s)}return n&&n.resolve(),r},"sig/finalize":function(t,n){var r=this,s=r[m],o=r[g],u;while((u=o.shift())!==i)s.unbind(u[0],u[1]);return delete r[m],n&&n.resolve(),r},weave:function(t){var n=this;return n[m].find(w).weave(t),n},unweave:function(t){var n=this;return n[m].find(E).andSelf().unweave(t),this},one:function(){var t=this;return c.apply(t[m],arguments),t},bind:function(){var t=this;return h.apply(t[m],arguments),t},unbind:function(){var t=this;return p.apply(t[m],arguments),t},trigger:function(){var t=this;return l.apply(t[m],arguments),t},before:x(n.fn.before),after:x(n.fn.after),html:x(n.fn.html),text:x(n.fn.text),append:x(n.fn.append),prepend:x(n.fn.prepend),empty:function(t){var n=this;return t=t||r(),r(function(r){r.then(t.resolve,t.reject,t.notify);var i=n[m],s=i.contents().detach();i.trigger(v,n),setTimeout(function(){var t=s.get();s.remove(),r.resolve(t)},0)}),n}})}),define("troopjs-core/dimensions/service",["../component/service"],function(t){function i(e,t,r){e.data.publish(n,t,r)}var n="dimensions",r="$element";return t.extend(function(t,i){var s=this;s[r]=t,s[n]=i},{displayName:"core/dimensions/service","sig/initialize":function(t,s){var o=this;o[r].bind(n+"."+o[n],o,i),s&&s.resolve()},"sig/start":function(t,i){var s=this;s[r].trigger("resize."+n),i&&i.resolve()},"sig/finalize":function(t,s){var o=this;o[r].unbind(n+"."+o[n],i),s&&s.resolve()}})}),define("troopjs-core/store/base",["compose","../component/gadget"],function(t,n){var r="storage";return n.extend({storage:t.required,set:function(t,n,i){this[r].setItem(t,JSON.stringify(n)),i&&i.resolve(n)},get:function(t,n){var i=JSON.parse(this[r].getItem(t));n&&n.resolve(i)},remove:function(t,n){this[r].removeItem(t),n&&n.resolve()},clear:function(t){this[r].clear(),t&&t.resolve()}})}),define("troopjs-core/store/session",["compose","./base"],function(t,n){return t.create(n,{displayName:"core/store/session",storage:window.sessionStorage})}),define("troopjs-core/store/local",["compose","./base"],function(t,n){return t.create(n,{displayName:"core/store/local",storage:window.localStorage})}),define("troopjs-core/route/router",["../component/service","troopjs-utils/uri"],function(t,n){function u(e){var t=e.data,r=n(e.target.location.hash.replace(o,"")),i=r.toString();i!==t[s]&&(t[s]=i,t.publish(s,r))}var r="hashchange",i="$element",s="route",o=/^#/;return t.extend(function(t){this[i]=t},{displayName:"core/route/router","sig/initialize":function(t,n){var s=this;return s[i].bind(r,s,u),n&&n.resolve(),s},"sig/start":function(t,n){var s=this;return s[i].trigger(r),n&&n.resolve(),s},"sig/finalize":function(t,n){var s=this;return s[i].unbind(r,u),n&&n.resolve(),s}})}),define("troopjs-core/widget/placeholder",["../component/widget","troopjs-utils/deferred","require"],function(t,n,r){function c(){var e=this,t=arguments,c=t.length,h=c>0&&t[c-1][l]instanceof i?s.call(t):n();return n(function(s){var l,c,p,d;if(o in e)s.done(h.resolve).resolve(e[o]);else{s.then([function(n){e[a].attr(u,n),e[o]=n},h.resolve],h.reject,h.notify),p=e[f],d=[e[a],p];for(l=0,c=t.length;l<c;l++)d[l+2]=t[l];r([p],function(t){n(function(n){var r=t.apply(t,d);n.then(function(){s.resolve(r)},s.reject,s.notify),r.start(n)})})}}),e}function h(e){var t=this;return e=e||n(),n(function(r){var i;r.then(e.resolve,e.reject,e.notify),o in t?(i=t[o],delete t[o],t[a].removeAttr(u),i.stop(r)):r.resolve()}),t}var i=Function,s=Array.prototype.pop,o="holding",u="data-"+o,a="$element",f="target",l="then";return t.extend(function(t,n,r){this[f]=r},{displayName:"core/widget/placeholder","sig/finalize":function(t,n){this.hold(n)},release:c,hold:h})}),define("troopjs-core/route/placeholder",["../widget/placeholder"],function(t){var n=null,r="route";return t.extend(function(t,n){this[r]=RegExp(t.data("route"))},{displayName:"core/route/placeholder","hub:memory/route":function(t,i){var s=this,o=s[r].exec(i.path);o!==n?s.release.apply(s,o.slice(1)):s.hold()}})}),define("troopjs-core/widget/application",["../component/widget","troopjs-utils/deferred"],function(t,n){return t.extend({displayName:"core/widget/application","sig/start":function(t,n){this.weave(n)},"sig/stop":function(t,n){this.unweave(n)}})}),define("troopjs-core/pubsub/topic",["../component/base","troopjs-utils/unique"],function(t,n){function s(e,t){return e.publisherInstanceCount===t.publisherInstanceCount}var r=Object.prototype.toString,i=r.call(Array.prototype),o=t.extend(function(t,n,r){var i=this;i.topic=t,i.publisher=n,i.parent=r,i.publisherInstanceCount=n.instanceCount},{displayName:"core/pubsub/topic",trace:function(){var t=this,o=t.constructor,u,a,f="",l,c,h;while(t){if(r.call(t)===i){c=n.call(t,s);for(l=0,h=c.length;l<h;l++)a=c[l],c[l]=a.constructor===o?a.trace():a.topic;f+=c.join(",");break}u=t.parent,f+=u?t.publisher+":":t.publisher,t=u}return f}});return o.prototype.toString=function(){return this.topic},o}),define("troopjs-core/remote/ajax",["../component/service","../pubsub/topic","jquery","troopjs-utils/merge"],function(t,n,r,i){return t.extend({displayName:"core/remote/ajax","hub/ajax":function(t,s,o){r.ajax(i.call({headers:{"x-request-id":(new Date).getTime(),"x-components":t instanceof n?t.trace():t}},s)).then(o.resolve,o.reject,o.notify)}})}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment