Commit 47c8ab0a authored by Mikael Karon's avatar Mikael Karon Committed by Sindre Sorhus

Close GH-120: Example using TroopJS.

parent c7f05d38
@import url("../../../../assets/base.css");
/* base.css overrides */
#todo-list li .prepare {
#footer, #main {
display: none;
position: absolute;
top: 20px;
right: 35px;
cursor: pointer;
width: 20px;
height: 20px;
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2RpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3ODkwNzY1QzM1QUVFMDExOUI0MDg3MzJEODFDNjBGNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpEODdBM0JBQ0I3MDAxMUUwQjBBMUMzMUU4NTAwRDVFRSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpEODdBM0JBQkI3MDAxMUUwQjBBMUMzMUU4NTAwRDVFRSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoxQTFDMDFENjAwQjdFMDExQTVFMUI2QkYyMkQyMTM0OSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3ODkwNzY1QzM1QUVFMDExOUI0MDg3MzJEODFDNjBGNyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PstLE0sAAAD3SURBVHjanNLBCgFBHMdxO22urh6AB1BSHkAJ7UE2OeDg6kr2CSgHt5WLA6VwIhd3LyFOrh5AKeU39d+apml2Zv/1qTW7vu0244RhmEowJ6jR9RUaboLICjzhdx0uzDKSg65ivWoT4oEX/wz4SPcc09AUNnCEmyJ2YIaRCV17itgaOq5FJCXFfCjAnS8yy4gYa0cRXUgX4TOHrbjAEkRmMJYXWYJIoLohhkp0Vr62ETk0pAd9RUwbEUNZKMMOzlIsNsInOkd9WMIP0pCBN+1MYHL0Gf1xAE8YwQMq0DSNRG/Ugjws6LgX6W2sxqXd6sFes2Ox8xdgAFvzNQf5podzAAAAAElFTkSuQmCC') no-repeat center center;
}
#todo-list li:hover .prepare {
display: block;
}
#todo-list li {
padding-right: 60px;
.filter-active .completed,
.filter-completed .active {
display: none;
}
......@@ -2,36 +2,55 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>Troop.js</title>
<link rel="stylesheet" href="css/app.css" type="text/css" media="screen" charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Troop.js • TodoMVC</title>
<link rel="stylesheet" href="../../../assets/base.css">
<!-- CSS overrides - remove if you don't need it -->
<link rel="stylesheet" href="css/app.css">
<!--[if IE]>
<script src="../../../assets/ie.js"></script>
<![endif]-->
</head>
<body>
<div id="todoapp">
<header>
<h1>Todos</h1>
<input id="new-todo" type="text" placeholder="What needs to be done?" data-weave="widget/create">
<section id="todoapp">
<header id="header">
<h1>todos</h1>
<input id="new-todo" placeholder="What needs to be done?" autofocus data-weave="widget/create">
</header>
<!-- this section is hidden by default and you be shown when there are todos and hidden when not -->
<!-- This section should be hidden by default and shown when there are todos -->
<section id="main" data-weave="widget/display">
<input id="toggle-all" type="checkbox" data-weave="widget/mark">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list" data-weave="widget/list"></ul>
</section>
<!-- This footer should hidden by default and shown when there are todos -->
<footer id="footer" data-weave="widget/display">
<div id="todo-count" data-weave="widget/count"></div>
<a id="clear-completed" data-weave="widget/clear">Clear completed</a>
<!-- This should be `0 items left` by default -->
<span id="todo-count" data-weave="widget/count"><strong>0</strong> items left</span>
<!-- Remove this if you don't implement routing -->
<ul id="filters" data-weave="widget/filters">
<li>
<a href="#/">All</a>
</li>
<li>
<a href="#/active">Active</a>
</li>
<li>
<a href="#/completed">Completed</a>
</li>
</ul>
<button id="clear-completed" data-weave="widget/clear">Clear completed (0)</button>
</footer>
</div>
<div id="instructions">
Double click to edit a todo.
</div>
<div id="credits">
<p>Created by <a href="http://troopjs.com/">Mikael Karon</a>.</p>
<p>Part of <a href="http://todomvc.com/">TodoMVC</a>.</p>
</div>
<!-- scripts here -->
<script type="text/javascript" data-main="js/app.js" src="js/lib/require.js"></script>
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Template by <a href="http://github.com/sindresorhus">Sindre Sorhus</a></p>
<!-- Change this out with your name and url ↓ -->
<p>Created by <a href="http://github.com/mikaelkaron">Mikael Karon</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<!-- Scripts here. Don't remove this ↓ -->
<script src="../../../assets/base.js"></script>
<script type="text/javascript" data-main="js/app.js" src="../../../assets/require.min.js"></script>
</body>
</html>
require({
"baseUrl" : "js",
"paths" : {
"jquery" : "lib/jquery",
"troopjs-bundle" : "lib/troopjs-bundle"
"jquery" : "../../../../assets/jquery.min",
"troopjs-bundle" : "lib/troopjs-bundle.min"
},
"priority": [ "jquery", "config", "troopjs-bundle" ]
}, [ "jquery" ], function App(jQuery) {
jQuery(document).ready(function ready($) {
$(this.body).find("[data-weave]").weave();
}, [ "require", "jquery", "troopjs-bundle" ], function Deps(parentRequire, jQuery) {
// Application and plug-ins
parentRequire([
"widget/application",
"troopjs-jquery/weave",
"troopjs-jquery/destroy",
"troopjs-jquery/hashchange",
"troopjs-jquery/action" ], function App(Application) {
// Hook ready
jQuery(document).ready(function ready($) {
Application($(this.body), "app/todos").start();
});
});
});
This source diff could not be displayed because it is too large. You can view the blob instead.
/** vim: et:ts=4:sw=4:sts=4
* @license RequireJS 1.0.7 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
* Available via the MIT or new BSD license.
* see: http://github.com/jrburke/requirejs for details
*/
/*jslint strict: false, plusplus: false, sub: true */
/*global window, navigator, document, importScripts, jQuery, setTimeout, opera */
var requirejs, require, define;
(function () {
//Change this version number for each release.
var version = "1.0.7",
commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
cjsRequireRegExp = /require\(\s*["']([^'"\s]+)["']\s*\)/g,
currDirRegExp = /^\.\//,
jsSuffixRegExp = /\.js$/,
ostring = Object.prototype.toString,
ap = Array.prototype,
aps = ap.slice,
apsp = ap.splice,
isBrowser = !!(typeof window !== "undefined" && navigator && document),
isWebWorker = !isBrowser && typeof importScripts !== "undefined",
//PS3 indicates loaded and complete, but need to wait for complete
//specifically. Sequence is "loading", "loaded", execution,
// then "complete". The UA check is unfortunate, but not sure how
//to feature test w/o causing perf issues.
readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ?
/^complete$/ : /^(complete|loaded)$/,
defContextName = "_",
//Oh the tragedy, detecting opera. See the usage of isOpera for reason.
isOpera = typeof opera !== "undefined" && opera.toString() === "[object Opera]",
empty = {},
contexts = {},
globalDefQueue = [],
interactiveScript = null,
checkLoadedDepth = 0,
useInteractive = false,
reservedDependencies = {
require: true,
module: true,
exports: true
},
req, cfg = {}, currentlyAddingScript, s, head, baseElement, scripts, script,
src, subPath, mainScript, dataMain, globalI, ctx, jQueryCheck, checkLoadedTimeoutId;
function isFunction(it) {
return ostring.call(it) === "[object Function]";
}
function isArray(it) {
return ostring.call(it) === "[object Array]";
}
/**
* Simple function to mix in properties from source into target,
* but only if target does not already have a property of the same name.
* This is not robust in IE for transferring methods that match
* Object.prototype names, but the uses of mixin here seem unlikely to
* trigger a problem related to that.
*/
function mixin(target, source, force) {
for (var prop in source) {
if (!(prop in empty) && (!(prop in target) || force)) {
target[prop] = source[prop];
}
}
return req;
}
/**
* Constructs an error with a pointer to an URL with more information.
* @param {String} id the error ID that maps to an ID on a web page.
* @param {String} message human readable error.
* @param {Error} [err] the original error, if there is one.
*
* @returns {Error}
*/
function makeError(id, msg, err) {
var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id);
if (err) {
e.originalError = err;
}
return e;
}
/**
* Used to set up package paths from a packagePaths or packages config object.
* @param {Object} pkgs the object to store the new package config
* @param {Array} currentPackages an array of packages to configure
* @param {String} [dir] a prefix dir to use.
*/
function configurePackageDir(pkgs, currentPackages, dir) {
var i, location, pkgObj;
for (i = 0; (pkgObj = currentPackages[i]); i++) {
pkgObj = typeof pkgObj === "string" ? { name: pkgObj } : pkgObj;
location = pkgObj.location;
//Add dir to the path, but avoid paths that start with a slash
//or have a colon (indicates a protocol)
if (dir && (!location || (location.indexOf("/") !== 0 && location.indexOf(":") === -1))) {
location = dir + "/" + (location || pkgObj.name);
}
//Create a brand new object on pkgs, since currentPackages can
//be passed in again, and config.pkgs is the internal transformed
//state for all package configs.
pkgs[pkgObj.name] = {
name: pkgObj.name,
location: location || pkgObj.name,
//Remove leading dot in main, so main paths are normalized,
//and remove any trailing .js, since different package
//envs have different conventions: some use a module name,
//some use a file name.
main: (pkgObj.main || "main")
.replace(currDirRegExp, '')
.replace(jsSuffixRegExp, '')
};
}
}
/**
* jQuery 1.4.3-1.5.x use a readyWait/ready() pairing to hold DOM
* ready callbacks, but jQuery 1.6 supports a holdReady() API instead.
* At some point remove the readyWait/ready() support and just stick
* with using holdReady.
*/
function jQueryHoldReady($, shouldHold) {
if ($.holdReady) {
$.holdReady(shouldHold);
} else if (shouldHold) {
$.readyWait += 1;
} else {
$.ready(true);
}
}
if (typeof define !== "undefined") {
//If a define is already in play via another AMD loader,
//do not overwrite.
return;
}
if (typeof requirejs !== "undefined") {
if (isFunction(requirejs)) {
//Do not overwrite and existing requirejs instance.
return;
} else {
cfg = requirejs;
requirejs = undefined;
}
}
//Allow for a require config object
if (typeof require !== "undefined" && !isFunction(require)) {
//assume it is a config object.
cfg = require;
require = undefined;
}
/**
* Creates a new context for use in require and define calls.
* Handle most of the heavy lifting. Do not want to use an object
* with prototype here to avoid using "this" in require, in case it
* needs to be used in more super secure envs that do not want this.
* Also there should not be that many contexts in the page. Usually just
* one for the default context, but could be extra for multiversion cases
* or if a package needs a special context for a dependency that conflicts
* with the standard context.
*/
function newContext(contextName) {
var context, resume,
config = {
waitSeconds: 7,
baseUrl: "./",
paths: {},
pkgs: {},
catchError: {}
},
defQueue = [],
specified = {
"require": true,
"exports": true,
"module": true
},
urlMap = {},
defined = {},
loaded = {},
waiting = {},
waitAry = [],
urlFetched = {},
managerCounter = 0,
managerCallbacks = {},
plugins = {},
//Used to indicate which modules in a build scenario
//need to be full executed.
needFullExec = {},
fullExec = {},
resumeDepth = 0;
/**
* Trims the . and .. from an array of path segments.
* It will keep a leading path segment if a .. will become
* the first path segment, to help with module name lookups,
* which act like paths, but can be remapped. But the end result,
* all paths that use this function should look normalized.
* NOTE: this method MODIFIES the input array.
* @param {Array} ary the array of path segments.
*/
function trimDots(ary) {
var i, part;
for (i = 0; (part = ary[i]); i++) {
if (part === ".") {
ary.splice(i, 1);
i -= 1;
} else if (part === "..") {
if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
//End of the line. Keep at least one non-dot
//path segment at the front so it can be mapped
//correctly to disk. Otherwise, there is likely
//no path mapping for a path starting with '..'.
//This can still fail, but catches the most reasonable
//uses of ..
break;
} else if (i > 0) {
ary.splice(i - 1, 2);
i -= 2;
}
}
}
}
/**
* Given a relative module name, like ./something, normalize it to
* a real name that can be mapped to a path.
* @param {String} name the relative name
* @param {String} baseName a real name that the name arg is relative
* to.
* @returns {String} normalized name
*/
function normalize(name, baseName) {
var pkgName, pkgConfig;
//Adjust any relative paths.
if (name && name.charAt(0) === ".") {
//If have a base name, try to normalize against it,
//otherwise, assume it is a top-level require that will
//be relative to baseUrl in the end.
if (baseName) {
if (config.pkgs[baseName]) {
//If the baseName is a package name, then just treat it as one
//name to concat the name with.
baseName = [baseName];
} else {
//Convert baseName to array, and lop off the last part,
//so that . matches that "directory" and not name of the baseName's
//module. For instance, baseName of "one/two/three", maps to
//"one/two/three.js", but we want the directory, "one/two" for
//this normalization.
baseName = baseName.split("/");
baseName = baseName.slice(0, baseName.length - 1);
}
name = baseName.concat(name.split("/"));
trimDots(name);
//Some use of packages may use a . path to reference the
//"main" module name, so normalize for that.
pkgConfig = config.pkgs[(pkgName = name[0])];
name = name.join("/");
if (pkgConfig && name === pkgName + '/' + pkgConfig.main) {
name = pkgName;
}
} else if (name.indexOf("./") === 0) {
// No baseName, so this is ID is resolved relative
// to baseUrl, pull off the leading dot.
name = name.substring(2);
}
}
return name;
}
/**
* Creates a module mapping that includes plugin prefix, module
* name, and path. If parentModuleMap is provided it will
* also normalize the name via require.normalize()
*
* @param {String} name the module name
* @param {String} [parentModuleMap] parent module map
* for the module name, used to resolve relative names.
*
* @returns {Object}
*/
function makeModuleMap(name, parentModuleMap) {
var index = name ? name.indexOf("!") : -1,
prefix = null,
parentName = parentModuleMap ? parentModuleMap.name : null,
originalName = name,
normalizedName, url, pluginModule;
if (index !== -1) {
prefix = name.substring(0, index);
name = name.substring(index + 1, name.length);
}
if (prefix) {
prefix = normalize(prefix, parentName);
}
//Account for relative paths if there is a base name.
if (name) {
if (prefix) {
pluginModule = defined[prefix];
if (pluginModule && pluginModule.normalize) {
//Plugin is loaded, use its normalize method.
normalizedName = pluginModule.normalize(name, function (name) {
return normalize(name, parentName);
});
} else {
normalizedName = normalize(name, parentName);
}
} else {
//A regular module.
normalizedName = normalize(name, parentName);
url = urlMap[normalizedName];
if (!url) {
//Calculate url for the module, if it has a name.
//Use name here since nameToUrl also calls normalize,
//and for relative names that are outside the baseUrl
//this causes havoc. Was thinking of just removing
//parentModuleMap to avoid extra normalization, but
//normalize() still does a dot removal because of
//issue #142, so just pass in name here and redo
//the normalization. Paths outside baseUrl are just
//messy to support.
url = context.nameToUrl(name, null, parentModuleMap);
//Store the URL mapping for later.
urlMap[normalizedName] = url;
}
}
}
return {
prefix: prefix,
name: normalizedName,
parentMap: parentModuleMap,
url: url,
originalName: originalName,
fullName: prefix ? prefix + "!" + (normalizedName || '') : normalizedName
};
}
/**
* Determine if priority loading is done. If so clear the priorityWait
*/
function isPriorityDone() {
var priorityDone = true,
priorityWait = config.priorityWait,
priorityName, i;
if (priorityWait) {
for (i = 0; (priorityName = priorityWait[i]); i++) {
if (!loaded[priorityName]) {
priorityDone = false;
break;
}
}
if (priorityDone) {
delete config.priorityWait;
}
}
return priorityDone;
}
function makeContextModuleFunc(func, relModuleMap, enableBuildCallback) {
return function () {
//A version of a require function that passes a moduleName
//value for items that may need to
//look up paths relative to the moduleName
var args = aps.call(arguments, 0), lastArg;
if (enableBuildCallback &&
isFunction((lastArg = args[args.length - 1]))) {
lastArg.__requireJsBuild = true;
}
args.push(relModuleMap);
return func.apply(null, args);
};
}
/**
* Helper function that creates a require function object to give to
* modules that ask for it as a dependency. It needs to be specific
* per module because of the implication of path mappings that may
* need to be relative to the module name.
*/
function makeRequire(relModuleMap, enableBuildCallback, altRequire) {
var modRequire = makeContextModuleFunc(altRequire || context.require, relModuleMap, enableBuildCallback);
mixin(modRequire, {
nameToUrl: makeContextModuleFunc(context.nameToUrl, relModuleMap),
toUrl: makeContextModuleFunc(context.toUrl, relModuleMap),
defined: makeContextModuleFunc(context.requireDefined, relModuleMap),
specified: makeContextModuleFunc(context.requireSpecified, relModuleMap),
isBrowser: req.isBrowser
});
return modRequire;
}
/*
* Queues a dependency for checking after the loader is out of a
* "paused" state, for example while a script file is being loaded
* in the browser, where it may have many modules defined in it.
*/
function queueDependency(manager) {
context.paused.push(manager);
}
function execManager(manager) {
var i, ret, err, errFile, errModuleTree,
cb = manager.callback,
map = manager.map,
fullName = map.fullName,
args = manager.deps,
listeners = manager.listeners,
cjsModule;
//Call the callback to define the module, if necessary.
if (cb && isFunction(cb)) {
if (config.catchError.define) {
try {
ret = req.execCb(fullName, manager.callback, args, defined[fullName]);
} catch (e) {
err = e;
}
} else {
ret = req.execCb(fullName, manager.callback, args, defined[fullName]);
}
if (fullName) {
//If setting exports via "module" is in play,
//favor that over return value and exports. After that,
//favor a non-undefined return value over exports use.
cjsModule = manager.cjsModule;
if (cjsModule &&
cjsModule.exports !== undefined &&
//Make sure it is not already the exports value
cjsModule.exports !== defined[fullName]) {
ret = defined[fullName] = manager.cjsModule.exports;
} else if (ret === undefined && manager.usingExports) {
//exports already set the defined value.
ret = defined[fullName];
} else {
//Use the return value from the function.
defined[fullName] = ret;
//If this module needed full execution in a build
//environment, mark that now.
if (needFullExec[fullName]) {
fullExec[fullName] = true;
}
}
}
} else if (fullName) {
//May just be an object definition for the module. Only
//worry about defining if have a module name.
ret = defined[fullName] = cb;
//If this module needed full execution in a build
//environment, mark that now.
if (needFullExec[fullName]) {
fullExec[fullName] = true;
}
}
//Clean up waiting. Do this before error calls, and before
//calling back listeners, so that bookkeeping is correct
//in the event of an error and error is reported in correct order,
//since the listeners will likely have errors if the
//onError function does not throw.
if (waiting[manager.id]) {
delete waiting[manager.id];
manager.isDone = true;
context.waitCount -= 1;
if (context.waitCount === 0) {
//Clear the wait array used for cycles.
waitAry = [];
}
}
//Do not need to track manager callback now that it is defined.
delete managerCallbacks[fullName];
//Allow instrumentation like the optimizer to know the order
//of modules executed and their dependencies.
if (req.onResourceLoad && !manager.placeholder) {
req.onResourceLoad(context, map, manager.depArray);
}
if (err) {
errFile = (fullName ? makeModuleMap(fullName).url : '') ||
err.fileName || err.sourceURL;
errModuleTree = err.moduleTree;
err = makeError('defineerror', 'Error evaluating ' +
'module "' + fullName + '" at location "' +
errFile + '":\n' +
err + '\nfileName:' + errFile +
'\nlineNumber: ' + (err.lineNumber || err.line), err);
err.moduleName = fullName;
err.moduleTree = errModuleTree;
return req.onError(err);
}
//Let listeners know of this manager's value.
for (i = 0; (cb = listeners[i]); i++) {
cb(ret);
}
return undefined;
}
/**
* Helper that creates a callack function that is called when a dependency
* is ready, and sets the i-th dependency for the manager as the
* value passed to the callback generated by this function.
*/
function makeArgCallback(manager, i) {
return function (value) {
//Only do the work if it has not been done
//already for a dependency. Cycle breaking
//logic in forceExec could mean this function
//is called more than once for a given dependency.
if (!manager.depDone[i]) {
manager.depDone[i] = true;
manager.deps[i] = value;
manager.depCount -= 1;
if (!manager.depCount) {
//All done, execute!
execManager(manager);
}
}
};
}
function callPlugin(pluginName, depManager) {
var map = depManager.map,
fullName = map.fullName,
name = map.name,
plugin = plugins[pluginName] ||
(plugins[pluginName] = defined[pluginName]),
load;
//No need to continue if the manager is already
//in the process of loading.
if (depManager.loading) {
return;
}
depManager.loading = true;
load = function (ret) {
depManager.callback = function () {
return ret;
};
execManager(depManager);
loaded[depManager.id] = true;
//The loading of this plugin
//might have placed other things
//in the paused queue. In particular,
//a loader plugin that depends on
//a different plugin loaded resource.
resume();
};
//Allow plugins to load other code without having to know the
//context or how to "complete" the load.
load.fromText = function (moduleName, text) {
/*jslint evil: true */
var hasInteractive = useInteractive;
//Indicate a the module is in process of loading.
loaded[moduleName] = false;
context.scriptCount += 1;
//Indicate this is not a "real" module, so do not track it
//for builds, it does not map to a real file.
context.fake[moduleName] = true;
//Turn off interactive script matching for IE for any define
//calls in the text, then turn it back on at the end.
if (hasInteractive) {
useInteractive = false;
}
req.exec(text);
if (hasInteractive) {
useInteractive = true;
}
//Support anonymous modules.
context.completeLoad(moduleName);
};
//No need to continue if the plugin value has already been
//defined by a build.
if (fullName in defined) {
load(defined[fullName]);
} else {
//Use parentName here since the plugin's name is not reliable,
//could be some weird string with no path that actually wants to
//reference the parentName's path.
plugin.load(name, makeRequire(map.parentMap, true, function (deps, cb) {
var moduleDeps = [],
i, dep, depMap;
//Convert deps to full names and hold on to them
//for reference later, when figuring out if they
//are blocked by a circular dependency.
for (i = 0; (dep = deps[i]); i++) {
depMap = makeModuleMap(dep, map.parentMap);
deps[i] = depMap.fullName;
if (!depMap.prefix) {
moduleDeps.push(deps[i]);
}
}
depManager.moduleDeps = (depManager.moduleDeps || []).concat(moduleDeps);
return context.require(deps, cb);
}), load, config);
}
}
/**
* Adds the manager to the waiting queue. Only fully
* resolved items should be in the waiting queue.
*/
function addWait(manager) {
if (!waiting[manager.id]) {
waiting[manager.id] = manager;
waitAry.push(manager);
context.waitCount += 1;
}
}
/**
* Function added to every manager object. Created out here
* to avoid new function creation for each manager instance.
*/
function managerAdd(cb) {
this.listeners.push(cb);
}
function getManager(map, shouldQueue) {
var fullName = map.fullName,
prefix = map.prefix,
plugin = prefix ? plugins[prefix] ||
(plugins[prefix] = defined[prefix]) : null,
manager, created, pluginManager, prefixMap;
if (fullName) {
manager = managerCallbacks[fullName];
}
if (!manager) {
created = true;
manager = {
//ID is just the full name, but if it is a plugin resource
//for a plugin that has not been loaded,
//then add an ID counter to it.
id: (prefix && !plugin ?
(managerCounter++) + '__p@:' : '') +
(fullName || '__r@' + (managerCounter++)),
map: map,
depCount: 0,
depDone: [],
depCallbacks: [],
deps: [],
listeners: [],
add: managerAdd
};
specified[manager.id] = true;
//Only track the manager/reuse it if this is a non-plugin
//resource. Also only track plugin resources once
//the plugin has been loaded, and so the fullName is the
//true normalized value.
if (fullName && (!prefix || plugins[prefix])) {
managerCallbacks[fullName] = manager;
}
}
//If there is a plugin needed, but it is not loaded,
//first load the plugin, then continue on.
if (prefix && !plugin) {
prefixMap = makeModuleMap(prefix);
//Clear out defined and urlFetched if the plugin was previously
//loaded/defined, but not as full module (as in a build
//situation). However, only do this work if the plugin is in
//defined but does not have a module export value.
if (prefix in defined && !defined[prefix]) {
delete defined[prefix];
delete urlFetched[prefixMap.url];
}
pluginManager = getManager(prefixMap, true);
pluginManager.add(function (plugin) {
//Create a new manager for the normalized
//resource ID and have it call this manager when
//done.
var newMap = makeModuleMap(map.originalName, map.parentMap),
normalizedManager = getManager(newMap, true);
//Indicate this manager is a placeholder for the real,
//normalized thing. Important for when trying to map
//modules and dependencies, for instance, in a build.
manager.placeholder = true;
normalizedManager.add(function (resource) {
manager.callback = function () {
return resource;
};
execManager(manager);
});
});
} else if (created && shouldQueue) {
//Indicate the resource is not loaded yet if it is to be
//queued.
loaded[manager.id] = false;
queueDependency(manager);
addWait(manager);
}
return manager;
}
function main(inName, depArray, callback, relModuleMap) {
var moduleMap = makeModuleMap(inName, relModuleMap),
name = moduleMap.name,
fullName = moduleMap.fullName,
manager = getManager(moduleMap),
id = manager.id,
deps = manager.deps,
i, depArg, depName, depPrefix, cjsMod;
if (fullName) {
//If module already defined for context, or already loaded,
//then leave. Also leave if jQuery is registering but it does
//not match the desired version number in the config.
if (fullName in defined || loaded[id] === true ||
(fullName === "jquery" && config.jQuery &&
config.jQuery !== callback().fn.jquery)) {
return;
}
//Set specified/loaded here for modules that are also loaded
//as part of a layer, where onScriptLoad is not fired
//for those cases. Do this after the inline define and
//dependency tracing is done.
specified[id] = true;
loaded[id] = true;
//If module is jQuery set up delaying its dom ready listeners.
if (fullName === "jquery" && callback) {
jQueryCheck(callback());
}
}
//Attach real depArray and callback to the manager. Do this
//only if the module has not been defined already, so do this after
//the fullName checks above. IE can call main() more than once
//for a module.
manager.depArray = depArray;
manager.callback = callback;
//Add the dependencies to the deps field, and register for callbacks
//on the dependencies.
for (i = 0; i < depArray.length; i++) {
depArg = depArray[i];
//There could be cases like in IE, where a trailing comma will
//introduce a null dependency, so only treat a real dependency
//value as a dependency.
if (depArg) {
//Split the dependency name into plugin and name parts
depArg = makeModuleMap(depArg, (name ? moduleMap : relModuleMap));
depName = depArg.fullName;
depPrefix = depArg.prefix;
//Fix the name in depArray to be just the name, since
//that is how it will be called back later.
depArray[i] = depName;
//Fast path CommonJS standard dependencies.
if (depName === "require") {
deps[i] = makeRequire(moduleMap);
} else if (depName === "exports") {
//CommonJS module spec 1.1
deps[i] = defined[fullName] = {};
manager.usingExports = true;
} else if (depName === "module") {
//CommonJS module spec 1.1
manager.cjsModule = cjsMod = deps[i] = {
id: name,
uri: name ? context.nameToUrl(name, null, relModuleMap) : undefined,
exports: defined[fullName]
};
} else if (depName in defined && !(depName in waiting) &&
(!(fullName in needFullExec) ||
(fullName in needFullExec && fullExec[depName]))) {
//Module already defined, and not in a build situation
//where the module is a something that needs full
//execution and this dependency has not been fully
//executed. See r.js's requirePatch.js for more info
//on fullExec.
deps[i] = defined[depName];
} else {
//Mark this dependency as needing full exec if
//the current module needs full exec.
if (fullName in needFullExec) {
needFullExec[depName] = true;
//Reset state so fully executed code will get
//picked up correctly.
delete defined[depName];
urlFetched[depArg.url] = false;
}
//Either a resource that is not loaded yet, or a plugin
//resource for either a plugin that has not
//loaded yet.
manager.depCount += 1;
manager.depCallbacks[i] = makeArgCallback(manager, i);
getManager(depArg, true).add(manager.depCallbacks[i]);
}
}
}
//Do not bother tracking the manager if it is all done.
if (!manager.depCount) {
//All done, execute!
execManager(manager);
} else {
addWait(manager);
}
}
/**
* Convenience method to call main for a define call that was put on
* hold in the defQueue.
*/
function callDefMain(args) {
main.apply(null, args);
}
/**
* jQuery 1.4.3+ supports ways to hold off calling
* calling jQuery ready callbacks until all scripts are loaded. Be sure
* to track it if the capability exists.. Also, since jQuery 1.4.3 does
* not register as a module, need to do some global inference checking.
* Even if it does register as a module, not guaranteed to be the precise
* name of the global. If a jQuery is tracked for this context, then go
* ahead and register it as a module too, if not already in process.
*/
jQueryCheck = function (jqCandidate) {
if (!context.jQuery) {
var $ = jqCandidate || (typeof jQuery !== "undefined" ? jQuery : null);
if ($) {
//If a specific version of jQuery is wanted, make sure to only
//use this jQuery if it matches.
if (config.jQuery && $.fn.jquery !== config.jQuery) {
return;
}
if ("holdReady" in $ || "readyWait" in $) {
context.jQuery = $;
//Manually create a "jquery" module entry if not one already
//or in process. Note this could trigger an attempt at
//a second jQuery registration, but does no harm since
//the first one wins, and it is the same value anyway.
callDefMain(["jquery", [], function () {
return jQuery;
}]);
//Ask jQuery to hold DOM ready callbacks.
if (context.scriptCount) {
jQueryHoldReady($, true);
context.jQueryIncremented = true;
}
}
}
}
};
function findCycle(manager, traced) {
var fullName = manager.map.fullName,
depArray = manager.depArray,
fullyLoaded = true,
i, depName, depManager, result;
if (manager.isDone || !fullName || !loaded[fullName]) {
return result;
}
//Found the cycle.
if (traced[fullName]) {
return manager;
}
traced[fullName] = true;
//Trace through the dependencies.
if (depArray) {
for (i = 0; i < depArray.length; i++) {
//Some array members may be null, like if a trailing comma
//IE, so do the explicit [i] access and check if it has a value.
depName = depArray[i];
if (!loaded[depName] && !reservedDependencies[depName]) {
fullyLoaded = false;
break;
}
depManager = waiting[depName];
if (depManager && !depManager.isDone && loaded[depName]) {
result = findCycle(depManager, traced);
if (result) {
break;
}
}
}
if (!fullyLoaded) {
//Discard the cycle that was found, since it cannot
//be forced yet. Also clear this module from traced.
result = undefined;
delete traced[fullName];
}
}
return result;
}
function forceExec(manager, traced) {
var fullName = manager.map.fullName,
depArray = manager.depArray,
i, depName, depManager, prefix, prefixManager, value;
if (manager.isDone || !fullName || !loaded[fullName]) {
return undefined;
}
if (fullName) {
if (traced[fullName]) {
return defined[fullName];
}
traced[fullName] = true;
}
//Trace through the dependencies.
if (depArray) {
for (i = 0; i < depArray.length; i++) {
//Some array members may be null, like if a trailing comma
//IE, so do the explicit [i] access and check if it has a value.
depName = depArray[i];
if (depName) {
//First, make sure if it is a plugin resource that the
//plugin is not blocked.
prefix = makeModuleMap(depName).prefix;
if (prefix && (prefixManager = waiting[prefix])) {
forceExec(prefixManager, traced);
}
depManager = waiting[depName];
if (depManager && !depManager.isDone && loaded[depName]) {
value = forceExec(depManager, traced);
manager.depCallbacks[i](value);
}
}
}
}
return defined[fullName];
}
/**
* Checks if all modules for a context are loaded, and if so, evaluates the
* new ones in right dependency order.
*
* @private
*/
function checkLoaded() {
var waitInterval = config.waitSeconds * 1000,
//It is possible to disable the wait interval by using waitSeconds of 0.
expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(),
noLoads = "", hasLoadedProp = false, stillLoading = false,
cycleDeps = [],
i, prop, err, manager, cycleManager, moduleDeps;
//If there are items still in the paused queue processing wait.
//This is particularly important in the sync case where each paused
//item is processed right away but there may be more waiting.
if (context.pausedCount > 0) {
return undefined;
}
//Determine if priority loading is done. If so clear the priority. If
//not, then do not check
if (config.priorityWait) {
if (isPriorityDone()) {
//Call resume, since it could have
//some waiting dependencies to trace.
resume();
} else {
return undefined;
}
}
//See if anything is still in flight.
for (prop in loaded) {
if (!(prop in empty)) {
hasLoadedProp = true;
if (!loaded[prop]) {
if (expired) {
noLoads += prop + " ";
} else {
stillLoading = true;
if (prop.indexOf('!') === -1) {
//No reason to keep looking for unfinished
//loading. If the only stillLoading is a
//plugin resource though, keep going,
//because it may be that a plugin resource
//is waiting on a non-plugin cycle.
cycleDeps = [];
break;
} else {
moduleDeps = managerCallbacks[prop] && managerCallbacks[prop].moduleDeps;
if (moduleDeps) {
cycleDeps.push.apply(cycleDeps, moduleDeps);
}
}
}
}
}
}
//Check for exit conditions.
if (!hasLoadedProp && !context.waitCount) {
//If the loaded object had no items, then the rest of
//the work below does not need to be done.
return undefined;
}
if (expired && noLoads) {
//If wait time expired, throw error of unloaded modules.
err = makeError("timeout", "Load timeout for modules: " + noLoads);
err.requireType = "timeout";
err.requireModules = noLoads;
err.contextName = context.contextName;
return req.onError(err);
}
//If still loading but a plugin is waiting on a regular module cycle
//break the cycle.
if (stillLoading && cycleDeps.length) {
for (i = 0; (manager = waiting[cycleDeps[i]]); i++) {
if ((cycleManager = findCycle(manager, {}))) {
forceExec(cycleManager, {});
break;
}
}
}
//If still waiting on loads, and the waiting load is something
//other than a plugin resource, or there are still outstanding
//scripts, then just try back later.
if (!expired && (stillLoading || context.scriptCount)) {
//Something is still waiting to load. Wait for it, but only
//if a timeout is not already in effect.
if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) {
checkLoadedTimeoutId = setTimeout(function () {
checkLoadedTimeoutId = 0;
checkLoaded();
}, 50);
}
return undefined;
}
//If still have items in the waiting cue, but all modules have
//been loaded, then it means there are some circular dependencies
//that need to be broken.
//However, as a waiting thing is fired, then it can add items to
//the waiting cue, and those items should not be fired yet, so
//make sure to redo the checkLoaded call after breaking a single
//cycle, if nothing else loaded then this logic will pick it up
//again.
if (context.waitCount) {
//Cycle through the waitAry, and call items in sequence.
for (i = 0; (manager = waitAry[i]); i++) {
forceExec(manager, {});
}
//If anything got placed in the paused queue, run it down.
if (context.paused.length) {
resume();
}
//Only allow this recursion to a certain depth. Only
//triggered by errors in calling a module in which its
//modules waiting on it cannot finish loading, or some circular
//dependencies that then may add more dependencies.
//The value of 5 is a bit arbitrary. Hopefully just one extra
//pass, or two for the case of circular dependencies generating
//more work that gets resolved in the sync node case.
if (checkLoadedDepth < 5) {
checkLoadedDepth += 1;
checkLoaded();
}
}
checkLoadedDepth = 0;
//Check for DOM ready, and nothing is waiting across contexts.
req.checkReadyState();
return undefined;
}
/**
* Resumes tracing of dependencies and then checks if everything is loaded.
*/
resume = function () {
var manager, map, url, i, p, args, fullName;
//Any defined modules in the global queue, intake them now.
context.takeGlobalQueue();
resumeDepth += 1;
if (context.scriptCount <= 0) {
//Synchronous envs will push the number below zero with the
//decrement above, be sure to set it back to zero for good measure.
//require() calls that also do not end up loading scripts could
//push the number negative too.
context.scriptCount = 0;
}
//Make sure any remaining defQueue items get properly processed.
while (defQueue.length) {
args = defQueue.shift();
if (args[0] === null) {
return req.onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1]));
} else {
callDefMain(args);
}
}
//Skip the resume of paused dependencies
//if current context is in priority wait.
if (!config.priorityWait || isPriorityDone()) {
while (context.paused.length) {
p = context.paused;
context.pausedCount += p.length;
//Reset paused list
context.paused = [];
for (i = 0; (manager = p[i]); i++) {
map = manager.map;
url = map.url;
fullName = map.fullName;
//If the manager is for a plugin managed resource,
//ask the plugin to load it now.
if (map.prefix) {
callPlugin(map.prefix, manager);
} else {
//Regular dependency.
if (!urlFetched[url] && !loaded[fullName]) {
req.load(context, fullName, url);
//Mark the URL as fetched, but only if it is
//not an empty: URL, used by the optimizer.
//In that case we need to be sure to call
//load() for each module that is mapped to
//empty: so that dependencies are satisfied
//correctly.
if (url.indexOf('empty:') !== 0) {
urlFetched[url] = true;
}
}
}
}
//Move the start time for timeout forward.
context.startTime = (new Date()).getTime();
context.pausedCount -= p.length;
}
}
//Only check if loaded when resume depth is 1. It is likely that
//it is only greater than 1 in sync environments where a factory
//function also then calls the callback-style require. In those
//cases, the checkLoaded should not occur until the resume
//depth is back at the top level.
if (resumeDepth === 1) {
checkLoaded();
}
resumeDepth -= 1;
return undefined;
};
//Define the context object. Many of these fields are on here
//just to make debugging easier.
context = {
contextName: contextName,
config: config,
defQueue: defQueue,
waiting: waiting,
waitCount: 0,
specified: specified,
loaded: loaded,
urlMap: urlMap,
urlFetched: urlFetched,
scriptCount: 0,
defined: defined,
paused: [],
pausedCount: 0,
plugins: plugins,
needFullExec: needFullExec,
fake: {},
fullExec: fullExec,
managerCallbacks: managerCallbacks,
makeModuleMap: makeModuleMap,
normalize: normalize,
/**
* Set a configuration for the context.
* @param {Object} cfg config object to integrate.
*/
configure: function (cfg) {
var paths, prop, packages, pkgs, packagePaths, requireWait;
//Make sure the baseUrl ends in a slash.
if (cfg.baseUrl) {
if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== "/") {
cfg.baseUrl += "/";
}
}
//Save off the paths and packages since they require special processing,
//they are additive.
paths = config.paths;
packages = config.packages;
pkgs = config.pkgs;
//Mix in the config values, favoring the new values over
//existing ones in context.config.
mixin(config, cfg, true);
//Adjust paths if necessary.
if (cfg.paths) {
for (prop in cfg.paths) {
if (!(prop in empty)) {
paths[prop] = cfg.paths[prop];
}
}
config.paths = paths;
}
packagePaths = cfg.packagePaths;
if (packagePaths || cfg.packages) {
//Convert packagePaths into a packages config.
if (packagePaths) {
for (prop in packagePaths) {
if (!(prop in empty)) {
configurePackageDir(pkgs, packagePaths[prop], prop);
}
}
}
//Adjust packages if necessary.
if (cfg.packages) {
configurePackageDir(pkgs, cfg.packages);
}
//Done with modifications, assing packages back to context config
config.pkgs = pkgs;
}
//If priority loading is in effect, trigger the loads now
if (cfg.priority) {
//Hold on to requireWait value, and reset it after done
requireWait = context.requireWait;
//Allow tracing some require calls to allow the fetching
//of the priority config.
context.requireWait = false;
//But first, call resume to register any defined modules that may
//be in a data-main built file before the priority config
//call.
resume();
context.require(cfg.priority);
//Trigger a resume right away, for the case when
//the script with the priority load is done as part
//of a data-main call. In that case the normal resume
//call will not happen because the scriptCount will be
//at 1, since the script for data-main is being processed.
resume();
//Restore previous state.
context.requireWait = requireWait;
config.priorityWait = cfg.priority;
}
//If a deps array or a config callback is specified, then call
//require with those args. This is useful when require is defined as a
//config object before require.js is loaded.
if (cfg.deps || cfg.callback) {
context.require(cfg.deps || [], cfg.callback);
}
},
requireDefined: function (moduleName, relModuleMap) {
return makeModuleMap(moduleName, relModuleMap).fullName in defined;
},
requireSpecified: function (moduleName, relModuleMap) {
return makeModuleMap(moduleName, relModuleMap).fullName in specified;
},
require: function (deps, callback, relModuleMap) {
var moduleName, fullName, moduleMap;
if (typeof deps === "string") {
if (isFunction(callback)) {
//Invalid call
return req.onError(makeError("requireargs", "Invalid require call"));
}
//Synchronous access to one module. If require.get is
//available (as in the Node adapter), prefer that.
//In this case deps is the moduleName and callback is
//the relModuleMap
if (req.get) {
return req.get(context, deps, callback);
}
//Just return the module wanted. In this scenario, the
//second arg (if passed) is just the relModuleMap.
moduleName = deps;
relModuleMap = callback;
//Normalize module name, if it contains . or ..
moduleMap = makeModuleMap(moduleName, relModuleMap);
fullName = moduleMap.fullName;
if (!(fullName in defined)) {
return req.onError(makeError("notloaded", "Module name '" +
moduleMap.fullName +
"' has not been loaded yet for context: " +
contextName));
}
return defined[fullName];
}
//Call main but only if there are dependencies or
//a callback to call.
if (deps && deps.length || callback) {
main(null, deps, callback, relModuleMap);
}
//If the require call does not trigger anything new to load,
//then resume the dependency processing.
if (!context.requireWait) {
while (!context.scriptCount && context.paused.length) {
resume();
}
}
return context.require;
},
/**
* Internal method to transfer globalQueue items to this context's
* defQueue.
*/
takeGlobalQueue: function () {
//Push all the globalDefQueue items into the context's defQueue
if (globalDefQueue.length) {
//Array splice in the values since the context code has a
//local var ref to defQueue, so cannot just reassign the one
//on context.
apsp.apply(context.defQueue,
[context.defQueue.length - 1, 0].concat(globalDefQueue));
globalDefQueue = [];
}
},
/**
* Internal method used by environment adapters to complete a load event.
* A load event could be a script load or just a load pass from a synchronous
* load call.
* @param {String} moduleName the name of the module to potentially complete.
*/
completeLoad: function (moduleName) {
var args;
context.takeGlobalQueue();
while (defQueue.length) {
args = defQueue.shift();
if (args[0] === null) {
args[0] = moduleName;
break;
} else if (args[0] === moduleName) {
//Found matching define call for this script!
break;
} else {
//Some other named define call, most likely the result
//of a build layer that included many define calls.
callDefMain(args);
args = null;
}
}
if (args) {
callDefMain(args);
} else {
//A script that does not call define(), so just simulate
//the call for it. Special exception for jQuery dynamic load.
callDefMain([moduleName, [],
moduleName === "jquery" && typeof jQuery !== "undefined" ?
function () {
return jQuery;
} : null]);
}
//Doing this scriptCount decrement branching because sync envs
//need to decrement after resume, otherwise it looks like
//loading is complete after the first dependency is fetched.
//For browsers, it works fine to decrement after, but it means
//the checkLoaded setTimeout 50 ms cost is taken. To avoid
//that cost, decrement beforehand.
if (req.isAsync) {
context.scriptCount -= 1;
}
resume();
if (!req.isAsync) {
context.scriptCount -= 1;
}
},
/**
* Converts a module name + .extension into an URL path.
* *Requires* the use of a module name. It does not support using
* plain URLs like nameToUrl.
*/
toUrl: function (moduleNamePlusExt, relModuleMap) {
var index = moduleNamePlusExt.lastIndexOf("."),
ext = null;
if (index !== -1) {
ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length);
moduleNamePlusExt = moduleNamePlusExt.substring(0, index);
}
return context.nameToUrl(moduleNamePlusExt, ext, relModuleMap);
},
/**
* Converts a module name to a file path. Supports cases where
* moduleName may actually be just an URL.
*/
nameToUrl: function (moduleName, ext, relModuleMap) {
var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url,
config = context.config;
//Normalize module name if have a base relative module name to work from.
moduleName = normalize(moduleName, relModuleMap && relModuleMap.fullName);
//If a colon is in the URL, it indicates a protocol is used and it is just
//an URL to a file, or if it starts with a slash or ends with .js, it is just a plain file.
//The slash is important for protocol-less URLs as well as full paths.
if (req.jsExtRegExp.test(moduleName)) {
//Just a plain path, not module name lookup, so just return it.
//Add extension if it is included. This is a bit wonky, only non-.js things pass
//an extension, this method probably needs to be reworked.
url = moduleName + (ext ? ext : "");
} else {
//A module that needs to be converted to a path.
paths = config.paths;
pkgs = config.pkgs;
syms = moduleName.split("/");
//For each module name segment, see if there is a path
//registered for it. Start with most specific name
//and work up from it.
for (i = syms.length; i > 0; i--) {
parentModule = syms.slice(0, i).join("/");
if (paths[parentModule]) {
syms.splice(0, i, paths[parentModule]);
break;
} else if ((pkg = pkgs[parentModule])) {
//If module name is just the package name, then looking
//for the main module.
if (moduleName === pkg.name) {
pkgPath = pkg.location + '/' + pkg.main;
} else {
pkgPath = pkg.location;
}
syms.splice(0, i, pkgPath);
break;
}
}
//Join the path parts together, then figure out if baseUrl is needed.
url = syms.join("/") + (ext || ".js");
url = (url.charAt(0) === '/' || url.match(/^\w+:/) ? "" : config.baseUrl) + url;
}
return config.urlArgs ? url +
((url.indexOf('?') === -1 ? '?' : '&') +
config.urlArgs) : url;
}
};
//Make these visible on the context so can be called at the very
//end of the file to bootstrap
context.jQueryCheck = jQueryCheck;
context.resume = resume;
return context;
}
/**
* Main entry point.
*
* If the only argument to require is a string, then the module that
* is represented by that string is fetched for the appropriate context.
*
* If the first argument is an array, then it will be treated as an array
* of dependency string names to fetch. An optional function callback can
* be specified to execute when all of those dependencies are available.
*
* Make a local req variable to help Caja compliance (it assumes things
* on a require that are not standardized), and to give a short
* name for minification/local scope use.
*/
req = requirejs = function (deps, callback) {
//Find the right context, use default
var contextName = defContextName,
context, config;
// Determine if have config object in the call.
if (!isArray(deps) && typeof deps !== "string") {
// deps is a config object
config = deps;
if (isArray(callback)) {
// Adjust args if there are dependencies
deps = callback;
callback = arguments[2];
} else {
deps = [];
}
}
if (config && config.context) {
contextName = config.context;
}
context = contexts[contextName] ||
(contexts[contextName] = newContext(contextName));
if (config) {
context.configure(config);
}
return context.require(deps, callback);
};
/**
* Support require.config() to make it easier to cooperate with other
* AMD loaders on globally agreed names.
*/
req.config = function (config) {
return req(config);
};
/**
* Export require as a global, but only if it does not already exist.
*/
if (!require) {
require = req;
}
/**
* Global require.toUrl(), to match global require, mostly useful
* for debugging/work in the global space.
*/
req.toUrl = function (moduleNamePlusExt) {
return contexts[defContextName].toUrl(moduleNamePlusExt);
};
req.version = version;
//Used to filter out dependencies that are already paths.
req.jsExtRegExp = /^\/|:|\?|\.js$/;
s = req.s = {
contexts: contexts,
//Stores a list of URLs that should not get async script tag treatment.
skipAsync: {}
};
req.isAsync = req.isBrowser = isBrowser;
if (isBrowser) {
head = s.head = document.getElementsByTagName("head")[0];
//If BASE tag is in play, using appendChild is a problem for IE6.
//When that browser dies, this can be removed. Details in this jQuery bug:
//http://dev.jquery.com/ticket/2709
baseElement = document.getElementsByTagName("base")[0];
if (baseElement) {
head = s.head = baseElement.parentNode;
}
}
/**
* Any errors that require explicitly generates will be passed to this
* function. Intercept/override it if you want custom error handling.
* @param {Error} err the error object.
*/
req.onError = function (err) {
throw err;
};
/**
* Does the request to load a module for the browser case.
* Make this a separate function to allow other environments
* to override it.
*
* @param {Object} context the require context to find state.
* @param {String} moduleName the name of the module.
* @param {Object} url the URL to the module.
*/
req.load = function (context, moduleName, url) {
req.resourcesReady(false);
context.scriptCount += 1;
req.attach(url, context, moduleName);
//If tracking a jQuery, then make sure its ready callbacks
//are put on hold to prevent its ready callbacks from
//triggering too soon.
if (context.jQuery && !context.jQueryIncremented) {
jQueryHoldReady(context.jQuery, true);
context.jQueryIncremented = true;
}
};
function getInteractiveScript() {
var scripts, i, script;
if (interactiveScript && interactiveScript.readyState === 'interactive') {
return interactiveScript;
}
scripts = document.getElementsByTagName('script');
for (i = scripts.length - 1; i > -1 && (script = scripts[i]); i--) {
if (script.readyState === 'interactive') {
return (interactiveScript = script);
}
}
return null;
}
/**
* The function that handles definitions of modules. Differs from
* require() in that a string for the module should be the first argument,
* and the function to execute after dependencies are loaded should
* return a value to define the module corresponding to the first argument's
* name.
*/
define = function (name, deps, callback) {
var node, context;
//Allow for anonymous functions
if (typeof name !== 'string') {
//Adjust args appropriately
callback = deps;
deps = name;
name = null;
}
//This module may not have dependencies
if (!isArray(deps)) {
callback = deps;
deps = [];
}
//If no name, and callback is a function, then figure out if it a
//CommonJS thing with dependencies.
if (!deps.length && isFunction(callback)) {
//Remove comments from the callback string,
//look for require calls, and pull them into the dependencies,
//but only if there are function args.
if (callback.length) {
callback
.toString()
.replace(commentRegExp, "")
.replace(cjsRequireRegExp, function (match, dep) {
deps.push(dep);
});
//May be a CommonJS thing even without require calls, but still
//could use exports, and module. Avoid doing exports and module
//work though if it just needs require.
//REQUIRES the function to expect the CommonJS variables in the
//order listed below.
deps = (callback.length === 1 ? ["require"] : ["require", "exports", "module"]).concat(deps);
}
}
//If in IE 6-8 and hit an anonymous define() call, do the interactive
//work.
if (useInteractive) {
node = currentlyAddingScript || getInteractiveScript();
if (node) {
if (!name) {
name = node.getAttribute("data-requiremodule");
}
context = contexts[node.getAttribute("data-requirecontext")];
}
}
//Always save off evaluating the def call until the script onload handler.
//This allows multiple modules to be in a file without prematurely
//tracing dependencies, and allows for anonymous module support,
//where the module name is not known until the script onload event
//occurs. If no context, use the global queue, and get it processed
//in the onscript load callback.
(context ? context.defQueue : globalDefQueue).push([name, deps, callback]);
return undefined;
};
define.amd = {
multiversion: true,
plugins: true,
jQuery: true
};
/**
* Executes the text. Normally just uses eval, but can be modified
* to use a more environment specific call.
* @param {String} text the text to execute/evaluate.
*/
req.exec = function (text) {
return eval(text);
};
/**
* Executes a module callack function. Broken out as a separate function
* solely to allow the build system to sequence the files in the built
* layer in the right sequence.
*
* @private
*/
req.execCb = function (name, callback, args, exports) {
return callback.apply(exports, args);
};
/**
* Adds a node to the DOM. Public function since used by the order plugin.
* This method should not normally be called by outside code.
*/
req.addScriptToDom = function (node) {
//For some cache cases in IE 6-8, the script executes before the end
//of the appendChild execution, so to tie an anonymous define
//call to the module name (which is stored on the node), hold on
//to a reference to this node, but clear after the DOM insertion.
currentlyAddingScript = node;
if (baseElement) {
head.insertBefore(node, baseElement);
} else {
head.appendChild(node);
}
currentlyAddingScript = null;
};
/**
* callback for script loads, used to check status of loading.
*
* @param {Event} evt the event from the browser for the script
* that was loaded.
*
* @private
*/
req.onScriptLoad = function (evt) {
//Using currentTarget instead of target for Firefox 2.0's sake. Not
//all old browsers will be supported, but this one was easy enough
//to support and still makes sense.
var node = evt.currentTarget || evt.srcElement, contextName, moduleName,
context;
if (evt.type === "load" || (node && readyRegExp.test(node.readyState))) {
//Reset interactive script so a script node is not held onto for
//to long.
interactiveScript = null;
//Pull out the name of the module and the context.
contextName = node.getAttribute("data-requirecontext");
moduleName = node.getAttribute("data-requiremodule");
context = contexts[contextName];
contexts[contextName].completeLoad(moduleName);
//Clean up script binding. Favor detachEvent because of IE9
//issue, see attachEvent/addEventListener comment elsewhere
//in this file.
if (node.detachEvent && !isOpera) {
//Probably IE. If not it will throw an error, which will be
//useful to know.
node.detachEvent("onreadystatechange", req.onScriptLoad);
} else {
node.removeEventListener("load", req.onScriptLoad, false);
}
}
};
/**
* Attaches the script represented by the URL to the current
* environment. Right now only supports browser loading,
* but can be redefined in other environments to do the right thing.
* @param {String} url the url of the script to attach.
* @param {Object} context the context that wants the script.
* @param {moduleName} the name of the module that is associated with the script.
* @param {Function} [callback] optional callback, defaults to require.onScriptLoad
* @param {String} [type] optional type, defaults to text/javascript
* @param {Function} [fetchOnlyFunction] optional function to indicate the script node
* should be set up to fetch the script but do not attach it to the DOM
* so that it can later be attached to execute it. This is a way for the
* order plugin to support ordered loading in IE. Once the script is fetched,
* but not executed, the fetchOnlyFunction will be called.
*/
req.attach = function (url, context, moduleName, callback, type, fetchOnlyFunction) {
var node;
if (isBrowser) {
//In the browser so use a script tag
callback = callback || req.onScriptLoad;
node = context && context.config && context.config.xhtml ?
document.createElementNS("http://www.w3.org/1999/xhtml", "html:script") :
document.createElement("script");
node.type = type || (context && context.config.scriptType) ||
"text/javascript";
node.charset = "utf-8";
//Use async so Gecko does not block on executing the script if something
//like a long-polling comet tag is being run first. Gecko likes
//to evaluate scripts in DOM order, even for dynamic scripts.
//It will fetch them async, but only evaluate the contents in DOM
//order, so a long-polling script tag can delay execution of scripts
//after it. But telling Gecko we expect async gets us the behavior
//we want -- execute it whenever it is finished downloading. Only
//Helps Firefox 3.6+
//Allow some URLs to not be fetched async. Mostly helps the order!
//plugin
node.async = !s.skipAsync[url];
if (context) {
node.setAttribute("data-requirecontext", context.contextName);
}
node.setAttribute("data-requiremodule", moduleName);
//Set up load listener. Test attachEvent first because IE9 has
//a subtle issue in its addEventListener and script onload firings
//that do not match the behavior of all other browsers with
//addEventListener support, which fire the onload event for a
//script right after the script execution. See:
//https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution
//UNFORTUNATELY Opera implements attachEvent but does not follow the script
//script execution mode.
if (node.attachEvent && !isOpera) {
//Probably IE. IE (at least 6-8) do not fire
//script onload right after executing the script, so
//we cannot tie the anonymous define call to a name.
//However, IE reports the script as being in "interactive"
//readyState at the time of the define call.
useInteractive = true;
if (fetchOnlyFunction) {
//Need to use old school onreadystate here since
//when the event fires and the node is not attached
//to the DOM, the evt.srcElement is null, so use
//a closure to remember the node.
node.onreadystatechange = function (evt) {
//Script loaded but not executed.
//Clear loaded handler, set the real one that
//waits for script execution.
if (node.readyState === 'loaded') {
node.onreadystatechange = null;
node.attachEvent("onreadystatechange", callback);
fetchOnlyFunction(node);
}
};
} else {
node.attachEvent("onreadystatechange", callback);
}
} else {
node.addEventListener("load", callback, false);
}
node.src = url;
//Fetch only means waiting to attach to DOM after loaded.
if (!fetchOnlyFunction) {
req.addScriptToDom(node);
}
return node;
} else if (isWebWorker) {
//In a web worker, use importScripts. This is not a very
//efficient use of importScripts, importScripts will block until
//its script is downloaded and evaluated. However, if web workers
//are in play, the expectation that a build has been done so that
//only one script needs to be loaded anyway. This may need to be
//reevaluated if other use cases become common.
importScripts(url);
//Account for anonymous modules
context.completeLoad(moduleName);
}
return null;
};
//Look for a data-main script attribute, which could also adjust the baseUrl.
if (isBrowser) {
//Figure out baseUrl. Get it from the script tag with require.js in it.
scripts = document.getElementsByTagName("script");
for (globalI = scripts.length - 1; globalI > -1 && (script = scripts[globalI]); globalI--) {
//Set the "head" where we can append children by
//using the script's parent.
if (!head) {
head = script.parentNode;
}
//Look for a data-main attribute to set main script for the page
//to load. If it is there, the path to data main becomes the
//baseUrl, if it is not already set.
if ((dataMain = script.getAttribute('data-main'))) {
if (!cfg.baseUrl) {
//Pull off the directory of data-main for use as the
//baseUrl.
src = dataMain.split('/');
mainScript = src.pop();
subPath = src.length ? src.join('/') + '/' : './';
//Set final config.
cfg.baseUrl = subPath;
//Strip off any trailing .js since dataMain is now
//like a module name.
dataMain = mainScript.replace(jsSuffixRegExp, '');
}
//Put the data-main script in the files to load.
cfg.deps = cfg.deps ? cfg.deps.concat(dataMain) : [dataMain];
break;
}
}
}
//See if there is nothing waiting across contexts, and if not, trigger
//resourcesReady.
req.checkReadyState = function () {
var contexts = s.contexts, prop;
for (prop in contexts) {
if (!(prop in empty)) {
if (contexts[prop].waitCount) {
return;
}
}
}
req.resourcesReady(true);
};
/**
* Internal function that is triggered whenever all scripts/resources
* have been loaded by the loader. Can be overridden by other, for
* instance the domReady plugin, which wants to know when all resources
* are loaded.
*/
req.resourcesReady = function (isReady) {
var contexts, context, prop;
//First, set the public variable indicating that resources are loading.
req.resourcesDone = isReady;
if (req.resourcesDone) {
//If jQuery with DOM ready delayed, release it now.
contexts = s.contexts;
for (prop in contexts) {
if (!(prop in empty)) {
context = contexts[prop];
if (context.jQueryIncremented) {
jQueryHoldReady(context.jQuery, false);
context.jQueryIncremented = false;
}
}
}
}
};
//FF < 3.6 readyState fix. Needed so that domReady plugin
//works well in that environment, since require.js is normally
//loaded via an HTML script tag so it will be there before window load,
//where the domReady plugin is more likely to be loaded after window load.
req.pageLoaded = function () {
if (document.readyState !== "complete") {
document.readyState = "complete";
}
};
if (isBrowser) {
if (document.addEventListener) {
if (!document.readyState) {
document.readyState = "loading";
window.addEventListener("load", req.pageLoaded, false);
}
}
}
//Set up default context. If require was a configuration object, use that as base config.
req(cfg);
//If modules are built into require.js, then need to make sure dependencies are
//traced. Use a setTimeout in the browser world, to allow all the modules to register
//themselves. In a non-browser env, assume that modules are not built into require.js,
//which seems odd to do on the server.
if (req.isAsync && typeof setTimeout !== "undefined") {
ctx = s.contexts[(cfg.context || defContextName)];
//Indicate that the script that includes require() is still loading,
//so that require()'d dependencies are not traced until the end of the
//file is parsed (approximated via the setTimeout call).
ctx.requireWait = true;
setTimeout(function () {
ctx.requireWait = false;
if (!ctx.scriptCount) {
ctx.resume();
}
req.checkReadyState();
}, 0);
}
}());
/*
* 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 base component
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/**
* The base trait provides functionality for instance counting,
* configuration and a default 'toString' method.
*/
define('troopjs-core/component/base',[ "compose", "config" ], function ComponentModule(Compose, config) {
var COUNT = 0;
return Compose(function Component() {
this.instanceCount = COUNT++;
}, {
/**
* Application configuration
*/
config : config,
/**
* Generates string representation of this object
* @returns Combination displayName and instanceCount
*/
toString : function toString() {
var self = this;
return (self.displayName || "component") + "@" + self.instanceCount;
}
});
});
/*!
* TroopJS deferred component
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
define('deferred',[ "jquery" ], function DeferredModule($) {
return $.Deferred;
});
/*!
* TroopJS pubsub/topic module
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
define('troopjs-core/pubsub/topic',[ "../component/base" ], function TopicModule(Component) {
var ARRAY = Array;
return Component.extend(function Topic(topic, publisher, parent) {
var self = this;
self.topic = topic;
self.publisher = publisher;
self.parent = parent;
}, {
displayName : "pubsub/topic",
/**
* Generates string representation of this object
* @returns Instance topic
*/
toString : function toString() {
return this.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 iMax;
while (current) {
if (current.constructor === ARRAY) {
for (i = 0, iMax = current.length; i < iMax; i++) {
item = current[i];
current[i] = item.constructor === constructor
? item.trace()
: item;
}
stack += current.join(",");
break;
}
parent = current.parent;
stack += parent
? current.publisher + ":"
: current.publisher;
current = parent;
}
return stack;
}
});
});
/*!
* TroopJS pubsub/hub module
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
define('troopjs-core/pubsub/hub',[ "compose", "../component/base", "./topic" ], function HubModule(Compose, Component, Topic) {
var UNDEFINED = undefined;
var RECURSIVE = "\/" + "**";
return Compose.create(Component, function Hub() {
var self = this;
self.re = /\/\w+(?=\/)|\*{1,2}$/g;
self.context = {};
self.topics = {};
}, {
displayName: "pubsub/hub",
/**
* Subscribe to a topic
*
* @param topic Topic to subscribe to
* @param context (optional) context to scope callbacks to
* @param callback Callback for this topic
* @returns self
*/
subscribe : function subscribe(topic /*, context, callback,* callback, ..*/) {
var self = this;
var topics = self.topics;
var offset = 1;
var length = arguments.length;
var context = arguments[offset];
var head;
var tail;
// No provided context, set default one
if (context instanceof Function) {
context = self.context;
}
// Context was provided, increase offset
else {
offset++;
}
// Do we have handlers
if (topic in topics) {
// Get last handler
tail = topics[topic].tail;
for (; offset < length; offset++) {
// Set last -> last.next -> handler
tail = tail.next = {
"callback" : arguments[offset],
"context" : context
};
}
// Set last handler
topics[topic].tail = tail;
}
// No handlers
else {
// Create head and tail
head = tail = {
"callback" : arguments[offset++],
"context" : context
};
// Loop through arguments
for (; offset < length; offset++) {
// Set last -> last.next -> handler
tail = tail.next = {
"callback" : arguments[offset],
"context" : context
};
}
// Create topic list
topics[topic] = {
"head" : head,
"tail" : tail
};
}
return self;
},
/**
* Unsubscribes from topic
*
* @param topic Topic to unsubscribe from
* @param callback (optional) Callback to unsubscribe, if none
* are provided all callbacks are unsubscribed
* @returns self
*/
unsubscribe : function unsubscribe(topic /*, callback, callback, ..*/) {
var self = this;
var topics = self.topics;
var offset;
var length = arguments.length;
var head;
var previous = null;
var handler;
var callback;
unsubscribe: {
// Fast fail if we don't have subscribers
if (!topic in topics) {
break unsubscribe;
}
// Simply delete list if there is no callback to match
if (length === 1) {
delete topics[topic];
break unsubscribe;
}
// Get head
head = topics[topic].head;
// Loop over remaining arguments
for (offset = 1; offset < length; offset++) {
// Store callback
callback = arguments[offset];
// Get first handler
handler = previous = head;
// Loop through handlers
do {
// Check if this handler should be unlinked
if (handler.callback === callback) {
// 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);
// Delete list if we've deleted all handlers
if (head === UNDEFINED) {
delete topics[topic];
break unsubscribe;
}
}
// Update head and tail
topics[topic] = {
"head" : head,
"tail" : previous
};
}
return self;
},
/**
* Publishes on a topic
*
* @param topic Topic to publish to
* @param arg (optional) Argument
* @returns self
*/
publish : function publish(topic /*, arg, arg, ..*/) {
var self = this;
var topics = self.topics;
var string = topic.constructor === Topic
? topic.toString()
: topic;
var re = self.re;
var candidates = new Array();
var candidate;
var length = 0;
var handler;
// Create initial set of candidates
while (re.test(string)) {
candidates[length++] = string.substr(0, re.lastIndex) + RECURSIVE;
}
// Are sub-topics even possible?
if (length > 0) {
// Copy second to last candidate to last, minus the last
// char
candidates[length] = candidates[length - 1].slice(0, -1);
// Increase length
length++;
}
// Add original topic to the candidates
candidates[length] = string;
// Optimise of no arguments
if (arguments.length === 0) {
// Loop backwards over candidates
do {
// Get candidate
candidate = candidates[length];
// Fail fast if there are no handlers for candidate
if (!(candidate in topics)) {
continue;
}
// Get first handler
handler = topics[candidate].head;
// Loop through handlers
do {
handler.callback.call(handler.context);
} while (handler = handler.next);
} while (length-- > 0);
}
// Optimise for arguments
else {
// Loop backwards over candidates
do {
// Get candidate
candidate = candidates[length];
// Fail fast if there are no handlers for candidate
if (!(candidate in topics)) {
continue;
}
// Get first handler
handler = topics[candidate].head;
// Loop through handlers
do {
handler.callback
.apply(handler.context, arguments);
} while (handler = handler.next);
} while (length-- > 0);
}
return self;
}
});
});
/*!
* TroopJS gadget component
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/**
* The gadget trait provides convenient access to common application
* logic such as pubsub* and ajax
*/
define('troopjs-core/component/gadget',[ "compose", "./base", "../pubsub/hub", "../pubsub/topic", "deferred" ], function GadgetModule(Compose, Component, hub, Topic, Deferred) {
var NULL = null;
var FUNCTION = Function;
var BUILD = "build";
var DESTROY = "destroy";
var RE_SCAN = new RegExp("^(" + [BUILD, DESTROY].join("|") + ")/.+");
var RE_HUB = /^hub\/(.+)/;
var PUBLISH = hub.publish;
var SUBSCRIBE = hub.subscribe;
var UNSUBSCRIBE = hub.unsubscribe;
return Component.extend(function Gadget() {
var self = this;
var builder = NULL;
var destructor = NULL;
var subscriptions = new Array();
Compose.call(self, {
/**
* First scans for build/destroy signatures and pushes them on the stack
* then iterates builders and executes them in reverse order
* @returns self
*/
build : function build() {
var key = NULL;
var value;
var matches;
var current;
// Loop over each property in component
for (key in self) {
// Get value
value = self[key];
// Continue if value is not a function
if (!(value instanceof FUNCTION)) {
continue;
}
// Get matches
matches = RE_SCAN.exec(key);
// Make sure we have matches
if (matches !== NULL) {
// Switch on prefix
switch (matches[1]) {
case BUILD:
// Update next
value.next = builder;
// Update current
builder = value;
break;
case DESTROY:
// Update next
value.next = destructor;
// Update current
destructor = value;
break;
default:
continue;
}
// Update topic
value.topic = key;
// NULL value
self[key] = NULL;
}
}
// Set current
current = builder;
while (current !== NULL) {
current.call(self);
current = current.next;
}
return self;
},
/**
* Iterates destructors and executes them in reverse order
* @returns self
*/
destroy : function iterator() {
var current = destructor;
while (current !== NULL) {
current.call(self);
current = current.next;
}
return self;
},
/**
* Builder for hub subscriptions
* @returns self
*/
"build/hub" : function build() {
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[1];
// Subscribe
hub.subscribe(new Topic(topic, self), self, value);
// Store in subscriptions
subscriptions[subscriptions.length] = [topic, value];
// NULL value
self[key] = NULL;
}
}
return self;
},
/**
* Destructor for hub subscriptions
* @returns self
*/
"destroy/hub": function destroy() {
var subscription;
// Loop over subscriptions
while (subscription = subscriptions.shift()) {
hub.unsubscribe(subscription[0], subscription[1]);
}
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;
}
});
});
/*!
* TroopJS widget component
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/**
* The widget trait provides common UI related logic
*/
define('troopjs-core/component/widget',[ "compose", "./gadget", "jquery" ], function WidgetModule(Compose, Gadget, $) {
var NULL = null;
var FUNCTION = Function;
var ARRAY_PROTO = Array.prototype;
var SLICE = ARRAY_PROTO.slice;
var UNSHIFT = ARRAY_PROTO.unshift;
var RE = /^dom(?::(\w+))?\/([^\.]+(?:\.(.+))?)/;
var REFRESH = "widget/refresh";
var $ELEMENT = "$element";
var ONE = "one";
var BIND = "bind";
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 'op' parameter set
* @param op name of jQuery method call
* @returns {Function} proxied render
*/
function renderProxy(op) {
/**
* Renders contents into element
* @param contents (Function | String) Template/String to render
* @param data (Object) If contents is a template - template data
* @param deferred (Deferred) Deferred (optional)
* @returns self
*/
function render(contents, data, deferred) {
var self = this;
// If contents is a function, call it
if (contents instanceof FUNCTION) {
contents = contents.call(self, data);
}
// otherwise data makes no sense
else {
deferred = data;
}
var $element = self[$ELEMENT];
// Defer render (as weaving it may need to load async)
var deferredRender = $.Deferred(function deferredRender(dfdRender) {
// Call render
op.call($element, contents);
// Weave element
self.weave($element, dfdRender);
})
.done(function renderDone() {
// After render is complete, trigger "widget/refresh" with woven components
$element.trigger(REFRESH, arguments);
});
if (deferred) {
deferredRender.then(deferred.resolve, deferred.reject);
}
return self;
}
return render;
}
return Gadget.extend(function Widget($element, displayName) {
var self = this;
var $proxies = new Array();
// Extend self
Compose.call(self, {
"build/dom" : function build() {
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
$element[matches[2] === ONE ? ONE : BIND](topic, self, value);
// Store in $proxies
$proxies[$proxies.length] = [topic, value];
// NULL value
self[key] = NULL;
}
}
return self;
},
/**
* Destructor for dom events
* @returns self
*/
"destroy/dom" : function destroy() {
var $proxy;
// Loop over subscriptions
while ($proxy = $proxies.shift()) {
$element.unbind($proxy[0], $proxy[1]);
}
return self;
},
"$element" : $element,
"displayName" : displayName || "component/widget"
});
}, {
/**
* Weaves all children of $element
* @param $element (jQuery) Element to weave
* @param deferred (Deferred) Deferred (optional)
* @returns self
*/
weave : function weave($element, deferred) {
$element.find(ATTR_WEAVE).weave(deferred);
return this;
},
/**
* Unweaves all children of $element _and_ self
* @param $element (jQuery) Element to unweave
* @returns self
*/
unweave : function unweave($element) {
$element.find(ATTR_WOVEN).andSelf().unweave();
return this;
},
/**
* Triggers event on $element
* @param $event (jQuery.Event | String) Event to trigger
* @returns self
*/
trigger : function trigger($event) {
var self = this;
self[$ELEMENT].trigger($event, SLICE.call(arguments, 1));
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 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;
var $element = self[$ELEMENT];
// Create deferred for emptying
var emptyDeferred = $.Deferred(function emptyDeferred(dfd) {
// Detach contents
var $contents = $element.contents().detach();
// Trigger refresh
$element.trigger(REFRESH, arguments);
// Get DOM elements
var contents = $contents.get();
// Use timeout in order to yield
setTimeout(function emptyTimeout() {
try {
// Remove elements from DOM
$contents.remove();
// Resolve deferred
dfd.resolve(contents);
}
// If there's an error
catch (e) {
// Reject deferred
dfd.reject(contents);
}
}, 0);
});
// If a deferred was passed, add resolve/reject
if (deferred) {
emptyDeferred.then(deferred.resolve, deferred.reject);
}
return self;
}
});
});
/*!
* TroopJS util/merge module
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
define('troopjs-core/util/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 remote/ajax module
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
define('troopjs-core/remote/ajax',[ "compose", "../component/gadget", "../pubsub/topic", "jquery", "../util/merge" ], function AjaxModule(Compose, Gadget, Topic, $, merge) {
return Compose.create(Gadget, function Ajax() {
// Build
this.build();
}, {
displayName : "remote/ajax",
"hub/ajax" : function request(topic, settings, deferred) {
// Request
$.ajax(merge.call({
"headers": {
"x-request-id": new Date().getTime(),
"x-components": topic.constructor === Topic ? topic.trace() : topic
}
}, settings)).then(deferred.resolve, deferred.reject);
}
});
});
/*!
* TroopJS store/base module
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
define('troopjs-core/store/base',[ "compose", "../component/gadget" ], function StoreModule(Compose, Gadget) {
return Gadget.extend({
set : Compose.required,
get : Compose.required,
remove : Compose.required,
clear : Compose.required
});
});
/*!
* TroopJS store/local module
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
define('troopjs-core/store/local',[ "compose", "./base" ], function StoreLocalModule(Compose, Store) {
// Grab local storage
var STORAGE = window.localStorage;
return Compose.create(Store, {
displayName : "store/local",
set : function set(key, value, deferred) {
// JSON encoded 'value' then store as 'key'
STORAGE.setItem(key, JSON.stringify(value));
// Resolve deferred
if (deferred && deferred.resolve instanceof Function) {
deferred.resolve(value);
}
},
get : function get(key, deferred) {
// Get value from 'key', parse JSON
var value = JSON.parse(STORAGE.getItem(key));
// Resolve deferred
if (deferred && deferred.resolve instanceof Function) {
deferred.resolve(value);
}
},
remove : function remove(key, deferred) {
// Remove key
STORAGE.removeItem(key);
// Resolve deferred
if (deferred && deferred.resolve instanceof Function) {
deferred.resolve();
}
},
clear : function clear(deferred) {
// Clear
STORAGE.clear();
// Resolve deferred
if (deferred && deferred.resolve instanceof Function) {
deferred.resolve();
}
}
});
});
/*!
* TroopJS store/session module
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
define('troopjs-core/store/session',[ "compose", "./base" ], function StoreSessionModule(Compose, Store) {
// Grab session storage
var STORAGE = window.sessionStorage;
return Compose.create(Store, {
displayName : "store/session",
set : function set(key, value, deferred) {
// JSON encoded 'value' then store as 'key'
STORAGE.setItem(key, JSON.stringify(value));
// Resolve deferred
if (deferred && deferred.resolve instanceof Function) {
deferred.resolve(value);
}
},
get : function get(key, deferred) {
// Get value from 'key', parse JSON
var value = JSON.parse(STORAGE.getItem(key));
// Resolve deferred
if (deferred && deferred.resolve instanceof Function) {
deferred.resolve(value);
}
},
remove : function remove(key, deferred) {
// Remove key
STORAGE.removeItem(key);
// Resolve deferred
if (deferred && deferred.resolve instanceof Function) {
deferred.resolve();
}
},
clear : function clear(deferred) {
// Clear
STORAGE.clear();
// Resolve deferred
if (deferred && deferred.resolve instanceof Function) {
deferred.resolve();
}
}
});
});
/**
* @license RequireJS text 1.0.7 Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
* Available via the MIT or new BSD license.
* see: http://github.com/jrburke/requirejs for details
*/
/*jslint regexp: false, nomen: false, plusplus: false, strict: false */
/*global require: false, XMLHttpRequest: false, ActiveXObject: false,
define: false, window: false, process: false, Packages: false,
java: false, location: false */
(function () {
var progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'],
xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im,
bodyRegExp = /<body[^>]*>\s*([\s\S]+)\s*<\/body>/im,
hasLocation = typeof location !== 'undefined' && location.href,
defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''),
defaultHostName = hasLocation && location.hostname,
defaultPort = hasLocation && (location.port || undefined),
buildMap = [];
define('text',[],function () {
var text, get, fs;
if (typeof window !== "undefined" && window.navigator && window.document) {
get = function (url, callback) {
var xhr = text.createXhr();
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);
};
} else if (typeof process !== "undefined" &&
process.versions &&
!!process.versions.node) {
//Using special require.nodeRequire, something added by r.js.
fs = require.nodeRequire('fs');
get = function (url, callback) {
var file = fs.readFileSync(url, 'utf8');
//Remove BOM (Byte Mark Order) from utf8 files if it is there.
if (file.indexOf('\uFEFF') === 0) {
file = file.substring(1);
}
callback(file);
};
} else if (typeof Packages !== 'undefined') {
//Why Java, why is this so awkward?
get = function (url, callback) {
var encoding = "utf-8",
file = new java.io.File(url),
lineSeparator = java.lang.System.getProperty("line.separator"),
input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)),
stringBuffer, line,
content = '';
try {
stringBuffer = new java.lang.StringBuffer();
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);
};
}
text = {
version: '1.0.7',
strip: function (content) {
//Strips <?xml ...?> declarations so that external SVG and XML
//documents can be added to a document without worry. Also, if the string
//is an HTML document, only the part inside the body tag is returned.
if (content) {
content = content.replace(xmlRegExp, "");
var matches = content.match(bodyRegExp);
if (matches) {
content = matches[1];
}
} else {
content = "";
}
return content;
},
jsEscape: function (content) {
return content.replace(/(['\\])/g, '\\$1')
.replace(/[\f]/g, "\\f")
.replace(/[\b]/g, "\\b")
.replace(/[\n]/g, "\\n")
.replace(/[\t]/g, "\\t")
.replace(/[\r]/g, "\\r");
},
createXhr: function () {
//Would love to dump the ActiveX crap in here. Need IE 6 to die first.
var xhr, i, progId;
if (typeof XMLHttpRequest !== "undefined") {
return new XMLHttpRequest();
} else {
for (i = 0; i < 3; i++) {
progId = progIds[i];
try {
xhr = new ActiveXObject(progId);
} catch (e) {}
if (xhr) {
progIds = [progId]; // so faster next time
break;
}
}
}
if (!xhr) {
throw new Error("createXhr(): XMLHttpRequest not available");
}
return xhr;
},
get: get,
/**
* Parses a resource name into its component parts. Resource names
* look like: module/name.ext!strip, where the !strip part is
* optional.
* @param {String} name the resource name
* @returns {Object} with properties "moduleName", "ext" and "strip"
* where strip is a boolean.
*/
parseName: function (name) {
var strip = false, index = name.indexOf("."),
modName = name.substring(0, index),
ext = name.substring(index + 1, name.length);
index = ext.indexOf("!");
if (index !== -1) {
//Pull off the strip arg.
strip = ext.substring(index + 1, ext.length);
strip = strip === "strip";
ext = ext.substring(0, index);
}
return {
moduleName: modName,
ext: ext,
strip: strip
};
},
xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/,
/**
* Is an URL on another domain. Only works for browser use, returns
* false in non-browser environments. Only used to know if an
* optimized .js version of a text resource should be loaded
* instead.
* @param {String} url
* @returns Boolean
*/
useXhr: function (url, protocol, hostname, port) {
var match = text.xdRegExp.exec(url),
uProtocol, uHostName, uPort;
if (!match) {
return true;
}
uProtocol = match[2];
uHostName = match[3];
uHostName = uHostName.split(':');
uPort = uHostName[1];
uHostName = uHostName[0];
return (!uProtocol || uProtocol === protocol) &&
(!uHostName || uHostName === hostname) &&
((!uPort && !uHostName) || uPort === port);
},
finishLoad: function (name, strip, content, onLoad, config) {
content = strip ? text.strip(content) : content;
if (config.isBuild) {
buildMap[name] = content;
}
onLoad(content);
},
load: function (name, req, onLoad, config) {
//Name has format: some.module.filext!strip
//The strip part is optional.
//if strip is present, then that means only get the string contents
//inside a body tag in an HTML string. For XML/SVG content it means
//removing the <?xml ...?> declarations so the content can be inserted
//into the current doc without problems.
// Do not bother with the work if a build and text will
// not be inlined.
if (config.isBuild && !config.inlineText) {
onLoad();
return;
}
var parsed = text.parseName(name),
nonStripName = parsed.moduleName + '.' + parsed.ext,
url = req.toUrl(nonStripName),
useXhr = (config && config.text && config.text.useXhr) ||
text.useXhr;
//Load the text. Use XHR if possible and in a browser.
if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) {
text.get(url, function (content) {
text.finishLoad(name, parsed.strip, content, onLoad, config);
});
} else {
//Need to fetch the resource across domains. Assume
//the resource has been optimized into a JS module. Fetch
//by the module name + extension, but do not include the
//!strip part to avoid file system issues.
req([nonStripName], function (content) {
text.finishLoad(parsed.moduleName + '.' + parsed.ext,
parsed.strip, content, onLoad, config);
});
}
},
write: function (pluginName, moduleName, write, config) {
if (moduleName in buildMap) {
var content = text.jsEscape(buildMap[moduleName]);
write.asModule(pluginName + "!" + moduleName,
"define(function () { return '" +
content +
"';});\n");
}
},
writeFile: function (pluginName, moduleName, req, write, config) {
var parsed = text.parseName(moduleName),
nonStripName = parsed.moduleName + '.' + parsed.ext,
//Use a '.js' file name so that it indicates it is a
//script that can be loaded across domains.
fileName = req.toUrl(parsed.moduleName + '.' +
parsed.ext) + '.js';
//Leverage own load() method to load plugin value, but only
//write out values that do not have the strip argument,
//to avoid any potential issues with ! in file names.
text.load(nonStripName, req, function (value) {
//Use own write() method to construct full module value.
//But need to create shell that translates writeFile's
//write() to the right interface.
var textWrite = function (contents) {
return write(fileName, contents);
};
textWrite.asModule = function (moduleName, contents) {
return write.asModule(moduleName, fileName, contents);
};
text.write(pluginName, nonStripName, textWrite, config);
}, config);
}
};
return text;
});
}());
/*!
* TroopJS RequireJS template plug-in
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/**
* This plugin provides a template loader and compiler.
*/
define('template',[ "text" ], function TemplateModule(text) {
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"
};
var buildMap = {};
/**
* 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 new Function("data", ("var o; 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));
}
return {
load : function(name, req, load, config) {
text.load(name, req, function(value) {
// Compile template and store in buildMap
var template = buildMap[name] = compile(value);
// Set display name for debugging
template.displayName = name;
// Pass template to load
load(template);
}, config);
},
write : function(pluginName, moduleName, write, config) {
if (moduleName in buildMap) {
write.asModule(pluginName + "!" + moduleName, "define(function () { return " + buildMap[moduleName].toString().replace(RE_SANITIZE, EMPTY) + ";});\n");
}
}
};
});
/*!
* TroopJS jQuery action plug-in
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
define('troopjs-jquery/action',[ "jquery" ], function ActionModule($) {
var UNDEFINED = undefined;
var FALSE = false;
var NULL = null;
var SLICE = Array.prototype.slice;
var ACTION = "action";
var TRUE = "true";
var ORIGINALEVENT = "originalEvent";
var RE_ACTION = /^([\w\d\s_\-\/]+)(?:\.([\w\.]+))?(?:\((.*)\))?$/;
var RE_SEPARATOR = /\s*,\s*/;
var RE_DOT = /\.+/;
var RE_STRING = /^(["']).*\1$/;
var RE_DIGIT = /^\d+$/;
var RE_BOOLEAN = /^false|true$/i;
/**
* 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
? args.split(RE_SEPARATOR)
: [];
// Iterate argv to determine arg type
$.each(argv, function argsIterator(i, value) {
if (value in $data) {
argv[i] = $data[value];
} else if (RE_STRING.test(value)) {
argv[i] = value.slice(1, -1);
} else if (RE_DIGIT.test(value)) {
argv[i] = Number(value);
} else if (RE_BOOLEAN.test(value)) {
argv[i] = value === TRUE;
} else {
argv[i] = UNDEFINED;
}
});
// Trigger exclusive ACTION event
$target.trigger($.Event($event, {
type: ACTION + "!",
action: name
}), argv);
}
$.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 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
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 dimensions plug-in
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
define('troopjs-jquery/dimensions',[ "jquery" ], function DimensionsModule($) {
var RE = /(w|h)(\d*)/g;
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 a < b ? 1 : a > b ? -1 : 0;
}
function onResize($event) {
var $self = $(this);
var w = $self.width();
var h = $self.height();
$.each($self.data(DIMENSIONS), function dimensionIterator(namespace, dimension) {
var dimension_w = dimension[W];
var dimension_w_max = dimension_w.length - 1;
var dimension_h = dimension[H];
var dimension_h_max = dimension_h.length - 1;
var _w = $.grep(dimension_w, function(_w, i) {
return _w <= w || i === dimension_w_max;
})[0];
var _h = $.grep(dimension_h, function(_h, i) {
return _h <= h || i === dimension_h_max;
})[0];
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, {});
},
add : function onDimensionsAdd(handleObj) {
var namespace = handleObj.namespace;
var dimension = {};
var w = dimension[W] = [];
var h = dimension[H] = [];
var matches;
while (matches = RE.exec(namespace)) {
dimension[matches[1]].push(parseInt(matches[2]));
}
w.sort(reverse);
h.sort(reverse);
$.data(this, DIMENSIONS)[namespace] = dimension;
},
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 hashchange plug-in
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/**
* Normalized hashchange event, ripped a _lot_ of code from
* https://github.com/millermedeiros/Hasher
*/
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://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
var _isIE = !+"\v1";
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 getElement() {
return this.element;
},
getHash : function getHash() {
return this.element.contentWindow.frameHash;
},
update : function update(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>&nbsp;</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 jQuery weave plug-in
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
define('troopjs-jquery/weave',[ "jquery" ], function WeaveModule($) {
var UNDEFINED = undefined;
var NULL = null;
var TRUE = true;
var ARRAY = Array;
var ARRAY_PROTO = ARRAY.prototype;
var JOIN = ARRAY_PROTO.join;
var WHEN = $.when;
var WEAVE = "weave";
var UNWEAVE = "unweave";
var WOVEN = "woven";
var DESTROY = "destroy";
var DATA_WEAVE = "data-" + WEAVE;
var DATA_WOVEN = "data-" + WOVEN;
var SELECTOR_WEAVE = "[" + DATA_WEAVE + "]";
var SELECTOR_WOVEN = "[" + DATA_WOVEN + "]";
var RE_WEAVE = /[\s,]*([\w_\-\/]+)(?:\(([^\)]+)\))?/g;
var RE_SEPARATOR = /\s*,\s*/;
var RE_STRING = /^(["']).*\1$/;
var RE_DIGIT = /^\d+$/;
var RE_BOOLEAN = /^false|true$/i;
/**
* Generic destroy handler.
* Simply makes sure that unweave has been called
* @param $event
*/
function onDestroy($event) {
$(this).unweave();
}
$.fn[WEAVE] = function weave(/* arg, arg, arg, */ deferred) {
var required = [];
var i = 0;
var $elements = $(this);
// Make arguments into a real array
var argx = ARRAY.apply(ARRAY_PROTO, arguments);
// Update deferred to the last argument
deferred = argx.pop();
$elements
// Reduce to only elements that can be woven
.filter(SELECTOR_WEAVE)
// Iterate
.each(function elementIterator(index, element) {
var $element = $(element);
var $data = $element.data();
var weave = $element.attr(DATA_WEAVE) || "";
var widgets = [];
var mark = i;
var j = 0;
var matches;
$element
// Store DATA_WEAVE attribute as WEAVE
.data(WEAVE, weave)
// Store widgets array as WOVEN
.data(WOVEN, widgets)
// Make sure to remove DATA_WEAVE (so we don't try processing this again)
.removeAttr(DATA_WEAVE);
// Iterate widgets (while the rexep matches)
while ((matches = RE_WEAVE.exec(weave)) !== NULL) {
// Add deferred to required array
required[i] = $.Deferred(function deferedRequire(dfd) {
// Store prefixed values as they will not be available during require
var _j = j;
var _matches = matches;
// Get widget name
var name = _matches[1];
// Get widget args
var args = _matches[2];
try {
// Require widget
require([ name ], function required(Widget) {
var k;
var l;
var kMax;
var value;
var widget;
// Set initial argv
var argv = [ $element, name ];
// Copy values from argx to argv
for (k = 0, kMax = argx.length, l = argv.length; k < kMax; k++, l++) {
argv[l] = arg[k];
}
// Any widget arguments
if (args !== UNDEFINED) {
// Convert args to array
args = args.split(RE_SEPARATOR);
// Iterate to 'cast' values
for (k = 0, kMax = args.length, l = argv.length; k < kMax; k++, l++) {
// Get value
value = args[k];
if (value in $data) {
argv[l] = $data[value];
} else if (RE_STRING.test(value)) {
argv[l] = value.slice(1, -1);
} else if (RE_DIGIT.test(value)) {
argv[l] = Number(value);
} else if (RE_BOOLEAN.test(value)) {
argv[l] = value === TRUE;
} else {
argv[l] = value;
}
}
}
// Simple or complex instantiation
widget = l === 2
? Widget($element, name)
: Widget.apply(Widget, argv);
// Bind destroy event handler
$element.bind(DESTROY, onDestroy);
// Build
widget.build();
// Store widgets[_j] and resolve with widget instance
dfd.resolve(widgets[_j] = widget);
});
}
catch (e) {
// Reset widgets[_j] and resolve with UNDEFINED
dfd.resolve(widgets[_j] = UNDEFINED);
}
});
// Step i, j
i++;
j++;
}
// Slice out widgets woven for this element
WHEN.apply($, required.slice(mark, i)).done(function doneRequired() {
// Set 'data-woven' attribute
$element.attr(DATA_WOVEN, JOIN.call(arguments, " "));
});
});
if (deferred) {
// When all deferred are resolved, resolve original deferred
WHEN.apply($, required).then(deferred.resolve, deferred.reject);
}
return $elements;
};
$.fn[UNWEAVE] = function unweave() {
return $(this)
// Reduce to only elements that are woven
.filter(SELECTOR_WOVEN)
// Iterate
.each(function elementIterator(index, element) {
var $element = $(element);
var widgets = $element.data(WOVEN);
var widget;
$element
// Remove WOVEN data
.removeData(WOVEN)
// Remove DATA_WOVEN attribute
.removeAttr(DATA_WOVEN);
// Somewhat safe(r) iterator over widgets
while (widget = widgets.shift()) {
// Destroy
widget.destroy();
}
$element
// Copy woven data to data-weave attribute
.attr(DATA_WEAVE, $element.data(WEAVE))
// Remove data fore WEAVE
.removeData(DATA_WEAVE)
// Make sure to clean the destroy event handler
.unbind(DESTROY, onDestroy);
});
};
});
\ No newline at end of file
/*!
* TroopJS RequireJS template plug-in
*
* parts of code from require-cs 0.4.0+ Copyright (c) 2010-2011, The Dojo Foundation
*
* @license TroopJS 0.0.2 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS base component
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS deferred component
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS pubsub/topic module
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS pubsub/hub module
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS gadget component
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS service component
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS util/merge module
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS remote/ajax module
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS util/uri module
*
* parts of code from parseUri 1.2.2 Copyright Steven Levithan <stevenlevithan.com>
*
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS route/router module
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS store/base module
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS store/local module
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS store/session module
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS widget component
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS widget/placeholder component
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS route/placeholder module
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS widget/application component
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS jQuery action plug-in
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS jQuery destroy plug-in
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS jQuery dimensions plug-in
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS jQuery hashchange plug-in
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
/*!
* TroopJS jQuery weave plug-in
* @license TroopJS 0.0.1 Copyright 2012, Mikael Karon <mikael@karon.se>
* Released under the MIT license.
*/
define("template",[],function(){function j(a){function k(a,c,d){return b[j]=c?'" +'+d+'+ "':'";'+d+'o += "',"<%"+String(j++)+"%>"}function l(a,c){return b[c]}function m(a,b){return i[b]||b}var b=[],j=0;return('function template(data) { var o = "'+a.replace(c,"").replace(d,k).replace(f,m).replace(e,l)+'"; return o; }').replace(g,h)}var b={node:function(){var a=require.nodeRequire("fs");return function(c,d){d(a.readFileSync(c,"utf8"))}},browser:function(){var a=["Msxml2.XMLHTTP","Microsoft.XMLHTTP","Msxml2.XMLHTTP.4.0"],b,c,d;if(typeof XMLHttpRequest!="undefined")c=XMLHttpRequest;else a:{for(d=0;d<3;d++){b=a[d];try{c=ActiveXObject(b);break a}catch(e){}}throw new Error("XHR: XMLHttpRequest not available")}return function(b,d){var e=new c;e.open("GET",b,!0),e.onreadystatechange=function(a){e.readyState===4&&d(e.responseText)},e.send(null)}},rhino:function(){var a="utf-8",b=java.lang.System.getProperty("line.separator");return function(d,e){var f=new java.io.File(d),g=new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(f),a)),h=new java.lang.StringBuffer,i,j="";try{i=g.readLine(),i&&i.length()&&i.charAt(0)===65279&&(i=i.substring(1)),h.append(i);while((i=g.readLine())!==null)h.append(b),h.append(i);j=String(h.toString())}finally{g.close()}e(j)}},borked:function(){return function(){throw new Error("Environment unsupported.")}}},c=/^[\n\t\r]+|[\n\t\r]+$/g,d=/<%(=)?([\S\s]*?)%>/g,e=/<%(\d+)%>/gm,f=/(["\n\t\r])/gm,g=/o \+= "";| \+ ""/gm,h="",i={'"':'\\"',"\n":"\\n"," ":"\\t","\r":"\\r"},k={},l=b[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(a,b,c,d){var e=b.toUrl(a);l(e,function(f){try{f="define(function() { return "+j(f,a,e,d.template)+"; })"}catch(g){throw g.message="In "+e+", "+g.message,g}d.isBuild?k[a]=f:f+="\n//@ sourceURL="+e,c.fromText(a,f),b([a],function(a){c(a)})})},write:function(a,b,c){k.hasOwnProperty(b)&&c.asModule(a+"!"+b,k[b])}}}),function(a){a("compose",[],function(){function a(){}function c(a){if(!a)throw new Error("Compose arguments must be functions or objects");return a}function d(a,b,d){var f,g=b.length;for(;d<g;d++){var h=b[d];if(typeof h=="function"){var i=h.prototype;for(var k in i){f=i[k];var l=i.hasOwnProperty(k);if(typeof f=="function"&&k in a&&f!==a[k]){var n=a[k];f==j?f=n:l||(e(f,k,m([].slice.call(b,0,d),!0))?f=n:e(n,k,m([h],!0))||console.error("Conflicted method "+k+", final composer must explicitly override with correct method."))}f&&f.install&&l&&!e(n,k,m([h],!0))?f.install.call(a,k):a[k]=f}}else for(var k in c(h)){var f=h[k];if(typeof f=="function"){if(f.install){f.install.call(a,k);continue}if(k in a&&f==j)continue}a[k]=f}}return a}function e(a,b,c){for(var d=0;d<c.length;d++){var e=c[d];if(e[b]==a)return!0}}function f(a,b){function c(){if(b)return b.apply(this,arguments);throw new Error("Decorator not applied")}return c.install=a,c}function g(a){return function(b){return f(function c(d){var e=this[d];(b=this[d]=e?a(this,e,b):b).install=c},b)}}function j(){throw new Error("This method is required and no implementation has been provided")}function k(){var a=[this];return a.push.apply(a,arguments),l.apply(0,a)}function l(e){function h(){var b;this instanceof h?b=this:(a.prototype=g,b=new a);for(var c=0;c<j;c++){var d=i[c],e=d.apply(b,arguments);if(typeof e=="object")if(e instanceof h)b=e;else for(var f in e)e.hasOwnProperty(f)&&(b[f]=e[f])}return b}var f=arguments,g=f.length<2&&typeof f[0]!="function"?f[0]:d(b(c(e)),f,1);h._getBases=function(a){return a?n:i};var i=m(f),j=i.length;typeof f[f.length-1]=="object"&&(f[f.length-1]=g);var n=m(f,!0);return h.extend=k,l.secure||(g.constructor=h),h.prototype=g,h}function m(a,b){function d(a,e){a:for(var f=0;f<a.length;f++){var g=a[f],h=b&&typeof g=="function"?g.prototype:g;if(b||typeof g=="function"){var i=e&&g._getBases;if(i)d(i(b));else{for(var j=0;j<c.length;j++)if(h==c[j])continue a;c.push(h)}}}}var c=[];return d(a,!0),c}var b=Object.create?function(a){return Object.create(typeof a=="function"?a.prototype:a||Object.prototype)}:function(b){a.prototype=typeof b=="function"?b.prototype:b;var c=new a;return a.prototype=null,c};l._setMixin=function(a){d=a},l.Decorator=f,l.around=g(function(a,b,c){return c.call(a,b)}),l.before=g(function(a,b,c){return function(){var a=c.apply(this,arguments);if(a!==h)return b.apply(this,a||arguments)}});var h=l.stop={},i;return l.after=g(function(a,b,c){return function(){var a=b.apply(this,arguments),d=c.apply(this,arguments);return d===i?a:d}}),l.from=function(a,b){return b?(typeof a=="function"?a.prototype:a)[b]:f(function(c){if(!(this[c]=typeof a=="string"?this[a]:(typeof a=="function"?a.prototype:a)[b||c]))throw new Error("Source method "+b+" was not available to be renamed to "+c)})},l.create=function(a){var c=d(b(a),arguments,1),e=arguments.length;for(var f=0;f<e;f++){var g=arguments[f];typeof g=="function"&&(c=g.call(c)||c)}return c},l.required=j,l.apply=function(a,b){return a?d(a,b,0):k.apply.call(l,0,b)},l.call=function(a){return d(a,arguments,1)},l})}(typeof define!="undefined"?define:function(a,b){typeof module!="undefined"?module.exports=b():Compose=b()}),define("troopjs-core/component/base",["compose","config"],function(b,c){var d=0;return b(function(){this.instanceCount=d++},{displayName:"core/component",config:c,toString:function e(){var a=this;return a.displayName+"@"+a.instanceCount}})}),define("troopjs-core/util/deferred",["jquery"],function(b){return b.Deferred}),define("troopjs-core/pubsub/topic",["../component/base"],function(b){var c=Array;return b.extend(function(b,c,d){var e=this;e.topic=b,e.publisher=c,e.parent=d},{displayName:"core/pubsub/topic",toString:function d(){return this.topic},trace:function(){var b=this,d=b.constructor,e,f,g="",h,i;while(b){if(b.constructor===c){for(h=0,i=b.length;h<i;h++)f=b[h],b[h]=f.constructor===d?f.trace():f;g+=b.join(",");break}e=b.parent,g+=e?b.publisher+":":b.publisher,b=e}return g}})}),define("troopjs-core/pubsub/hub",["compose","../component/base","./topic"],function(b,c,d){var e={},f={},g="memory",h="head",i="tail",j="next";return b.create({displayName:"core/pubsub/hub",subscribe:function(b){var c=this,d=arguments.length,k=arguments[1],l=arguments[2],m=arguments[3],n,o,p,q,r;if(k instanceof Function)m=k,l=!1,k=e,n=1;else if(k===!0||k===!1)m=l,l=k,k=e,n=2;else if(l instanceof Function)m=l,l=!1,n=2;else{if(!(m instanceof Function))return c;n=3}if(b in f){o=f[b],p={callback:arguments[n++],context:k},r=i in o?o[i][j]=p:o[h]=p;while(n<d)r=r[j]={callback:arguments[n++],context:k};o[i]=r;if(l&&g in o){l=o[g];if(l.length>0)while(p)p.callback.apply(p.context,l),p=p[j];else while(p)p.callback.call(p.context),p=p[j]}}else{q=r={callback:arguments[n++],context:k};while(n<d)r=r[j]={callback:arguments[n++],context:k};f[b]={head:q,tail:r}}return c},unsubscribe:function(b){var c=arguments.length,d=arguments[1],g=arguments[2],k,l,m,n,o=null;if(d instanceof Function)g=d,d=e,k=1;else{if(!(g instanceof Function))return self;k=2}a:{if(!b in f)break a;l=f[b],n=l[h];while(k<c){g=arguments[k++],m=o=n;do{if(m.callback===g&&m.context===d){if(m===n){n=o=m[j];continue}o[j]=m[j];continue}o=m}while(m=m[j])}n&&o?(l[h]=n,l[i]=o):(delete l[h],delete l[i])}return this},publish:function(b){var c,d;if(b in f){c=f[b],c[g]=arguments,d=c[h];if(arguments.length>0)while(d)d.callback.apply(d.context,arguments),d=d[j];else while(d)d.callback.call(d.context),d=d[j]}else arguments.length>0&&(f[b]=c={},c[g]=arguments);return this}})}),define("troopjs-core/component/gadget",["compose","./base","../util/deferred","../pubsub/hub"],function(b,c,d,e){var f=null,g=Object,h=Function,i=/^hub(?::(\w+))?\/(.+)/,j=/^sig\/(.+)/,k=e.publish,l=e.subscribe,m=e.unsubscribe,n="memory",o="subscriptions",p="__proto__",q=g.getPrototypeOf||(p in g?function(b){return b[p]}:function(b){return b.constructor.prototype});return c.extend(function(){var c=this,__proto__=c,e,g,i,k,l={},m,n,o=null;do a:for(o in __proto__){g=__proto__[o];if(!(g instanceof h))continue;n=j.exec(o);if(n!==f){m=n[1];if(m in l){e=l[m],i=k=e.length;while(i--)if(g===e[i])continue a;e[k]=g}else l[m]=[g]}}while(__proto__=q(__proto__));b.call(c,{signal:function p(p,a){var b=this,c,e,f=a;if(p in l){c=l[p],e=c.length;while(--e)f=d(function(a){var d=c[e],g=f;a.done(function(){d.call(b,p,g)})});c[0].call(b,p,f)}else a&&a.resolve();return b}})},{displayName:"core/component/gadget","sig/initialize":function(b,c){var d=this,g=d[o]=[],j=f,k,l,m;for(j in d){k=d[j];if(!(k instanceof h))continue;l=i.exec(j),l!==f&&(m=l[2],e.subscribe(m,d,l[1]===n,k),g[g.length]=[m,d,k],d[j]=f)}return c&&c.resolve(),d},"sig/finalize":function(b,c){var d=this,f=d[o],g;while(g=f.shift())e.unsubscribe(g[0],g[1],g[2]);return c&&c.resolve(),d},publish:function(){var b=this;return k.apply(e,arguments),b},subscribe:function(){var b=this;return l.apply(e,arguments),b},unsubscribe:function(){var b=this;return m.apply(e,arguments),b},start:function(b){var c=this;return d(function(e){d(function(b){c.signal("initialize",b)}).done(function(){c.signal("start",e)}).fail(e.reject),b&&e.then(b.resolve,b.reject)}),c},stop:function(b){var c=this;return d(function(e){d(function(b){c.signal("stop",b)}).done(function(){c.signal("finalize",e)}).fail(e.reject),b&&e.then(b.resolve,b.reject)}),c}})}),define("troopjs-core/component/service",["./gadget"],function(b){return b.extend({displayName:"core/component/service"})}),define("troopjs-core/util/merge",[],function(){var b=Array,c=Object;return function d(a){var e=this,f=null,g,h,i,j;for(g=0,h=arguments.length;g<h;g++){a=arguments[g];for(f in a)i=a[f],j=i.constructor,f in e?j===b?e[f]=e[f].concat(i):j===c?d.call(e[f],i):e[f]=i:e[f]=i}return e}}),define("troopjs-core/remote/ajax",["../component/service","../pubsub/topic","jquery","../util/merge"],function(b,c,d,e){return b.extend({displayName:"core/remote/ajax","hub/ajax":function(b,f,g){d.ajax(e.call({headers:{"x-request-id":(new Date).getTime(),"x-components":b instanceof c?b.trace():b}},f)).then(g.resolve,g.reject)}})}),define("troopjs-core/util/uri",["compose"],function(b){var c=null,d=Function,e=Array,f=e.prototype,g=/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?(?:([^?#]*)(?:\?([^#]*))?(?:#(.*))?)/,h="protocol",i="authority",j="path",k="query",l="anchor",m=["source",h,i,"userInfo","user","password","host","port",j,k,l],n=b.secure;b.secure=!0;var o=b(function(b){if(!b||b.length===0)return;var c=this,d,f,g,h=/(?:&|^)([^&=]*)=?([^&]*)/g;while(d=h.exec(b))f=d[1],f in c?(g=c[f],g instanceof e?g[g.length]=d[2]:c[f]=[g,d[2]]):c[f]=d[2]},{toString:function r(){var a=this,b=c,f=c,g=[],h=0,i;for(b in a){if(a[b]instanceof d)continue;g[h++]=b}g.sort();while(h--){b=g[h],f=a[b];if(f instanceof e){f=f.slice(0),f.sort(),i=f.length;while(i--)f[i]=b+"="+f[i];g[h]=f.join("&")}else g[h]=b+"="+f}return g.join("&")}}),p=b(f,function(b){if(!b||b.length===0)return;var c=this,d,e=/(?:\/|^)([^\/]*)/g;while(d=e.exec(b))c.push(d[1])},{toString:function s(){return this.join("/")}}),q=b(function(b){var c=this,d=g.exec(b),e=d.length,f;while(e--)f=d[e],f&&(c[m[e]]=f);k in c&&(c[k]=o(c[k])),j in c&&(c[j]=p(c[j]))},{toString:function t(){var a=this,b=[h,"://",i,j,"?",k,"#",l],c,d;h in a||b.splice(0,3),j in a||b.splice(0,1),l in a||b.splice(-2,2),k in a||b.splice(-2,2),c=b.length;while(c--)d=b[c],d in a&&(b[c]=a[d]);return b.join("")}});return b.secure=n,q}),define("troopjs-core/route/router",["../component/service","../util/uri"],function(b,c){function h(a){var b=a.data,d=c(a.target.location.hash.replace(g,"")),e=d.toString();e!==b[f]&&(b[f]=e,b.publish(f,d))}var d="hashchange",e="$element",f="route",g=/^#/;return b.extend(function(b){this[e]=b},{displayName:"core/route/router","sig/initialize":function(b,c){var f=this;return f[e].bind(d,f,h),c&&c.resolve(),f},"sig/start":function(b,c){var f=this;return f[e].trigger(d),c&&c.resolve(),f},"sig/finalize":function(b,c){var f=this;return f[e].unbind(d,h),c&&c.resolve(),f}})}),define("troopjs-core/store/base",["compose","../component/gadget"],function(b,c){var d="storage";return c.extend({storage:b.required,set:function(b,c,e){this[d].setItem(b,JSON.stringify(c)),e&&e.resolve(c)},get:function(b,c){var e=JSON.parse(this[d].getItem(b));c&&c.resolve(e)},remove:function(b,c){this[d].removeItem(b),c&&c.resolve()},clear:function(b){this[d].clear(),b&&b.resolve()}})}),define("troopjs-core/store/local",["compose","./base"],function(b,c){return b.create(c,{displayName:"core/store/local",storage:window.localStorage})}),define("troopjs-core/store/session",["compose","./base"],function(b,c){return b.create(c,{displayName:"core/store/session",storage:window.sessionStorage})}),define("troopjs-core/component/widget",["./gadget","jquery","../util/deferred"],function(b,c,d){function x(a,b,c){return function(){return j.call(arguments,a),c.apply(b,arguments)}}function y(a){function b(){var b=this,c=b[r],e=arguments,h=i.call(e),j=e.length,l=j>0&&e[j-1][u]instanceof f?k.call(e):g;return a.call(c,h instanceof f?h.apply(b,e):h),d(function(b){c.find(v).weave(b),b.done(function(){c.trigger(q,arguments)}),l&&b.then(l.resolve,l.reject)}),b}return b}var e=null,f=Function,g=undefined,h=Array.prototype,i=h.shift,j=h.unshift,k=h.pop,l=c.fn.trigger,m=c.fn.one,n=c.fn.bind,o=c.fn.unbind,p=/^dom(?::(\w+))?\/([^\.]+(?:\.(.+))?)/,q="widget/refresh",r="$element",s="$proxies",t="one",u="then",v="[data-weave]",w="[data-woven]";return b.extend(function(b,c){var d=this;d[r]=b,c&&(d.displayName=c)},{displayName:"core/component/widget","sig/initialize":function(b,c){var d=this,g=d[r],h=d[s]=[],i=e,j,k,l;for(i in d){j=d[i];if(!(j instanceof f))continue;k=p.exec(i),k!==e&&(l=k[2],j=x(l,d,j),(k[2]===t?m:n).call(g,l,d,j),h[h.length]=[l,j],d[i]=e)}return c&&c.resolve(),d},"sig/finalize":function(b,c){var d=this,e=d[r],f=d[s],g;while(g=f.shift())e.unbind(g[0],g[1]);return c&&c.resolve(),d},weave:function(b){var c=this;return c[r].find(v).weave(b),c},unweave:function(){var b=this;return b[r].find(w).andSelf().unweave(),this},one:function(){var b=this;return m.apply(b[r],arguments),b},bind:function(){var b=this;return n.apply(b[r],arguments),b},unbind:function(){var b=this;return o.apply(b[r],arguments),b},trigger:function(){var b=this;return l.apply(b[r],arguments),b},before:y(c.fn.before),after:y(c.fn.after),html:y(c.fn.html),text:y(c.fn.text),append:y(c.fn.append),prepend:y(c.fn.prepend),empty:function(b){var c=this;return d(function(d){var e=c[r],f=e.contents().detach();e.trigger(q,c),setTimeout(function(){var b=f.get();f.remove(),d.resolve(b)},0),b&&d.then(b.resolve,b.reject)}),c}})}),define("troopjs-core/widget/placeholder",["../component/widget","../util/deferred"],function(b,c){function n(){var a=this,b=arguments,f=b.length,g=f>0&&b[f-1][m]instanceof e?h.call(b):d;return c(function(e){var f,h,m,n;if(i in a)e.resolve(a[i]);else{e.done(function(c){a[k].attr(j,c),a[i]=c}),m=a[l],n=[a[k],m];for(f=0,h=b.length;f<h;f++)n[f+2]=b[f];require([m],function(b){var d=b.apply(b,n);c(function(b){d.start(b)}).done(function(){e.resolve(d)}).fail(e.reject)})}g&&e.then(g.resolve,g.reject)}),a}function o(a){var b=this;return c(function(e){var f;i in b?(f=b[i],delete b[i],b[k].removeAttr(j),c(function(b){f.stop(b)}).then(e.resolve,e.reject)):e.resolve(),a&&dfd.then(a.resolve,a.reject)}),b}var d=undefined,e=Function,f=Array,g=f.prototype,h=g.pop,i="holding",j="data-"+i,k="$element",l="target",m="then";return b.extend(function(b,c,d){this[l]=d},{displayName:"core/widget/placeholder",release:n,hold:o,finalize:o})}),define("troopjs-core/route/placeholder",["../widget/placeholder"],function(b){var c=null,d="route";return b.extend(function(b,c){this[d]=RegExp(b.data("route"))},{displayName:"core/route/placeholder","hub:memory/route":function(b,e){var f=this,g=f[d].exec(e.path);g!==c?f.release.apply(f,g.slice(1)):f.hold()}})}),define("troopjs-core/widget/application",["../component/widget","../util/deferred"],function(b,c){return b.extend({displayName:"core/widget/application","sig/start":function(b,c){var d=this;return d.weave(c),d},"sig/stop":function(b,c){var d=this;return d.unweave(c),d}})}),define("troopjs-jquery/action",["jquery"],function(b){function p(a,b){return a?a+"."+g:e}function q(a){var c=b(this),e=f.call(arguments,1),h=i in a?a[i].type:g,j=a[g];a.type=g+"/"+j+"."+h,c.trigger(a,e),a.result!==d&&(a.type=g+"/"+j+"!",c.trigger(a,e),a.result!==d&&(a.type=g+"."+h,c.trigger(a,e)))}function r(a){var d=b(a.target).closest("[data-action]");if(d.length===0)return;var f=d.data(),i=j.exec(f[g]);if(i===e)return;var p=i[1],q=i[2],r=i[3];if(q!==c&&!RegExp(q.split(l).join("|")).test(a.type))return;var s=r!==c?r.split(k):[];b.each(s,function(b,d){d in f?s[b]=f[d]:m.test(d)?s[b]=d.slice(1,-1):n.test(d)?s[b]=Number(d):o.test(d)?s[b]=d===h:s[b]=c}),d.trigger(b.Event(a,{type:g+"!",action:p}),s),a.stopPropagation()}var c=undefined,d=!1,e=null,f=Array.prototype.slice,g="action",h="true",i="originalEvent",j=/^([\w\d\s_\-\/]+)(?:\.([\w\.]+))?(?:\((.*)\))?$/,k=/\s*,\s*/,l=/\.+/,m=/^(["']).*\1$/,n=/^\d+$/,o=/^false|true$/i;b.event.special[g]={setup:function(c,d,e){b(this).bind(g,c,q)},add:function(c){var d=b.map(c.namespace.split(l),p);d.length!==0&&b(this).bind(d.join(" "),r)},remove:function(c){var d=b.map(c.namespace.split(l),p);d.length!==0&&b(this).unbind(d.join(" "),r)},teardown:function(c){b(this).unbind(g,q)}},b.fn[g]=function(c){return b(this).trigger({type:g+"!",action:c},f.call(arguments,1))}}),define("troopjs-jquery/destroy",["jquery"],function(b){b.event.special.destroy={remove:function(c){var d=this;c.handler.call(d,b.Event({type:c.type,data:c.data,namespace:c.namespace,target:d}))}}}),define("troopjs-jquery/dimensions",["jquery"],function(b){function j(a,b){return a<b?1:a>b?-1:0}function k(a){var c=b(this),e=c.width(),j=c.height();b.each(c.data(d),function(k,l){var m=l[f],n=m.length-1,o=l[g],p=o.length-1,q=b.grep(m,function(a,b){return a<=e||b===n})[0],r=b.grep(o,function(a,b){return a<=j||b===p})[0];if(q!==l[h]||r!==l[i])l[h]=q,l[i]=r,c.trigger(d+"."+k,[q,r])})}var c=/(w|h)(\d*)/g,d="dimensions",e="resize."+d,f="w",g="h",h="_"+f,i="_"+g;b.event.special[d]={setup:function(c,f,g){b(this).bind(e,k).data(d,{})},add:function(e){var h=e.namespace,i={},k=i[f]=[],l=i[g]=[],m;while(m=c.exec(h))i[m[1]].push(parseInt(m[2]));k.sort(j),l.sort(j),b.data(this,d)[h]=i},remove:function(c){delete b.data(this,d)[c.namespace]},teardown:function(c){b(this).removeData(d).unbind(e,k)}}}),define("troopjs-jquery/hashchange",["jquery"],function(b){function i(a){var b=f.exec(a.location.href);return b&&b[1]?decodeURIComponent(b[1]):""}function j(a){var b=this,c;b.element=c=a.createElement("iframe"),c.src="about:blank",c.style.display="none"}var c="interval",d="hashchange",e="on"+d,f=/#(.*)$/,g=/\?/,h=!1;j.prototype={getElement:function(){return this.element},getHash:function(){return this.element.contentWindow.frameHash},update:function(b){var c=this,d=c.element.contentWindow.document;if(c.getHash()===b)return;d.open(),d.write("<html><head><title>' + document.title + '</title><script type='text/javascript'>var frameHash='"+b+"';</script></head><body>&nbsp;</body></html>"),d.close()}},b.event.special[d]={setup:function(f,k,l){var m=this;if(e in m)return!1;if(!b.isWindow(m))throw new Error("Unable to bind 'hashchange' to a non-window object");var n=b(m),o=i(m),p=m.location;n.data(c,m.setInterval(h?function(){var b=m.document,c=p.protocol==="file:",e=new j(b);return b.body.appendChild(e.getElement()),e.update(o),function(){var b=o,f,h=i(m),j=e.getHash();j!==o&&j!==h?(f=decodeURIComponent(j),o!==f&&(o=f,e.update(o),n.trigger(d,[f,b])),p.hash="#"+encodeURI(c?j.replace(g,"%3F"):j)):h!==o&&(f=decodeURIComponent(h),o!==f&&(o=f,n.trigger(d,[f,b])))}}():function(){var b=o,c,e=i(m);e!==o&&(c=decodeURIComponent(e),o!==c&&(o=c,n.trigger(d,[c,b])))},25))},teardown:function(d){var f=this;if(e in f)return!1;f.clearInterval(b.data(f,c))}}}),define("troopjs-jquery/weave",["jquery"],function(b){function x(a){b(this).unweave()}var c=undefined,d=!0,e=Array,f=Function,g=e.prototype,h=g.join,i=g.pop,j=b.when,k="then",l="weave",m="unweave",n="woven",o="destroy",p="data-"+l,q="data-"+n,r="["+p+"]",s="["+q+"]",t=/\s*,\s*/,u=/^(["']).*\1$/,v=/^\d+$/,w=/^false|true$/i;b.fn[l]=function(){var e=[],g=0,m=b(this),s=arguments,y=s.length,z=y>0&&s[y-1][k]instanceof f?i.call(s):c;return m.filter(r).each(function(f,i){var k=b(i),m=k.data(),r=k.attr(p)||"",y=/[\s,]*([\w_\-\/]+)(?:\(([^\)]+)\))?/g,z=[],A=g,B=0,C;k.data(l,r).data(n,z).removeAttr(p);while(C=y.exec(r))b.Deferred(function(f){var h=B++,i,j,l,n;e[g++]=f,f.done(function(b){z[h]=b});var p=C[1],q=[k,p];for(i=0,l=s.length,j=q.length;i<l;i++,j++)q[j]=s[i];var r=C[2];if(r!==c){r=r.split(t);for(i=0,l=r.length,j=q.length;i<l;i++,j++)n=r[i],n in m?q[j]=m[n]:u.test(n)?q[j]=n.slice(1,-1):v.test(n)?q[j]=Number(n):w.test(n)?q[j]=n===d:q[j]=n}require([p],function(c){var d=c.apply(c,q).bind(o,x);b.Deferred(function(b){d.start(b)}).done(function(){f.resolve(d)}).fail(f.reject)})});j.apply(b,e.slice(A,g)).done(function(){k.attr(q,h.call(arguments," "))})}),z&&j.apply(b,e).then(z.resolve,z.reject),m},b.fn[m]=function(c){var d=[],e=0,f=b(this);return f.filter(s).each(function(c,f){var g=b(f),h=g.data(n),i;g.removeData(n).removeAttr(q);while(i=h.shift())b.Deferred(function(b){d[e++]=b,i.stop(b)});g.attr(p,g.data(l)).removeData(l).unbind(o,x)}),c&&j.apply(b,d).then(c.resolve,c.reject),f}})
\ No newline at end of file
define( [ "troopjs-core/widget/application", "troopjs-core/route/router", "jquery" ], function ApplicationModule(Application, Router, $) {
function forward(signal, deferred) {
var services = $.map(this.services, function map(service, index) {
return $.Deferred(function deferredSignal(deferSignal) {
service.signal(signal, deferSignal);
});
});
if (deferred) {
$.when.apply($, services).then(deferred.resolve, deferred.reject);
}
}
return Application.extend({
"sig/initialize" : forward,
"sig/finalize" : forward,
"sig/start" : forward,
"sif/stop" : forward,
services : [ Router($(window)) ]
});
});
......@@ -5,20 +5,10 @@ define( [ "troopjs-core/component/widget", "jquery" ], function ClearModule(Widg
}
return Widget.extend({
"hub/todos/change" : function onChange(topic, items) {
"hub:memory/todos/change" : function onChange(topic, items) {
var count = $.grep(items, filter, true).length;
var $element = this.$element;
if (count > 0) {
$element
.text("Clear " + count + (count > 1 ? " completed items" : " completed item"))
.show();
}
else {
$element
.text("Clear no completed items")
.hide();
}
this.$element.text("Clear completed (" + count + ")")[count > 0 ? "show" : "hide"]();
},
"dom/click" : function onClear(topic, $event) {
......
......@@ -5,16 +5,10 @@ define( [ "troopjs-core/component/widget", "jquery" ], function CountModule(Widg
}
return Widget.extend({
"hub/todos/change" : function onChange(topic, items) {
"hub:memory/todos/change" : function onChange(topic, items) {
var count = $.grep(items, filter, true).length;
var $element = this.$element;
if (count > 0) {
$element.text(count + (count > 1 ? " items left" : " item left"));
}
else {
$element.text("No items left");
}
this.$element.html("<strong>" + count + "</strong> " + (count === 1 ? "item" : "items") + " left");
}
});
});
\ No newline at end of file
});
define( [ "troopjs-core/component/widget" ], function CreateModule(Widget) {
var ENTER_KEY = 13;
return Widget.extend({
"dom/keyup" : function onKeyUp(topic, $event) {
var self = this;
var $element = self.$element;
var value;
switch($event.keyCode) {
case 13:
self.publish("todos/add", $element.val());
case ENTER_KEY:
value = $element.val().trim();
if (value !== "") {
self.publish("todos/add", value);
$element.val("");
$element.val("");
}
}
}
});
});
\ No newline at end of file
});
......@@ -5,8 +5,8 @@ define( [ "troopjs-core/component/widget", "jquery" ], function DisplayModule(Wi
}
return Widget.extend({
"hub/todos/change": function onChange(topic, items) {
this.$element[$.grep(items, filter, true).length > 0 ? "show" : "hide"]("fast");
"hub:memory/todos/change": function onChange(topic, items) {
this.$element[$.grep(items, filter, true).length > 0 ? "show" : "hide"]();
}
});
});
\ No newline at end of file
});
define( [ "troopjs-core/component/widget", "jquery" ], function FiltersModule(Widget, $) {
return Widget.extend({
"hub:memory/route" : function onRoute(topic, uri) {
this.publish("todos/filter", uri.source);
},
"hub:memory/todos/filter" : function onFilter(topic, filter) {
filter = filter || "/";
// Update UI
$("a[href^='#']")
.removeClass("selected")
.filter("[href='#" + filter + "']")
.addClass("selected");
}
});
});
<%
var i = data.i;
var item = data.item;
if (item.completed) { %>
<li class="done">
<div class="view">
<input class="toggle" type="checkbox" data-action="status(<%= i %>)" checked>
<label data-action="prepare.dblclick(<%= i %>)"><%= item.text %></label>
<a class="destroy" data-action="delete(<%= i %>)"></a>
<a class="prepare" data-action="prepare.click(<%= i %>)"></a>
var completed = item.completed;
%>
<li class="<%= (completed ? 'completed' : 'active') %>">
<div class="view" data-action="prepare(<%= i %>)">
<input class="toggle" type="checkbox" <%= (completed ? 'checked' : '') %> data-action="status(<%= i %>)">
<label><%= item.title %></label>
<button class="destroy" data-action="delete(<%= i %>)"></button>
</div>
<input class="edit" type="text" data-action="update(<%= i %>)">
</li>
<% } else { %>
<li>
<div class="view">
<input class="toggle" type="checkbox" data-action="status(<%= i %>)">
<label data-action="prepare.dblclick(<%= i %>)"><%= item.text %></label>
<a class="destroy" data-action="delete(<%= i %>)"></a>
<a class="prepare" data-action="prepare.click(<%= i %>)"></a>
</div>
<input class="edit" type="text" data-action="update(<%= i %>)">
</li>
<% } %>
\ No newline at end of file
<input class="edit" data-action="commit(<%= i %>)">
</li>
\ No newline at end of file
define( [ "troopjs-core/component/widget", "troopjs-core/store/local", "jquery", "template!./item.html" ], function ListModule(Widget, store, $, template) {
var ENTER_KEY = 13;
var FILTER_ACTIVE = "filter-active";
var FILTER_COMPLETED = "filter-completed";
function filter(item, index) {
return item === null;
......@@ -8,14 +11,14 @@ define( [ "troopjs-core/component/widget", "troopjs-core/store/local", "jquery",
var self = this;
// Defer initialization
$.Deferred(function deferredInit(dfdInit) {
$.Deferred(function deferredInit(deferInit) {
// Defer get
$.Deferred(function deferredGet(dfdGet) {
store.get(self.config.store, dfdGet);
$.Deferred(function deferredGet(deferGet) {
store.get(self.config.store, deferGet);
})
.done(function doneGet(items) {
// Set items (empty or compacted) - then resolve
store.set(self.config.store, items === null ? [] : $.grep(items, filter, true), dfdInit);
store.set(self.config.store, items === null ? [] : $.grep(items, filter, true), deferInit);
});
})
.done(function doneInit(items) {
......@@ -32,22 +35,23 @@ define( [ "troopjs-core/component/widget", "troopjs-core/store/local", "jquery",
self.publish("todos/change", items);
});
}, {
"hub/todos/add" : function onAdd(topic, text) {
"hub/todos/add" : function onAdd(topic, title) {
var self = this;
// Defer set
$.Deferred(function deferredSet(dfdSet) {
$.Deferred(function deferredSet(deferSet) {
// Defer get
$.Deferred(function deferredGet(dfdGet) {
store.get(self.config.store, dfdGet);
$.Deferred(function deferredGet(deferGet) {
store.get(self.config.store, deferGet);
})
.done(function doneGet(items) {
// Get the next index
var i = items.length;
// Create new item, store in items
var item = items[i] = {
"completed": false,
"text": text
"title": title
};
// Append new item to self
......@@ -55,10 +59,9 @@ define( [ "troopjs-core/component/widget", "troopjs-core/store/local", "jquery",
"i": i,
"item": item
});
})
.done(function doneGet(items) {
// Set items and resolve set
store.set(self.config.store, items, dfdSet);
store.set(self.config.store, items, deferSet);
});
})
.done(function doneSet(items) {
......@@ -71,7 +74,28 @@ define( [ "troopjs-core/component/widget", "troopjs-core/store/local", "jquery",
},
"hub/todos/clear" : function onClear(topic) {
this.$element.find("li.done a.destroy").click();
this.$element.find(".completed .destroy").click();
},
"hub:memory/todos/filter" : function onFilter(topic, filter) {
var $element = this.$element;
switch (filter) {
case "/completed":
$element
.removeClass(FILTER_ACTIVE)
.addClass(FILTER_COMPLETED);
break;
case "/active":
$element
.removeClass(FILTER_COMPLETED)
.addClass(FILTER_ACTIVE);
break;
default:
$element.removeClass([FILTER_ACTIVE, FILTER_COMPLETED].join(" "));
}
},
"dom/action.change.click.dblclick.focusout.keyup" : $.noop,
......@@ -84,21 +108,21 @@ define( [ "troopjs-core/component/widget", "troopjs-core/store/local", "jquery",
// Update UI
$target
.closest("li")
.toggleClass("done", completed);
.toggleClass("completed", completed)
.toggleClass("active", !completed);
// Defer set
$.Deferred(function deferredSet(dfdSet) {
$.Deferred(function deferredSet(deferSet) {
// Defer get
$.Deferred(function deferredGet(dfdGet) {
store.get(self.config.store, dfdGet);
$.Deferred(function deferredGet(deferGet) {
store.get(self.config.store, deferGet);
})
.done(function doneGet(items) {
// Update completed
items[index].completed = completed;
})
.done(function doneGet(items) {
// Set items and resolve set
store.set(self.config.store, items, dfdSet);
store.set(self.config.store, items, deferSet);
});
})
.done(function doneSet(items) {
......@@ -112,25 +136,21 @@ define( [ "troopjs-core/component/widget", "troopjs-core/store/local", "jquery",
// Update UI
$($event.target)
.closest("li")
.hide("slow", function hidden() {
// Remove LI
$(this).remove();
});
.remove();
// Defer set
$.Deferred(function deferredSet(dfdSet) {
$.Deferred(function deferredSet(deferSet) {
// Defer get
$.Deferred(function deferredGet(dfdGet) {
$.Deferred(function deferredGet(deferGet) {
// Get the items
store.get(self.config.store, dfdGet);
store.get(self.config.store, deferGet);
})
.done(function doneGet(items) {
// Delete item
items[index] = null;
})
.done(function doneGet(items) {
// Set items and resolve set
store.set(self.config.store, items, dfdSet);
store.set(self.config.store, items, deferSet);
});
})
.done(function doneSet(items) {
......@@ -138,70 +158,89 @@ define( [ "troopjs-core/component/widget", "troopjs-core/store/local", "jquery",
});
},
"dom/action/prepare.click.dblclick" : function onPrepare(topic, $event, index) {
"dom/action/prepare.dblclick" : function onPrepare(topic, $event, index) {
var self = this;
var $li = $($event.target).closest("li");
// Update UI
$li.addClass("preparing");
// Get LI and update
var $li = $($event.target)
.closest("li")
.addClass("editing");
// Defer set
$.Deferred(function deferredSet(dfdSet) {
// Defer get
$.Deferred(function deferredGet(dfdGet) {
// Get items
store.get(self.config.store, dfdGet);
})
.done(function doneGet(items) {
// Update UI
$li
.removeClass("preparing")
.addClass("editing")
.find("input")
.val(items[index].text)
.focus();
});
// Get INPUT and disable
var $input = $li
.find("input")
.prop("disabled", true);
// Defer get
$.Deferred(function deferredGet(deferGet) {
// Get items
store.get(self.config.store, deferGet);
})
.done(function doneGet(items) {
// Update input value, enable and select
$input
.val(items[index].title)
.removeProp("disabled")
.select();
})
.fail(function failGet() {
$li.removeClass("editing");
});
},
"dom/action/update.keyup" : function onUpdateKeyUp(topic, $event) {
"dom/action/commit.keyup" : function onCommitKeyUp(topic, $event) {
switch($event.originalEvent.keyCode) {
case 13:
case ENTER_KEY:
$($event.target).focusout();
}
},
"dom/action/update.focusout" : function onUpdateFocusOut(topic, $event, index) {
"dom/action/commit.focusout" : function onCommitFocusOut(topic, $event, index) {
var self = this;
var $target = $($event.target);
var text = $target.val();
// Update UI
$target
.closest("li")
.removeClass("editing")
.find("label")
.text(text);
// Defer set
$.Deferred(function deferredSet(dfdSet) {
// Defer get
$.Deferred(function deferredGet(dfdGet) {
// Get items
store.get(self.config.store, dfdGet);
})
.done(function doneGet(items) {
// Update text
items[index].text = text;
})
.done(function doneGet(items) {
// Set items and resolve set
store.set(self.config.store, items, dfdSet);
var title = $target.val().trim();
if (title === "") {
$target
.closest("li.editing")
.removeClass("editing")
.find(".destroy")
.click();
}
else {
// Defer set
$.Deferred(function deferredSet(deferSet) {
// Disable
$target.prop("disabled", true);
// Defer get
$.Deferred(function deferredGet(deferGet) {
// Get items
store.get(self.config.store, deferGet);
})
.done(function doneGet(items) {
// Update text
items[index].title = title;
// Set items and resolve set
store.set(self.config.store, items, deferSet);
});
})
.done(function doneSet(items) {
// Update UI
$target
.closest("li")
.removeClass("editing")
.find("label")
.text(title);
self.publish("todos/change", items);
})
.always(function alwaysSet() {
// Enable
$target.removeProp("disabled");
});
});
}
}
});
});
\ No newline at end of file
});
define( [ "troopjs-core/component/widget" ], function MarkModule(Widget) {
var INDETERMINATE = "indeterminate";
var CHECKED = "checked";
return Widget.extend({
"hub/todos/change" : function onChange(topic, items) {
"hub:memory/todos/change" : function onChange(topic, items) {
var total = 0;
var count = 0;
var $element = this.$element;
......@@ -22,18 +20,18 @@ define( [ "troopjs-core/component/widget" ], function MarkModule(Widget) {
if (count === 0) {
$element
.prop(INDETERMINATE, false)
.prop(CHECKED, false);
.prop("indeterminate", false)
.prop("checked", false);
}
else if (count === total) {
$element
.prop(INDETERMINATE, false)
.prop(CHECKED, true);
.prop("indeterminate", false)
.prop("checked", true);
}
else {
$element
.prop(INDETERMINATE, true)
.prop(CHECKED, false);
.prop("indeterminate", true)
.prop("checked", false);
}
},
......@@ -41,4 +39,4 @@ define( [ "troopjs-core/component/widget" ], function MarkModule(Widget) {
this.publish("todos/mark", $($event.target).prop("checked"));
}
});
});
\ No newline at end of file
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment