Commit 4e3769bf authored by Sam Saccone's avatar Sam Saccone

Merge pull request #1514 from tastejs/sjs/troop-fixup

Update TroopJS to latest 3.0.x and new template.
parents 5136600f 126da65a
node_modules/*
!node_modules/todomvc-common/base.*
!node_modules/todomvc-app-css/index.css
!node_modules/jquery/dist/jquery.js
!node_modules/mu-emitter/main.js
!node_modules/mu-emitter/config.js
!node_modules/mu-emitter/handler.js
!node_modules/mu-emitter/executor.js
!node_modules/mu-emitter/error.js
!node_modules/mu-error/factory.js
!node_modules/mu-getargs/main.js
!node_modules/mu-getargs/src/*
!node_modules/mu-jquery-destroy/main.js
!node_modules/mu-jquery-destroy/src/*
!node_modules/mu-jquery-hashchange/jquery.hashchange.js
!node_modules/mu-merge/main.js
!node_modules/mu-selector-set/main.js
!node_modules/mu-selector-set/src/*
!node_modules/poly/lib/*
!node_modules/poly/support/*
!node_modules/poly/array.js
!node_modules/poly/json.js
!node_modules/poly/string.js
!node_modules/requirejs/require.js
!node_modules/troopjs/main.js
!node_modules/troopjs-route-hash/main.js
!node_modules/troopjs-widget/main.js
!node_modules/when/lib/state.js
!node_modules/when/lib/makePromise.js
!node_modules/when/lib/env.js
!node_modules/when/lib/Promise.js
!node_modules/when/lib/Scheduler.js
!node_modules/when/lib/format.js
!node_modules/when/lib/decorators/*
!node_modules/when/lib/apply.js
!node_modules/when/lib/TimeoutError.js
!node_modules/when/when.js
{
"name" : "todomvc-troopjs",
"version" : "2.0.0-SNAPSHOT",
"main" : "index.html",
"dependencies" : {
"todomvc-common" : "*",
"requirejs" : "~2.1",
"jquery" : "~2.0",
"troopjs-bundle" : "https://github.com/troopjs/troopjs-bundle/archive/build/2.x.zip"
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
/* base.css overrides */ /* base.css overrides */
#footer, #main { .footer,
display: none; .main,
} .clear-completed,
.filter-active .completed, .filter-active .completed,
.filter-completed .active { .filter-completed .active {
display: none; display: none;
......
...@@ -2,24 +2,37 @@ ...@@ -2,24 +2,37 @@
<html lang="en" data-framework="troopjs"> <html lang="en" data-framework="troopjs">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>TroopJS • TodoMVC</title> <title>TroopJS • TodoMVC</title>
<link rel="stylesheet" href="bower_components/todomvc-common/base.css"> <link rel="stylesheet" href="node_modules/todomvc-common/base.css">
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
<link rel="stylesheet" href="css/app.css"> <link rel="stylesheet" href="css/app.css">
</head> </head>
<body> <body>
<section id="todoapp"> <section class="todoapp">
<header id="header"> <header class="header">
<h1>todos</h1> <h1>todos</h1>
<input id="new-todo" placeholder="What needs to be done?" autofocus data-weave="troopjs-todos/widget/create"> <input class="new-todo" placeholder="What needs to be done?" autofocus data-weave="todos-troopjs/create">
</header> </header>
<section id="main" data-weave="troopjs-todos/widget/display"> <section class="main" data-weave="todos-troopjs/hide">
<input id="toggle-all" type="checkbox" data-weave="troopjs-todos/widget/mark"> <input class="toggle-all" type="checkbox" data-weave="todos-troopjs/toggle">
<label for="toggle-all">Mark all as complete</label> <label for="toggle-all">Mark all as complete</label>
<ul id="todo-list" data-weave="troopjs-todos/widget/list"></ul> <ul class="todo-list" data-weave="todos-troopjs/list">
<script type="text/x-html-template">
<li>
<div class="view">
<input class="toggle" type="checkbox">
<label></label>
<button class="destroy"></button>
</div>
<input class="edit">
</li>
</script>
</ul>
</section> </section>
<footer id="footer" data-weave="troopjs-todos/widget/display"> <footer class="footer" data-weave="todos-troopjs/hide">
<span id="todo-count" data-weave="troopjs-todos/widget/count"><strong>1</strong> item left</span> <span class="todo-count" data-weave="todos-troopjs/count"><strong>0</strong> items left</span>
<ul id="filters" data-weave="troopjs-todos/widget/filters"> <ul class="filters" data-weave="todos-troopjs/filter">
<li> <li>
<a class="selected" href="#/">All</a> <a class="selected" href="#/">All</a>
</li> </li>
...@@ -30,48 +43,15 @@ ...@@ -30,48 +43,15 @@
<a href="#/completed">Completed</a> <a href="#/completed">Completed</a>
</li> </li>
</ul> </ul>
<button id="clear-completed" data-weave="troopjs-todos/widget/clear">Clear completed</button> <button class="clear-completed" data-weave="todos-troopjs/clear">Clear completed</button>
</footer> </footer>
</section> </section>
<footer id="info"> <footer class="info">
<p>Double-click to edit a todo</p> <p>Double-click to edit a todo</p>
<p>Created by <a href="https://github.com/mikaelkaron">Mikael Karon</a></p> <p>Created by <a href="http://mikael.karon.se">Mikael Karon</a> for the <a href="http://troopjs.com">TroopJS</a> team</p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p> <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer> </footer>
<script src="bower_components/todomvc-common/base.js"></script> <script src="node_modules/todomvc-common/base.js"></script>
<script> <script src="node_modules/requirejs/require.js" data-main="index.js"></script>
'use strict';
var require = {
packages: [{
name: 'jquery',
location: 'bower_components/jquery',
main: 'jquery'
}, {
name: 'troopjs-bundle',
location: 'bower_components/troopjs-bundle',
main: 'maxi'
}, {
name: 'troopjs-todos',
location: 'js'
}],
map: {
'*': {
template: 'troopjs-requirejs/template'
}
},
deps: [ 'require', 'jquery', 'troopjs-bundle' ],
callback: function Boot (contextRequire, jQuery) {
contextRequire([ 'troopjs-browser/application/widget', 'troopjs-browser/route/widget' ], function Strap (Application, RouteWidget) {
jQuery(function ($) {
Application($('html'), 'bootstrap', RouteWidget($(window), 'route')).start();
});
});
}
};
</script>
<script src="bower_components/requirejs/require.js" async="async"></script>
</body> </body>
</html> </html>
require({
baseUrl: 'node_modules',
packages: [{
name: 'todos-troopjs',
location: '../js'
}],
paths: {
jquery: 'jquery/dist/jquery'
},
deps: [
'require',
'jquery',
'troopjs/main',
'troopjs-widget/main',
'troopjs-route-hash/main'
],
callback: function (localRequire, jQuery) {
// Require "application" and "route" components
localRequire([
'troopjs-widget/application',
'troopjs-route-hash/component'
], function (Application, Route) {
// Wait for `document.ready`
jQuery(function ($) {
// Create `Application` attached to `$('html')`, add `Route` child component and `.start`
Application($('html'), 'bootstrap', Route($(window))).start();
});
});
}
});
define([
'./component',
'troopjs-hub/emitter',
'poly/array'
], function (Component, hub) {
'use strict';
/**
* Component for the `.clear-completed` button
*/
return Component.extend({
/**
* HUB `todos/change` handler (memorized).
* Called whenever the task list is updated
* @param {Array} tasks Updated task array
*/
'hub/todos/change(true)': function (tasks) {
// Filter and count tasks that are completed
var count = tasks
.filter(function (task) {
return task.completed;
})
.length;
// Toggle visibility (visible if `count > 0`, hidden otherwise)
this.$element.toggle(count > 0);
},
/**
* DOM `click` handler
*/
'dom/click': function () {
// Emit `todos/clear` on `hub`
hub.emit('todos/clear');
}
});
});
define([
'troopjs-compose/factory',
'troopjs-hub/component',
'troopjs-dom/component'
], function (Factory, HUBComponent, DOMComponent) {
'use strict';
/**
* Base component.
* Mixes `HUBComponent` and `DOMComponent`.
*/
return Factory(HUBComponent, DOMComponent);
});
define([
'./component',
'poly/array'
], function (Component) {
'use strict';
/**
* Component for the `.todo-count` span
*/
return Component.extend({
/**
* HUB `todos/change` handler (memorized).
* Called whenever the task list is updated
* @param {Array} tasks Updated task array
*/
'hub/todos/change(true)': function (tasks) {
// Filter and count tasks that are not completed
var count = tasks
.filter(function (task) {
return !task.completed;
})
.length;
// Update HTML with `count` taking pluralization into account
this.$element.html('<strong>' + count + '</strong> ' + (count === 1 ? 'item' : 'items') + ' left');
}
});
});
define([
'./component',
'troopjs-hub/emitter'
], function (Component, hub) {
'use strict';
/**
* Component for the `.new-todo` input
*/
var ENTER_KEY = 13;
return Component.extend({
/**
* DOM `keyup` handler
* @param {Object} $event jQuery-like `$.Event` object
*/
'dom/keyup': function ($event) {
var $element = this.$element;
var value;
// If we pressed enter ...
if ($event.keyCode === ENTER_KEY) {
// Get `$element` value and trim whitespaces
value = $element
.val()
.trim();
if (value !== '') {
hub
.emit('todos/add', value)
// Wait for all handlers to execute
.then(function () {
// Set `$element` value to `''`
$element.val('');
});
}
}
}
});
});
define([
'./component',
'troopjs-hub/emitter'
], function (Component, hub) {
'use strict';
/**
* Component for the `.filters` list
*/
return Component.extend({
/**
* HUB `route/change` handler (memorized).
* Called whenever the route (`window.location.hash`) is updated.
* @param {String} route New route
*/
'hub/route/change(true)': function (route) {
return hub
// Emit `todos/filter` with the new `route` on `hub`, yield `void 0` to not mutate arguments for next handler
.emit('todos/filter', route)
.yield();
},
/**
* HUB `todos/filter` handler.
* Called whenever the task list filter is updated
* @param {String} filter New filter
*/
'hub/todos/filter': function (filter) {
this.$element
// Find all `a` elements with a `href` attribute staring with `#`
.find('a[href^="#"]')
// Remove the `selected` class from matched elements
.removeClass('selected')
// Filter matched elements with a `href` attribute matching (`filter` || `/`)
.filter('[href="#' + (filter || '/') + '"]')
// Add the `selected` to matching elements
.addClass('selected');
}
});
});
define([
'./component'
], function (Component) {
'use strict';
/**
* Component for handling visibility depending on tasks count
*/
return Component.extend({
/**
* HUB `todos/change` handler (memorized).
* Called whenever the task list is updated
* @param {Array} tasks Updated task array
*/
'hub/todos/change(true)': function (tasks) {
// Toggle visibility (visible if `task.length !== 0`, hidden otherwise)
this.$element.toggle(tasks.length !== 0);
}
});
});
define([
'./component',
'troopjs-hub/emitter',
'jquery',
'poly/array',
'poly/string',
'poly/json'
], function (Component, hub, $) {
'use strict';
/**
* Component for the `.todo-list` list
*/
var ENTER_KEY = 13;
var ESC_KEY = 27;
var STORAGE = window.localStorage;
/**
* Generates `tasks` JSON from HTML and triggers `todos/change` on `hub`
* @private
*/
function update() {
var tasks = this.$element
// Find all `li` `.children`
.children('li')
// `.map` to JSON
.map(function (index, task) {
var $task = $(task);
return {
title: $task
.find('label')
.text(),
completed: $task
.find('.toggle')
.prop('checked')
};
})
// Get underlying Array
.get();
// Emit `todos/change` with the new `tasks` on `hub`
return hub.emit('todos/change', tasks);
}
return Component.extend({
/**
* SIG `start` handler.
* Called when this component is started
*/
'sig/start': function () {
// Get previously stored tasks from `STORAGE` under the key `todos-troopjs`
var storage = STORAGE.getItem('todos-troopjs');
// Emit `todos/change` with deserialized tasks or `[]` on `hub`
return hub.emit('todos/change', storage !== null ? JSON.parse(storage) : []);
},
/**
* HUB `todos/change` handler (memorized).
* Called whenever the task list is updated
* @param {Array} tasks Updated task array
*/
'hub/todos/change(true)': function (tasks) {
var $element = this.$element;
// Get template HTML
var template = $element
.find('script[type="text/x-html-template"]')
.text();
$element
// `.remove` all `li` `.children`
.children('li')
.remove()
.end()
// `.append` the output from `tasks.map`
.append(tasks.map(function (task) {
var title = task.title;
var completed = task.completed;
// Create template element and populate
return $(template)
.addClass(completed ? 'completed' : 'active')
.find('.toggle')
.prop('checked', completed)
.end()
.find('label')
.text(title)
.end();
}));
// Serialize `tasks` to JSON and store in `STORAGE` under the key `todos-troopjs`
STORAGE.setItem('todos-troopjs', JSON.stringify(tasks));
},
/**
* HUB `todos/add` handler.
* Called when a new task is created
* @param {String} title Task title
*/
'hub/todos/add': function (title) {
var $element = this.$element;
// Get template HTML
var template = $element
.find('script[type="text/x-html-template"]')
.text();
// Create template element, populate and `.appendTo` `$element`
$(template)
.addClass('active')
.find('label')
.text(title)
.end()
.appendTo($element);
// Call `update`, yield `void 0` to not mutate arguments for next handler
return update.call(this)
.yield(void 0);
},
/**
* HUB `todos/complete` handler.
* Called whenever all tasks change their `completed` state in bulk
* @param {Boolean} toggle Completed state
*/
'hub/todos/complete': function (toggle) {
// Find all `.toggle` elements, set `checked` property to `toggle`, trigger `change` DOM event
this.$element
.find('.toggle')
.prop('checked', toggle)
.change();
// Call `update`, yield `void 0` to not mutate arguments for next handler
return update.call(this)
.yield(void 0);
},
/**
* HUB `todos/clear` handler.
* Called whenever all completed tasks should be cleared
*/
'hub/todos/clear': function () {
// Find `.destroy` elements that are a descendants of `li:has(.toggle:checked)`, trigger `click` DOM event
this.$element
.find('li:has(.toggle:checked) .destroy')
.click();
},
/**
* HUB `todos/filter` handler.
* Called whenever the task list filter is updated
* @param {String} filter New filter
*/
'hub/todos/filter(true)': function (filter) {
var $element = this.$element;
// Toggle CSS classes depending on `filter`
$element
.toggleClass('filter-completed', filter === '/completed')
.toggleClass('filter-active', filter === '/active');
},
/**
* DOM `change` handler (delegate filter `.toggle`)
* Called when "task completed" checkbox is toggled
* @param {Object} $event jQuery-like `$.Event` object
*/
'dom/change(.toggle)': function ($event) {
var $target = $($event.target);
var toggle = $target.prop('checked');
// Toggle CSS classes depending on `toggle`
$target
.closest('li')
.toggleClass('completed', toggle)
.toggleClass('active', !toggle);
// Call `update`
update.call(this);
},
/**
* DOM `click` handler (delegate filter `.destroy`)
* Called whenever "task destroy" is clicked
* @param {Object} $event jQuery-like `$.Event` object
*/
'dom/click(.destroy)': function ($event) {
// `.remove` `.closest` `li`
$($event.target)
.closest('li')
.remove();
// Call `update`
update.call(this);
},
/**
* DOM `dblclick` handler (delegate filter `label`)
* Called whenever "task title" is edited
* @param {Object} $event jQuery-like `$.Event` object
*/
'dom/dblclick(label)': function ($event) {
var $target = $($event.target);
$target
// Add class `editing` to `.closest` `li`,
.closest('li')
.addClass('editing')
// `.find` `.edit` and update `.val` with `$target.text()`,
.find('.edit')
.val($target.text())
// `.focus` to trigger DOM event
.focus();
},
/**
* DOM `keyup` handler (delegate filter `.edit`)
* Called each time a key is released while editing "task title"
* @param {Object} $event jQuery-like `$.Event` object
*/
'dom/keyup(.edit)': function ($event) {
var $target = $($event.target);
switch ($event.keyCode) {
case ESC_KEY:
// Restore "task title" `.val` from `label`
$target.val(function () {
return $(this)
.closest('li')
.find('label')
.text();
});
// falls through
case ENTER_KEY:
// Trigger `focusout` DOM event
$target.focusout();
break;
}
},
/**
* DOM `focusout` handler (delegate filter `.edit`)
* Called when focus is removed while editing "task title"
* @param {Object} $event jQuery-like `$.Event` object
*/
'dom/focusout(.edit)': function ($event) {
// `.removeClass` `editing` from `.closest` `li`
$($event.target)
.closest('li')
.removeClass('editing');
},
/**
* DOM `change` handler (delegate filter `.edit`)
* Called when "task title" is changed
* @param {Object} $event jQuery-like `$.Event` object
*/
'dom/change(.edit)': function ($event) {
var $target = $($event.target);
// Get and `.trim` `.val`
var title = $target
.val()
.trim();
// If `title` is _not_ empty ...
if (title !== '') {
// Update `val` with trimmed `title`, update `.closest` `li` descendant `label` `.text` with `title`
$target
.val(title)
.closest('li')
.find('label')
.text(title);
// Call `update`
update.call(this);
// ... otherwise
} else {
// Find `.closest` `li` ascendant, `.find` `.destroy`, trigger `click` DOM event
$target
.closest('li')
.find('.destroy')
.click();
}
}
});
});
define([
'./component',
'troopjs-hub/emitter',
'poly/array'
], function (Component, hub) {
'use strict';
/**
* Component for `toggle-all` checkbox
*/
return Component.extend({
/**
* HUB `todos/change` handler (memorized).
* Called whenever the task list is updated
* @param {Array} tasks Updated task array
*/
'hub/todos/change(true)': function (tasks) {
// Set `this.$element` `checked` property based on all `tasks` `.completed` state
this.$element.prop('checked', tasks.every(function (task) {
return task.completed;
}, true));
},
/**
* DOM `change` handler
*/
'dom/change': function () {
// Emit `todos/complete` on `hub` with `this.$element` `checked` property
hub.emit('todos/complete', this.$element.prop('checked'));
}
});
});
/*global define:false */
define([
'troopjs-browser/component/widget',
'poly/array'
], function ClearModule(Widget) {
'use strict';
function filter(item) {
return item !== null && item.completed;
}
return Widget.extend({
'hub:memory/todos/change': function onChange(items) {
var count = items.filter(filter).length;
this.$element.toggle(count > 0);
},
'dom/click': function onClear() {
this.publish('todos/clear');
}
});
});
/*global define:false */
define([
'troopjs-browser/component/widget',
'poly/array'
], function CountModule(Widget) {
'use strict';
function filter(item) {
return item !== null && !item.completed;
}
return Widget.extend({
'hub:memory/todos/change': function onChange(items) {
var count = items.filter(filter).length;
this.$element.html('<strong>' + count + '</strong> ' + (count === 1 ? 'item' : 'items') + ' left');
}
});
});
/*global define:false */
define([ 'troopjs-browser/component/widget' ], function CreateModule(Widget) {
'use strict';
var ENTER_KEY = 13;
return Widget.extend({
'dom/keyup': function onKeyUp($event) {
var me = this;
var $element = me.$element;
var value;
if ($event.keyCode === ENTER_KEY) {
value = $element.val().trim();
if (value !== '') {
me.publish('todos/add', value)
.then(function () {
$element.val('');
});
}
}
}
});
});
/*global define:false */
define([
'troopjs-browser/component/widget',
'poly/array'
], function DisplayModule(Widget) {
'use strict';
function filter(item) {
return item !== null;
}
return Widget.extend({
'hub:memory/todos/change': function onChange(items) {
this.$element.toggle(items.some(filter));
}
});
});
/*global define:false */
define([
'troopjs-browser/component/widget',
'jquery'
], function FiltersModule(Widget, $) {
'use strict';
return Widget.extend({
'hub:memory/route': function onRoute(uri) {
this.publish('todos/filter', uri.source);
},
'hub:memory/todos/filter': function onFilter(filter) {
$('a[href^="#"]')
.removeClass('selected')
.filter('[href="#' + (filter || '/') + '"]')
.addClass('selected');
}
});
});
<%
var i = data.i;
var item = data.item;
var completed = item.completed;
function htmlEscape(str) {
return String(str)
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}
%>
<li class="<%= (completed ? 'completed' : 'active') %>" data-index="<%= i %>">
<div class='view'>
<input class="toggle" type="checkbox" <%= (completed ? 'checked' : '') %>>
<label><%= htmlEscape(item.title) %></label>
<button class="destroy"></button>
</div>
<input class="edit">
</li>
/*global define:false */
define([
'troopjs-browser/component/widget',
'troopjs-data/store/component',
'troopjs-browser/store/adapter/local',
'jquery',
'template!./item.html',
'poly/array'
], function ListModule(Widget, Store, Adapter, $, template) {
/*jshint newcap:false*/
'use strict';
var ARRAY_SLICE = Array.prototype.slice;
var ENTER_KEY = 13;
var ESC_KEY = 27;
var FILTER_ACTIVE = 'filter-active';
var FILTER_COMPLETED = 'filter-completed';
var KEY = 'todos-troopjs';
var STORE = 'store';
function filter(item) {
return item !== null;
}
return Widget.extend(function ListWidget() {
this[STORE] = Store(Adapter());
}, {
'sig/start': function () {
var me = this;
var store = me[STORE];
return store.ready(function () {
return store.get(KEY, function (getItems) {
return store.set(KEY, getItems && getItems.filter(filter) || [], function (setItems) {
setItems.forEach(function (item, i) {
me.append(template, {
i: i,
item: item
});
});
me.publish('todos/change', setItems);
});
});
});
},
'hub/todos/add': function onAdd(title) {
var me = this;
var store = me[STORE];
return store.ready(function () {
return store.get(KEY, function (getItems) {
var i = getItems.length;
var item = getItems[i] = {
completed: false,
title: title
};
me.append(template, {
i: i,
item: item
});
return store.set(KEY, getItems, function (setItems) {
me.publish('todos/change', setItems);
});
});
})
.yield(ARRAY_SLICE.call(arguments));
},
'hub/todos/mark': function onMark(value) {
this.$element.find(':checkbox').prop('checked', value).change();
},
'hub/todos/clear': function onClear() {
this.$element.find('.completed .destroy').click();
},
'hub:memory/todos/filter': function onFilter(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:.toggle/change': function onToggleChange($event) {
var me = this;
var store = me[STORE];
var $target = $($event.currentTarget);
var completed = $target.prop('checked');
var $li = $target.closest('li');
var index = $li.data('index');
$li
.toggleClass('completed', completed)
.toggleClass('active', !completed);
store.ready(function () {
return store.get(KEY, function (getItems) {
getItems[index].completed = completed;
return store.set(KEY, getItems, function (setItems) {
me.publish('todos/change', setItems);
});
});
});
},
'dom:.destroy/click': function onDestroyClick($event) {
var me = this;
var store = me[STORE];
var $li = $($event.currentTarget).closest('li');
var index = $li.data('index');
$li.remove();
store.ready(function () {
return store.get(KEY, function (getItems) {
getItems[index] = null;
return store.set(KEY, getItems, function (setItems) {
me.publish('todos/change', setItems);
});
});
});
},
'dom:.view label/dblclick': function onViewDblClick($event) {
var me = this;
var store = me[STORE];
var $li = $($event.currentTarget).closest('li');
var index = $li.data('index');
var $input = $li.find('input');
$li.addClass('editing');
$input.prop('disabled', true);
store.ready(function () {
return store.get(KEY, function (items) {
$input
.val(items[index].title)
.prop('disabled', false)
.focus();
});
}, function () {
$input.prop('disabled', false);
$li.removeClass('editing');
});
},
'dom:.edit/keyup': function onEditKeyUp($event) {
var $li = $($event.currentTarget).closest('li');
switch ($event.keyCode) {
case ENTER_KEY:
$li
.find('input')
.focusout();
break;
case ESC_KEY:
$li
.find('input')
.val($li.find('label').text())
.focusout();
break;
}
},
'dom:.edit/focusout': function onEditFocusOut($event) {
var me = this;
var store = me[STORE];
var $target = $($event.currentTarget);
var title = $target.val().trim();
if (title === '') {
$target
.closest('li.editing')
.removeClass('editing')
.find('.destroy')
.click();
} else {
$target.prop('disabled', true);
store.ready(function () {
return store.get(KEY, function (getItems) {
var $li = $target.closest('li');
var index = $li.data('index');
getItems[index].title = title;
return store.set(KEY, getItems, function (setItems) {
$li
.removeClass('editing')
.find('label')
.text(title);
me.publish('todos/change', setItems);
});
})
.ensure(function () {
$target.prop('disabled', false);
});
});
}
}
});
});
/*global define:false */
define([
'troopjs-browser/component/widget',
'jquery',
'poly/array'
], function MarkModule(Widget, $) {
'use strict';
return Widget.extend({
'hub:memory/todos/change': function onChange(items) {
var total = 0;
var completed = 0;
var $element = this.$element;
items.forEach(function (item) {
if (item === null) {
return;
}
if (item.completed) {
completed++;
}
total++;
});
if (completed === 0) {
$element
.prop('indeterminate', false)
.prop('checked', false);
} else if (completed === total) {
$element
.prop('indeterminate', false)
.prop('checked', true);
} else {
$element
.prop('indeterminate', true)
.prop('checked', false);
}
},
'dom/change': function onMark($event) {
this.publish('todos/mark', $($event.target).prop('checked'));
}
});
});
define(function () {
/**
* @class mu.emitter.config
* @enum {String}
*/
var CONFIG = {
/**
* Property name for `handlers`
*/
"handlers": "handlers",
/**
* Property name for `emitter`
*/
"emitter": "emitter",
/**
* Property name for `type`
*/
"type": "type",
/**
* Property name for `callback`
*/
"callback": "callback",
/**
* Property name for `data`
*/
"data": "data",
/**
* Property name for `scope`
*/
"scope": "scope",
/**
* Property name for `executor`
*/
"executor": "executor",
/**
* Property name for `head`
*/
"head": "head",
/**
* Property name for `tail`
*/
"tail": "tail",
/**
* Property name for `next`
*/
"next": "next",
/**
* Property name for `count`
*/
"count": "count",
/**
* Property name for `limit`
*/
"limit": "limit"
};
return CONFIG;
});
define([ "mu-error/factory" ], function (Factory) {
"use strict";
return Factory("EmitterError");
});
\ No newline at end of file
define([ "./config" ], function (config) {
"use strict";
var UNDEFINED;
var CALLBACK = config.callback;
var SCOPE = config.scope;
var HEAD = config.head;
var NEXT = config.next;
return function executor(event, handlers, args) {
var _handlers = [];
var _handlersCount = 0;
var _callback = event[CALLBACK];
var _scope = event[SCOPE];
var handler;
for (handler = handlers[HEAD]; handler !== UNDEFINED; handler = handler[NEXT]) {
if (_callback && handler[CALLBACK] !== _callback) {
continue;
}
if (_scope && handler[SCOPE] !== _scope) {
continue;
}
_handlers[_handlersCount++] = handler;
}
return _handlers.map(function (_handler) {
return _handler.handle(args);
});
}
});
\ No newline at end of file
define([
"./config",
"./error"
], function (config, EmitterError) {
"use strict";
var UNDEFINED;
var OBJECT_TOSTRING = Object.prototype.toString;
var TOSTRING_FUNCTION = "[object Function]";
var EMITTER = config.emitter;
var TYPE = config.type;
var CALLBACK = config.callback;
var DATA = config.data;
var SCOPE = config.scope;
var LIMIT = config.limit;
var COUNT = config.count;
function Handler(emitter, type, callback, data) {
var me = this;
me[EMITTER] = emitter;
me[TYPE] = type;
me[DATA] = data;
if (OBJECT_TOSTRING.call(callback) === TOSTRING_FUNCTION) {
me[CALLBACK] = callback;
me[SCOPE] = emitter;
}
else if (callback !== UNDEFINED) {
me[CALLBACK] = callback[CALLBACK];
me[SCOPE] = callback[SCOPE] || emitter;
if (callback.hasOwnProperty(LIMIT)) {
me[LIMIT] = callback[LIMIT];
me[COUNT] = 0;
}
}
else {
throw new EmitterError("Unable to use 'callback'");
}
}
Handler.prototype.handle = function (args) {
// Let `me` be `this`
var me = this;
// Get result from execution of `handler[CALLBACK]`
var result = me[CALLBACK].apply(me[SCOPE], args);
// If there's a `me[LIMIT]` and `++me[COUNT]` is greater or equal to it ...
if (me.hasOwnProperty(LIMIT) && ++me[COUNT] >= me[LIMIT]) {
// ... `me[EMITTER].off` `me` (note that `me[CALLBACK]` and `me[SCOPE]` are used by `.off`)
me[EMITTER].off(me[TYPE], me);
}
return result;
};
return Handler;
});
define([
"./config",
"./handler",
"./executor",
"./error"
], function (config, Handler, executor, EmitterError) {
"use strict";
var UNDEFINED;
var OBJECT_TOSTRING = Object.prototype.toString;
var TOSTRING_STRING = "[object String]";
var TOSTRING_FUNCTION = "[object Function]";
var HANDLERS = config.handlers;
var EXECUTOR = config.executor;
var TYPE = config.type;
var CALLBACK = config.callback;
var SCOPE = config.scope;
var LIMIT = config.limit;
var HEAD = config.head;
var TAIL = config.tail;
var NEXT = config.next;
function Emitter() {
}
Emitter.prototype[EXECUTOR] = executor;
Emitter.prototype.on = function (type, callback, data) {
var me = this;
var handlers = me[HANDLERS] || (me[HANDLERS] = {});
var handler;
if (callback === UNDEFINED) {
throw new EmitterError("no 'callback' provided");
}
handler = new Handler(me, type, callback, data);
if (handlers.hasOwnProperty(type)) {
handlers = handlers[type];
handlers[TAIL] = handlers.hasOwnProperty(TAIL)
? handlers[TAIL][NEXT] = handler
: handlers[HEAD] = handler;
}
else {
handlers = handlers[type] = {};
handlers[TYPE] = type;
handlers[HEAD] = handlers[TAIL] = handler;
}
return handler;
};
Emitter.prototype.off = function (type, callback) {
var me = this;
var result = [];
var length = 0;
var handlers = me[HANDLERS] || (me[HANDLERS] = {});
var handler;
var head = UNDEFINED;
var tail = UNDEFINED;
var _callback;
var _scope;
if (handlers.hasOwnProperty(type)) {
handlers = handlers[type];
if (OBJECT_TOSTRING.call(callback) === TOSTRING_FUNCTION) {
_callback = callback;
_scope = me;
}
else if (callback !== UNDEFINED) {
_callback = callback[CALLBACK];
_scope = callback[SCOPE];
}
for (handler = handlers[HEAD]; handler !== UNDEFINED; handler = handler[NEXT]) {
unlink: {
if (_callback && handler[CALLBACK] !== _callback) {
break unlink;
}
if (_scope && handler[SCOPE] !== _scope) {
break unlink;
}
result[length++] = handler;
continue;
}
if (head === UNDEFINED) {
head = tail = handler;
}
else {
tail = tail[NEXT] = handler;
}
}
if (head !== UNDEFINED) {
handlers[HEAD] = head;
}
else {
delete handlers[HEAD];
}
if (tail !== UNDEFINED) {
handlers[TAIL] = tail;
delete tail[NEXT];
}
else {
delete handlers[TAIL];
}
}
return result;
};
Emitter.prototype.one = function (type, callback, data) {
var me = this;
var _callback;
if (OBJECT_TOSTRING.call(callback) === TOSTRING_FUNCTION) {
_callback = {};
_callback[CALLBACK] = callback;
_callback[LIMIT] = 1;
}
else {
_callback = callback;
_callback[LIMIT] = 1;
}
return me.on(type, _callback, data);
};
Emitter.prototype.emit = function (event) {
var me = this;
var args = arguments;
var length = args.length;
var _args = new Array(length - 1);
var _handlers = me[HANDLERS] || (me[HANDLERS] = {});
var _event;
var _type;
var _executor;
// let `args` be `Array.prototyps.slice.call(arguments, 1)` without deop
while (length-- > 1) {
_args[length - 1] = args[length];
}
// If we `event` is a string use defaults ...
if (OBJECT_TOSTRING.call(event) === TOSTRING_STRING) {
_event = {};
_type = _event[TYPE] = event;
_executor = me[EXECUTOR];
}
// ... or if we ducktype TYPE extract params ...
else if (event.hasOwnProperty(TYPE)) {
_event = event;
_type = event[TYPE];
_executor = event[EXECUTOR] || me[EXECUTOR];
}
// ... or bail out
else {
throw new EmitterError("Unable to use 'event'");
}
// If we have `_handlers[type]` use it ...
if (_handlers.hasOwnProperty(_type)) {
_handlers = _handlers[_type];
}
// ... otherwise create it
else {
_handlers = _handlers[_type] = {};
_handlers[TYPE] = _type;
}
// Call `_executor` and return
return _executor.call(me, _event, _handlers, _args);
};
return Emitter;
});
\ No newline at end of file
define(function () {
"use strict";
var CAPTURE_STACK_TRACE = Error.captureStackTrace;
return function (name) {
var _Error = CAPTURE_STACK_TRACE
? function (message) {
var me = this;
CAPTURE_STACK_TRACE(me, _Error);
Object.defineProperties(me, {
"message": {
"enumerable": false,
"value": message
}
});
}
: function (message) {
var error = new Error(message);
Object.defineProperties(this, {
"message": {
"enumerable": false,
"value": message
},
"stack": {
"enumerable": false,
"get": function () {
return error.stack.replace(/.*\n/, "");
}
}
});
};
_Error.prototype = Object.create(Error.prototype, {
"name": {
"enumerable": false,
"value": name
},
"constructor": {
"enumerable": false,
"value": _Error
}
});
return _Error;
};
});
\ No newline at end of file
(function() {
'use strict';
if (typeof define === 'function' && define.amd) {
define(['./src/getargs'], factory);
} else if (typeof exports === 'object') {
module.exports = factory(
require('./src/getargs')
);
} else {
throw Error("no module loader found");
}
function factory(getargs) {
return getargs;
}
})();
(function() {
'use strict';
/* istanbul ignore next */
if (typeof define === 'function' && define.amd) {
define([], getargsFactory);
} else if (typeof exports === 'object') {
module.exports = getargsFactory();
} else {
throw Error("no module loader found");
}
function getargsFactory() {
/**
* @class mu-lib.getargs
* @mixin Function
* @static
*/
var UNDEFINED,
STR_SUBSTRING = String.prototype.substring,
STR_SLICE = String.prototype.slice,
RE_STRING = /^(["']).*\1$/,
RE_BOOLEAN = /^(?:false|true)$/i,
RE_BOOLEAN_TRUE = /^true$/i,
RE_DIGIT = /^\d+$/;
/**
* Function that calls on a String, to parses it as function parameters delimited by commas.
*
* " 1 , '2' , 3 ,false,5 "
*
* results in
*
* [ 1, "2", 3, false, 5]
*
*
* and
*
* "'1, 2 ', 3,\"4\", 5 "
*
* results in
*
* [ "1, 2 ", 3, "4", 5 ]
*
* Also handles named parameters.
*
* "1, two=2, 3, 'key.four'=4, 5"
*
* results in
*
* result = [1, 2, 3, 4, 5]
* result["two"] === result[1]
* result["key.four"] === result[3]
*
* @method constructor
* @return {Array} the array of parsed params.
*/
return function getargs(str) {
str = str || this;
if (!(typeof str == 'string' || str instanceof String))
throw Error("First argument must be a string");
var values = [];
var from;
var to;
var index;
var length;
var quote = false;
var key;
var c;
// Try to extract value from the specified string range.
function extract(from, to) {
// Nothing captured.
if (from === to)
return;
var value = STR_SUBSTRING.call(str, from, to);
if (RE_STRING.test(value)) {
value = STR_SLICE.call(value, 1, -1);
}
else if (RE_BOOLEAN.test(value)) {
value = RE_BOOLEAN_TRUE.test(value);
}
else if (RE_DIGIT.test(value)) {
value = +value;
}
// Store value by index.
values.push(value);
// Store value with key or just index
if (key !== UNDEFINED) {
values[key] = value;
// Reset key
key = UNDEFINED;
}
}
// Iterate string
for (index = from = to = 0, length = str.length; index < length; index++) {
// Get char
c = str.charAt(index);
switch (c) {
case "\"" :
/* falls through */
case "'" :
// If we are currently quoted...
if (quote === c) {
// Stop quote
quote = false;
// Update to
to = index + 1;
}
// Otherwise
else if (quote === false) {
// Start quote
quote = c;
// Update from/to
from = to = index;
}
break;
case " " :
/* falls through */
case "\t" :
// Continue if we're quoted
if (quote) {
to = index + 1;
break;
}
// Update from/to
if (from === to) {
from = to = index + 1;
}
break;
case "=":
// Continue if we're quoted
if (quote) {
to = index + 1;
break;
}
// If we captured something...
if (from !== to) {
// Extract substring
key = STR_SUBSTRING.call(str, from, to);
if (RE_STRING.test(key)) {
key = STR_SLICE.call(key, 1, -1);
}
}
from = index + 1;
break;
case "," :
// Continue if we're quoted
if (quote) {
to = index + 1;
break;
}
// If we captured something...
extract(from, to);
// Update from/to
from = to = index + 1;
break;
default :
// Update to
to = index + 1;
}
}
// If we captured something...
extract(from, to);
return values;
};
}
}());
(function() {
"use strict";
if (typeof define === "function" && define.amd) {
define(["./src/jQueryDestroy"], factory);
} else if (typeof exports === 'object') {
module.exports = factory(
require("./src/jQueryDestroy")
);
} else {
throw Error("no module loader found");
}
function factory(jQueryDestroy) {
return jQueryDestroy;
}
})();
/**
* @license MIT http://mu-lib.mit-license.org/
*/
(function() {
"use strict";
if (typeof define === "function" && define.amd) {
define(["jquery"], factory);
} else if (typeof exports === "object") {
module.exports = factory(require("jquery"));
} else {
throw Error("no module loader found");
}
function factory($) {
/**
* Extends {@link jQuery} with:
*
* - {@link $#event-destroy} event
*
* @class mu-lib.jquery.destroy
* @static
* @alias plugin.jquery
*/
var DESTROY = "destroy";
/**
* @class $
*/
/**
* A special jQuery event whose handler will be called, only when this handler it's removed from the element.
* @event destroy
*/
$.event.special[DESTROY] = {
"noBubble": true,
"trigger": function () {
return false;
},
"remove": function onDestroyRemove(handleObj) {
var me = this;
if (handleObj) {
handleObj.handler.call(me, $.Event(handleObj.type, {
"data": handleObj.data,
"namespace": handleObj.namespace,
"target": me
}));
}
}
};
}
})();
/**
* @license MIT http://mu-lib.mit-license.org/
*/
define([ "jquery" ], function ($) {
"use strict";
/**
* Normalized hashchange event. Extends {@link jQuery} with:
*
* - {@link $#event-hashchange} event
*
*
* <div class="notice">
* Heavily influenced by <a href="https://github.com/millermedeiros/Hasher">Hasher</a>
* which is available under <a href="ttp://www.opensource.org/licenses/mit-license.php">MIT license</a>.
* </div>
*
* @class mu-lib.jquery.hashchange
* @static
* @alias plugin.jquery
*/
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 = '\v' == 'v';
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 me = this;
var element;
me.element = element = document.createElement("iframe");
element.src = "about:blank";
element.style.display = "none";
}
Frame.prototype = {
"getElement" : function () {
return this.element;
},
"getHash" : function () {
return this.element.contentWindow.frameHash;
},
"update" : function (hash) {
/*jshint evil:true*/
var me = this;
var document = me.element.contentWindow.document;
// Quick return if hash has not changed
if (me.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();
}
};
/**
* @class $
*/
/**
* A special jQuery event fired ever when the URL hash has changed.
* @event hashchange
*/
$.event.special[HASHCHANGE] = {
"setup" : function onHashChangeSetup() {
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 () {
var document = window.document;
var _isLocal = location.protocol === "file:";
var frame = new Frame(document);
document.body.appendChild(frame.getElement());
frame.update(hash);
return function () {
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 () {
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));
},
"teardown" : function onHashChangeTeardown() {
var window = this;
// Quick return if we support onHashChange natively
if (ONHASHCHANGE in window) {
return false;
}
window.clearInterval($.data(window, INTERVAL));
}
};
});
/**
* @license MIT http://mu-lib.mit-license.org/
*/
define(function () {
"use strict";
/**
* @class mu-lib.merge
* @mixin Function
* @static
*/
var UNDEFINED;
var NULL = null;
var ARRAY_PROTO = Array.prototype;
var ARRAY_CONCAT = ARRAY_PROTO.concat;
var OBJECT_PROTO = Object.prototype;
var OBJECT_TOSTRING = OBJECT_PROTO.toString;
var TOSTRING_OBJECT = OBJECT_TOSTRING.call(OBJECT_PROTO);
var TOSTRING_ARRAY = OBJECT_TOSTRING.call(ARRAY_PROTO);
var LENGTH = "length";
/**
* Function that calls on an Object, to augments this object with enumerable properties from the source objects,
* subsequent sources will overwrite property assignments of previous sources on primitive values,
* while object and array values will get merged recursively.
* @method constructor
* @param {...Object} [source] One or more source objects.
* @return {*} Merged object
*/
return function merge(source) {
var target = this;
var key;
var keys;
var i;
var j;
var iMax;
var jMax;
var source_value;
var target_value;
var source_tostring;
var target_tostring;
// Check that we can use target
if (target !== UNDEFINED && target !== NULL) {
// Iterate arguments
for (i = 0, iMax = arguments[LENGTH]; i < iMax; i++) {
// Get source, and continue if it's UNDEFINED or NULL
if ((source = arguments[i]) === UNDEFINED || source === NULL) {
continue;
}
// Get source keys
keys = Object.keys(source);
// Iterate keys
for (j = 0, jMax = keys[LENGTH]; j < jMax; j++) {
key = keys[j];
source_value = source[key];
target_value = target[key];
// No merge - copy source_value
if (!(key in target)) {
target[key] = source_value;
continue;
}
// Get 'types'
source_tostring = OBJECT_TOSTRING.call(source_value);
target_tostring = OBJECT_TOSTRING.call(target_value);
// Can we merge objects?
if (target_tostring === TOSTRING_OBJECT && source_tostring === TOSTRING_OBJECT) {
merge.call(target_value, source_value);
}
// Can we merge arrays?
else if (target_tostring === TOSTRING_ARRAY && source_tostring === TOSTRING_ARRAY) {
target[key] = ARRAY_CONCAT.call(target_value, source_value);
}
// No merge - override target[key]
else {
target[key] = source_value;
}
}
}
}
return target;
};
});
(function() {
'use strict';
if (typeof define === 'function' && define.amd) {
define(['./src/SelectorSet'], factory);
} else if (typeof exports === 'object') {
module.exports = factory(
require('./src/SelectorSet')
);
} else {
throw Error("no module loader found");
}
function factory(SelectorSet) {
return SelectorSet;
}
})();
(function(global) {
'use strict';
/* istanbul ignore next */
if (typeof define === 'function' && define.amd) {
define(factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
throw Error("no module loader found");
}
function factory() {
/**
* A simple construct to manage lists by keys. Much like a hash map
* with key-value pairs, this map has key-list pairs.
* @constructor
*/
function MappedLists() {
this.lists = {};
}
/**
* Add data to the list in `key`
* @param key The key of the list
* @param data Data to add
* @returns {MappedLists}
*/
MappedLists.prototype.add = function(key, data) {
key += " ";
this.lists[key] = this.lists[key] || [];
this.lists[key].push(data);
return this;
};
/**
* Remove data from the list in `key`
* @param key The key of the list
* @param data Data to add
* @param {Function} comp custom comparator to determine which items to remove
* @returns {MappedLists}
*/
MappedLists.prototype.remove = function(key, data, comp) {
key += " ";
comp = comp || eqComp;
var i, list = this.lists[key];
if (!list) return this;
if (!data){
delete this.lists[key];
return this;
}
i = list.length;
while(i--) if (comp(data, list[i])) list.splice(i, 1);
return this;
};
/**
* Get the list associated with 'key', or an empty list
* if not found.
* @param key
* @returns {Array}
*/
MappedLists.prototype.get = function(key) {
key += " ";
if (!this.lists[key]) this.lists[key] = [];
return this.lists[key];
};
return MappedLists;
}
function eqComp(o1, o2){
return o1 === o2;
}
}(this));
(function() {
'use strict';
/* istanbul ignore next */
if (typeof define === 'function' && define.amd) {
define([
'./classifier',
'./splitter',
'./Subsets',
'./matchesSelector'
], factory);
} else if (typeof exports === 'object') {
module.exports = factory(
require('./classifier'),
require('./splitter'),
require('./Subsets'),
require('./matchesSelector')
);
} else {
throw Error("no module loader found");
}
function factory(classify, splitter, Subsets, matchesSelector) {
/**
* A SelectorSet is an object which manages a set of CSS selectors.
* It provides two core functionalities:
* 1. Adding selectors to the set.
* 2. Matching a DOM element against selectors in the set and retrieving
* an array of selectors which match the element.
* The SelectorSet object internal implementation takes care of indexing
* the selectors added to it, such that later retrieving matches against
* a DOM object is very fast.
* The indexing is done according to:
* 1. The most significant part of the selector.
* 2. The type of the most significant type.
* For example, the selector `".parent .child#bobby"` will be indexed as
* an ID selector with the ID `"#bobby"`.
* @constructor
*/
function SelectorSet() {
this.subsets = Subsets();
}
/**
* matchesSelector is a function which checks if an element matches a
* selector.
* It can be overridden by the user by:
* 1. Overriding the prototype, thus modifying all instances of
* SelectorSet:
* ```
* SelectorSet.prototype.matchesSelector = customFunction`;
* var sSet = new SelectorSet();
* ```
* 2. Overriding the instance, thus modifying a specific instance of
* SelectorSet:
* ```
* var sSet = new SelectorSet();
* sSet.matchesSelector = customFunction`;
* ```
* @param element {DOMElement} The element to match
* @param selector {String} The selector to match against
* @returns true if the element matches the selector. false otherwise.
*/
SelectorSet.prototype.matchesSelector = matchesSelector;
/**
* Add a selector to the set.
* @param selector {String} The selector to add.
* @param datum1, datum2, ... Arbitrary number of additional parameters
* which will be added with the selector as associated data.
* @returns {SelectorSet}
*/
SelectorSet.prototype.add = function(selector) {
// selector might actually contain multiple selections seperated
// by a comma. we need to separate them.
var args = Array.prototype.slice.call(arguments),
selectors = splitter(selector),
i = selectors.length;
while (i--) {
args.splice(0, 1, selectors[i]);
_add.apply(this, args);
}
return this;
};
function _add( /* selector, arg1, arg2, ... */ ) {
var i, subset, key,
args = Array.prototype.slice.call(arguments);
args[0] = args[0].trim();
key = classify(args[0]);
for (i = 0; i < this.subsets.length; i++) {
subset = this.subsets[i];
if (subset.isOfType(key)) {
subset.add(key, args);
return;
}
}
}
/**
* Remove a selector from the set.
* @param selector {String} The selector to remove.
* @param datum1, datum2, ... Arbitrary number of additional parameters.
* @returns {SelectorSet}
*/
SelectorSet.prototype.remove = function(selector) {
// selector might actually contain multiple selections seperated
// by a comma. we need to separate them.
var args = Array.prototype.slice.call(arguments),
selectors = splitter(selector),
i = selectors.length;
while (i--) {
args.splice(0, 1, selectors[i]);
_remove.apply(this, args);
}
return this;
};
function _remove( /* selector, arg1, arg2, ... */ ) {
var i, subset, key,
args = Array.prototype.slice.call(arguments);
args[0] = args[0].trim();
key = classify(args[0]);
for (i = 0; i < this.subsets.length; i++) {
subset = this.subsets[i];
if (subset.isOfType(key)) {
subset.remove(key, args, compare);
return;
}
}
function compare(args, candidate){
var i = 0, len = args.length;
for (; i < len; i++){
if (args[i] !== candidate[i]) return false;
}
return true;
}
}
/**
* Match DOM elements to selectors in the set.
* @param el1, el2, ... The DOM elements to match.
* @returns {Array} An array of arrays. Each sub-array is a selector
* that matches the elements + the data this selector was added with.
*/
SelectorSet.prototype.matches = function() {
var i, j, k, t, el, subset, elKey, elKeys, candidate, candidates,
res = [],
els = Array.prototype.slice.call(arguments);
els = Array.prototype.concat.apply([], els); // flatten 'els'
for (t = 0; t < els.length; t++) {
el = els[t];
for (i = 0; i < this.subsets.length; i++) {
subset = this.subsets[i];
elKeys = subset.extractElementKeys(el);
for (j = 0; j < elKeys.length; j++) {
elKey = elKeys[j];
candidates = subset.get(elKey);
for (k = 0; k < candidates.length; k++) {
candidate = candidates[k];
if (res.indexOf(candidate) === -1 &&
this.matchesSelector(el, candidate[0]))
res.push(candidate);
}
}
}
}
return res;
};
return SelectorSet;
}
}());
(function() {
'use strict';
/* istanbul ignore next */
if (typeof define === 'function' && define.amd) {
define(['./MappedLists'], factory);
} else if (typeof exports === 'object') {
module.exports = factory(require('./MappedLists'));
} else {
throw Error("no module loader found");
}
function factory(MappedLists) {
/**
* A "Subset" object encapsulates all the functionalities required to
* manage a certain type (subset) of selectors.
* @param re The regular expression to test if a selector is of the type
* corresponding to this subset.
* @param extractor {Function} A function which takes a DOM element and
* returns a string of keys of this elements that match this subset.
* For example, in the IDs subset this method will return an array with
* at most one value - the element's id.
* @param ci {Boolean} Is this subset case insensitive?
* @constructor
* @private
*/
function Subset(re, extractor, ci) {
var mappedLists = new MappedLists();
this.isOfType = function(selector) {
return re.test(selector);
};
this.extractElementKeys = extractor;
this.add = function(key, data) {
mappedLists.add(ci ? key.toLowerCase() : key, data);
return this;
};
this.remove = function(key, data, comp) {
mappedLists.remove(ci ? key.toLowerCase() : key, data, comp);
return this;
};
this.get = function(key) {
return mappedLists.get(ci ? key.toLowerCase() : key);
};
}
return function() {
var subsets = [];
// Note: The order of subsets in this array matters!
// It determined the priority of checking elements against
// subsets.
// ID selectors subset
subsets.push(
new Subset(
/^#.+$/,
function(el) {
return el.id ? ["#" + el.id] : [];
}
)
);
// CLASS selectors subset
subsets.push(
new Subset(
/^\..+$/,
function(el) {
var res = [], classes = el.className;
if (typeof classes === 'string')
res = classes.split(/\s+/);
// for SVG elements:
else if (typeof classes === 'object' && 'baseVal' in classes)
res = classes.baseVal.split(/\s+/);
return res.map(function(r) {
return "." + r;
});
}
)
);
// TAG selectors subset
subsets.push(
new Subset(
/^[^\*\.#].*$/,
function(el) {
return el.nodeName ? [el.nodeName] : [];
},
true // case insensitive
)
);
// other selectors subset
subsets.push(
new Subset(
/^\*$/,
function(el) {
return ['*'];
}
)
);
return subsets;
};
}
}());
(function() {
'use strict';
/* istanbul ignore next */
if (typeof define === 'function' && define.amd) {
define([], factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
throw Error("no module loader found");
}
function factory() {
/**
* See:
* https://github.com/jquery/sizzle/blob/709e1db5bcb42e9d761dd4a8467899dd36ce63bc/src/sizzle.js#L81
* http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
*/
var identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+";
// regular expressions
var re = {
/**
* End of line whitespace
*/
ws: /(\s+)$/,
/**
* End of line garbage is one of:
* - anything that starts with a colon
* - anything inside square brackets
*/
garbage: /(\:.+?|\[.*?\])$/,
/**
* CSS ID selector
*/
id: new RegExp("^#" + identifier),
/**
* CSS class selector
*/
cls: new RegExp("^\\." + identifier),
/**
* A candidate is either:
* - ID
* - Class
* - Tag
* Look for candidates from the end of the line.
*/
candidate: new RegExp("([#\\.]{0,1}" + identifier + "?)$")
};
/**
* Get the most significant part of a CSS selector.
* The "significant" part is defined as any leading id, class name or
* tag name component (in that order of precedence) of the last
* (right most) selector.
*
* See test/classifier.js for examples
*
* @private
* @static
* @param {String} selector CSS selector
* @return {String} the most significant part of the selector
*/
function classifier(selector) {
var i, m, c, candidates = [];
selector = selector
.replace(re.ws, "")
.replace(re.garbage, "");
while (m = selector.match(re.candidate)) {
selector = selector
.replace(re.candidate, "")
.replace(re.garbage, "");
candidates.push(m[0]);
}
c = candidates.length;
// if no candidates, return the universal selector
if (!c)
return '*';
// return the ID part of the selector:
for (i = 0; i < c; i++)
if (re.id.test(candidates[i]))
return candidates[i];
// if no ID, return the class
for (i = 0; i < c; i++)
if (re.cls.test(candidates[i]))
return candidates[i];
// if no class, return the tag
return candidates[0];
}
return classifier;
}
}());
/**
* ignore coverage measurements for this module,
* because this is just an adapter for native browser functions.
*/
/* istanbul ignore next */
(function() {
'use strict';
if (typeof define === 'function' && define.amd) {
define(factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
throw Error("no module loader found");
}
function factory() {
var docElem = window.document.documentElement,
matchesSelector = docElem.matches ||
docElem.matchesSelector ||
docElem.webkitMatchesSelector ||
docElem.mozMatchesSelector ||
docElem.oMatchesSelector ||
docElem.msMatchesSelector ||
matchesSelectorPoly;
/**
* https://developer.mozilla.org/en-US/docs/Web/API/Element.matches#Polyfill
*/
function matchesSelectorPoly(selector) {
var element = this,
doc = element.document || element.ownerDocument,
matches = doc.querySelectorAll(selector),
i = 0;
while (matches[i] && matches[i] !== element) i++;
return matches[i] ? true : false;
}
/**
* A service function which takes an element and a selector and returns
* `true` if the element matches the selector, or `false` otherwise.
* This function tries to use native browser *matchesSelector functions,
* and falls back to a simple polyfill.
*/
return function(element, selector) {
return matchesSelector.call(element, selector);
}
}
}());
(function() {
'use strict';
/* istanbul ignore next */
if (typeof define === 'function' && define.amd) {
define([], factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
throw Error("no module loader found");
}
function factory(){
var IGNORE_BOUNDARIES = ['\'', '"'],
SELECTORS_DELIMITER = ',';
function splitter(selector){
var res, len = 0, flag = false, tot = selector.length;
if (!tot) return [];
while (
selector[len] &&
(flag || selector[len] !== SELECTORS_DELIMITER)
) {
if (selector[len] === flag){
flag = false;
} else if (
flag === false &&
IGNORE_BOUNDARIES.indexOf(selector[len]) !== -1
){
flag = selector[len];
}
len++;
}
res = splitter(selector.substr(len + 1));
res.push(selector.substr(0, len));
return res;
}
return splitter;
}
}());
/** @license MIT License (c) copyright 2013 original authors */
/**
* Array -- a stand-alone module for using Javascript 1.6 array features
* in lame-o browsers that don't support Javascript 1.6
*
* @author Jared Cacurak
* @author Brian Cavalier
* @author John Hann
*/
/*
define(['poly!poly/array'], function () {
var items = [1, 2, 3];
items.forEach(function (item) {
console.log(item);
};
});
All of the wrapper API methods are shimmed and are reasonably close to
the ES5 specification, but may vary slightly in unforeseen edge cases:
var array = [1, 2, 3];
array.forEach(lambda [, context]);
array.every(lambda [, context]);
array.some(lambda [, context]);
array.filter(lambda [, context]);
array.map(lambda [, context]);
array.indexOf(item [, fromIndex]);
array.lastIndexOf(item [, fromIndex]);
array.reduce(reduceFunc [, initialValue]);
array.reduceRight(reduceFunc [, initialValue]);
Array.isArray(object)
*/
(function (define) {
define(function (require) {
"use strict";
var base = require('./lib/_base');
var array = require('./lib/_array');
var proto = Array.prototype,
featureMap,
_reduce,
_find;
featureMap = {
'array-foreach': 'forEach',
'array-every': 'every',
'array-some': 'some',
'array-map': 'map',
'array-filter': 'filter',
'array-reduce': 'reduce',
'array-reduceright': 'reduceRight',
'array-indexof': 'indexOf',
'array-lastindexof': 'lastIndexOf'
};
function has (feature) {
var prop = featureMap[feature];
return base.isFunction(proto[prop]);
}
function returnTruthy () {
return 1;
}
/***** iterators *****/
if (!has('array-foreach')) {
proto.forEach = function forEach (lambda) {
// arguments[+1] is to fool google closure compiler into NOT adding a function argument!
array.iterate(this, lambda, returnTruthy, arguments[+1]);
};
}
if (!has('array-every')) {
proto.every = function every (lambda) {
// arguments[+1] is to fool google closure compiler into NOT adding a function argument!
return array.iterate(this, lambda, array.returnValue, arguments[+1]);
};
}
if (!has('array-some')) {
proto.some = function some (lambda) {
// arguments[+1] is to fool google closure compiler into NOT adding a function argument!
return array.iterate(this, lambda, function (val) { return !val; }, arguments[+1]);
};
}
/***** mutators *****/
if(!has('array-map')) {
proto.map = function map (lambda) {
var arr, result;
arr = this;
result = new Array(arr.length);
// arguments[+1] is to fool google closure compiler into NOT adding a function argument!
array.iterate(arr, lambda, function (val, i) { result[i] = val; return 1; }, arguments[+1]);
return result;
};
}
if (!has('array-filter')) {
proto.filter = function filter (lambda) {
var arr, result;
arr = this;
result = [];
array.iterate(arr, lambda, function (val, i, orig) {
// use a copy of the original value in case
// the lambda function changed it
if (val) {
result.push(orig);
}
return 1;
}, arguments[1]);
return result;
};
}
/***** reducers *****/
if (!has('array-reduce') || !has('array-reduceright')) {
_reduce = function _reduce (reduceFunc, inc, initialValue, hasInitialValue) {
var reduced, startPos, initialValuePos;
startPos = initialValuePos = inc > 0 ? -1 : array.toArrayLike(this).length >>> 0;
// If no initialValue, use first item of array (we know length !== 0 here)
// and adjust i to start at second item
if (!hasInitialValue) {
array.iterate(this, array.returnValue, function (val, i) {
reduced = val;
initialValuePos = i;
}, null, startPos + inc, inc);
if (initialValuePos == startPos) {
// no intial value and no items in array!
throw new TypeError();
}
}
else {
// If initialValue provided, use it
reduced = initialValue;
}
// Do the actual reduce
array.iterate(this, function (item, i, arr) {
reduced = reduceFunc(reduced, item, i, arr);
}, returnTruthy, null, initialValuePos + inc, inc);
// we have a reduced value!
return reduced;
};
if (!has('array-reduce')) {
proto.reduce = function reduce (reduceFunc /*, initialValue */) {
return _reduce.call(this, reduceFunc, 1, arguments[+1], arguments.length > 1);
};
}
if (!has('array-reduceright')) {
proto.reduceRight = function reduceRight (reduceFunc /*, initialValue */) {
return _reduce.call(this, reduceFunc, -1, arguments[+1], arguments.length > 1);
};
}
}
/***** finders *****/
if (!has('array-indexof') || !has('array-lastindexof')) {
_find = function _find (arr, item, from, forward) {
var len = array.toArrayLike(arr).length >>> 0, foundAt = -1;
// convert to number, or default to start or end positions
from = isNaN(from) ? (forward ? 0 : len - 1) : Number(from);
// negative means it's an offset from the end position
if (from < 0) {
from = len + from - 1;
}
array.iterate(arr, array.returnValue, function (val, i) {
if (val === item) {
foundAt = i;
}
return foundAt == -1;
}, null, from, forward ? 1 : -1);
return foundAt;
};
if (!has('array-indexof')) {
proto.indexOf = function indexOf (item) {
// arguments[+1] is to fool google closure compiler into NOT adding a function argument!
return _find(this, item, arguments[+1], true);
};
}
if (!has('array-lastindexof')) {
proto.lastIndexOf = function lastIndexOf (item) {
// arguments[+1] is to fool google closure compiler into NOT adding a function argument!
return _find(this, item, arguments[+1], false);
};
}
}
if (!Array.isArray) {
Array.isArray = base.isArray;
}
});
}(
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(require); }
));
/** @license MIT License (c) copyright 2013 original authors */
/**
* JSON polyfill / shim
*
* @author Brian Cavalier
* @author John Hann
*/
(function (define) {
define(function (require) {
"use strict";
return require('./support/json3');
});
}(
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(require); }
));
/** @license MIT License (c) copyright 2013 original authors */
/**
* poly common array functions
*
* @author Jared Cacurak
* @author Brian Cavalier
* @author John Hann
*/
(function (define) {
define(function (require) {
var base = require('./_base');
var toObject = base.createCaster(Object, 'Array'),
isFunction = base.isFunction,
undef;
return {
returnValue: returnValue,
iterate: iterate,
toArrayLike: toArrayLike
};
function toArrayLike (o) {
return (base.toString(o) == '[object String]')
? o.split('')
: toObject(o);
}
function returnValue (val) {
return val;
}
function iterate (arr, lambda, continueFunc, context, start, inc) {
var alo, len, i, end;
alo = toArrayLike(arr);
len = alo.length >>> 0;
if (start === undef) start = 0;
if (!inc) inc = 1;
end = inc < 0 ? -1 : len;
if (!isFunction(lambda)) {
throw new TypeError(lambda + ' is not a function');
}
if (start == end) {
return false;
}
if ((start <= end) ^ (inc > 0)) {
throw new TypeError('Invalid length or starting index');
}
for (i = start; i != end; i = i + inc) {
if (i in alo) {
if (!continueFunc(lambda.call(context, alo[i], i, alo), i, alo[i])) {
return false;
}
}
}
return true;
}
});
}(
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(require); }
));
/** @license MIT License (c) copyright 2013 original authors */
/**
* poly common functions
*
* @author Brian Cavalier
* @author John Hann
*/
(function (define) {
define(function (require, exports) {
var toString;
toString = ({}).toString;
exports.isFunction = function (o) {
return typeof o == 'function';
};
exports.isString = function (o) {
return toString.call(o) == '[object String]';
};
exports.isArray = function (o) {
return toString.call(o) == '[object Array]';
};
exports.toString = function (o) {
return toString.apply(o);
};
exports.createCaster = function (caster, name) {
return function cast (o) {
if (o == null) throw new TypeError(name + ' method called on null or undefined');
return caster(o);
}
};
exports.isElement = function(o){
return typeof HTMLElement == 'undefined'
? 'tagName' in o && 'nodeName' in o
: o instanceof HTMLELEMENT;
};
});
}(
typeof define == 'function' && define.amd
? define
: function (factory) { factory(require, exports); }
));
/** @license MIT License (c) copyright 2013 original authors */
/**
* String polyfill / shims
*
* @author Brian Cavalier
* @author John Hann
*
* Adds str.trim(), str.trimRight(), and str.trimLeft()
*
* Note: we don't bother trimming all possible ES5 white-space characters.
* If you truly need strict ES5 whitespace compliance in all browsers,
* create your own trim function.
* from http://perfectionkills.com/whitespace-deviations/
* '\x09-\x0D\x20\xA0\u1680\u180E\u2000-\u200A\u202F\u205F\u3000\u2028\u2029'
*/
(function (define) {
define(function (require) {
"use strict";
var base = require('./lib/_base');
var proto = String.prototype,
featureMap,
has,
toString;
featureMap = {
'string-trim': 'trim',
'string-trimleft': 'trimLeft',
'string-trimright': 'trimRight'
};
function checkFeature (feature) {
var prop = featureMap[feature];
return base.isFunction(proto[prop]);
}
function neg () { return false; }
has = checkFeature;
// compressibility helper
function remove (str, rx) {
return str.replace(rx, '');
}
toString = base.createCaster(String, 'String');
var trimRightRx, trimLeftRx;
trimRightRx = /\s+$/;
trimLeftRx = /^\s+/;
function checkShims () {
if (!has('string-trim')) {
proto.trim = function trim () {
return remove(remove(toString(this), trimLeftRx), trimRightRx);
};
}
if (!has('string-trimleft')) {
proto.trimLeft = function trimLeft () {
return remove(toString(this), trimLeftRx);
};
}
if (!has('string-trimright')) {
proto.trimRight = function trimRight () {
return remove(toString(this), trimRightRx);
};
}
}
checkShims();
return {
setWhitespaceChars: function (wsc) {
trimRightRx = new RegExp(wsc + '$');
trimLeftRx = new RegExp('^' + wsc);
// fail all has() checks and check shims again
has = neg;
checkShims();
}
};
});
}(
typeof define == 'function' && define.amd
? define
: function (factory) { module.exports = factory(require); }
));
/*! JSON v3.2.4 | http://bestiejs.github.com/json3 | Copyright 2012, Kit Cambridge | http://kit.mit-license.org */
;(function () {
// Convenience aliases.
var getClass = {}.toString, isProperty, forEach, undef;
// Detect the `define` function exposed by asynchronous module loaders. The
// strict `define` check is necessary for compatibility with `r.js`.
var isLoader = typeof define === "function" && define.amd, JSON3 = !isLoader && typeof exports == "object" && exports;
if (JSON3 || isLoader) {
if (typeof JSON == "object" && JSON) {
// Delegate to the native `stringify` and `parse` implementations in
// asynchronous module loaders and CommonJS environments.
if (isLoader) {
JSON3 = JSON;
} else {
JSON3.stringify = JSON.stringify;
JSON3.parse = JSON.parse;
}
} else if (isLoader) {
JSON3 = this.JSON = {};
}
} else {
// Export for web browsers and JavaScript engines.
JSON3 = this.JSON || (this.JSON = {});
}
// Local variables.
var Escapes, toPaddedString, quote, serialize;
var fromCharCode, Unescapes, abort, lex, get, walk, update, Index, Source;
// Test the `Date#getUTC*` methods. Based on work by @Yaffle.
var isExtended = new Date(-3509827334573292), floor, Months, getDay;
try {
// The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical
// results for certain dates in Opera >= 10.53.
isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() == 1 &&
// Safari < 2.0.2 stores the internal millisecond time value correctly,
// but clips the values returned by the date methods to the range of
// signed 32-bit integers ([-2 ** 31, 2 ** 31 - 1]).
isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708;
} catch (exception) {}
// Internal: Determines whether the native `JSON.stringify` and `parse`
// implementations are spec-compliant. Based on work by Ken Snyder.
function has(name) {
var stringifySupported, parseSupported, value, serialized = '{"A":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}', all = name == "json";
if (all || name == "json-stringify" || name == "json-parse") {
// Test `JSON.stringify`.
if (name == "json-stringify" || all) {
if ((stringifySupported = typeof JSON3.stringify == "function" && isExtended)) {
// A test function object with a custom `toJSON` method.
(value = function () {
return 1;
}).toJSON = value;
try {
stringifySupported =
// Firefox 3.1b1 and b2 serialize string, number, and boolean
// primitives as object literals.
JSON3.stringify(0) === "0" &&
// FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object
// literals.
JSON3.stringify(new Number()) === "0" &&
JSON3.stringify(new String()) == '""' &&
// FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or
// does not define a canonical JSON representation (this applies to
// objects with `toJSON` properties as well, *unless* they are nested
// within an object or array).
JSON3.stringify(getClass) === undef &&
// IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and
// FF 3.1b3 pass this test.
JSON3.stringify(undef) === undef &&
// Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s,
// respectively, if the value is omitted entirely.
JSON3.stringify() === undef &&
// FF 3.1b1, 2 throw an error if the given value is not a number,
// string, array, object, Boolean, or `null` literal. This applies to
// objects with custom `toJSON` methods as well, unless they are nested
// inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON`
// methods entirely.
JSON3.stringify(value) === "1" &&
JSON3.stringify([value]) == "[1]" &&
// Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of
// `"[null]"`.
JSON3.stringify([undef]) == "[null]" &&
// YUI 3.0.0b1 fails to serialize `null` literals.
JSON3.stringify(null) == "null" &&
// FF 3.1b1, 2 halts serialization if an array contains a function:
// `[1, true, getClass, 1]` serializes as "[1,true,],". These versions
// of Firefox also allow trailing commas in JSON objects and arrays.
// FF 3.1b3 elides non-JSON values from objects and arrays, unless they
// define custom `toJSON` methods.
JSON3.stringify([undef, getClass, null]) == "[null,null,null]" &&
// Simple serialization test. FF 3.1b1 uses Unicode escape sequences
// where character escape codes are expected (e.g., `\b` => `\u0008`).
JSON3.stringify({ "A": [value, true, false, null, "\0\b\n\f\r\t"] }) == serialized &&
// FF 3.1b1 and b2 ignore the `filter` and `width` arguments.
JSON3.stringify(null, value) === "1" &&
JSON3.stringify([1, 2], null, 1) == "[\n 1,\n 2\n]" &&
// JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly
// serialize extended years.
JSON3.stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' &&
// The milliseconds are optional in ES 5, but required in 5.1.
JSON3.stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' &&
// Firefox <= 11.0 incorrectly serializes years prior to 0 as negative
// four-digit years instead of six-digit years. Credits: @Yaffle.
JSON3.stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' &&
// Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond
// values less than 1000. Credits: @Yaffle.
JSON3.stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"';
} catch (exception) {
stringifySupported = false;
}
}
if (!all) {
return stringifySupported;
}
}
// Test `JSON.parse`.
if (name == "json-parse" || all) {
if (typeof JSON3.parse == "function") {
try {
// FF 3.1b1, b2 will throw an exception if a bare literal is provided.
// Conforming implementations should also coerce the initial argument to
// a string prior to parsing.
if (JSON3.parse("0") === 0 && !JSON3.parse(false)) {
// Simple parsing test.
value = JSON3.parse(serialized);
if ((parseSupported = value.A.length == 5 && value.A[0] == 1)) {
try {
// Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings.
parseSupported = !JSON3.parse('"\t"');
} catch (exception) {}
if (parseSupported) {
try {
// FF 4.0 and 4.0.1 allow leading `+` signs, and leading and
// trailing decimal points. FF 4.0, 4.0.1, and IE 9-10 also
// allow certain octal literals.
parseSupported = JSON3.parse("01") != 1;
} catch (exception) {}
}
}
}
} catch (exception) {
parseSupported = false;
}
}
if (!all) {
return parseSupported;
}
}
return stringifySupported && parseSupported;
}
}
if (!has("json")) {
// Define additional utility methods if the `Date` methods are buggy.
if (!isExtended) {
floor = Math.floor;
// A mapping between the months of the year and the number of days between
// January 1st and the first of the respective month.
Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
// Internal: Calculates the number of days between the Unix epoch and the
// first day of the given month.
getDay = function (year, month) {
return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400);
};
}
// Internal: Determines if a property is a direct property of the given
// object. Delegates to the native `Object#hasOwnProperty` method.
if (!(isProperty = {}.hasOwnProperty)) {
isProperty = function (property) {
var members = {}, constructor;
if ((members.__proto__ = null, members.__proto__ = {
// The *proto* property cannot be set multiple times in recent
// versions of Firefox and SeaMonkey.
"toString": 1
}, members).toString != getClass) {
// Safari <= 2.0.3 doesn't implement `Object#hasOwnProperty`, but
// supports the mutable *proto* property.
isProperty = function (property) {
// Capture and break the object's prototype chain (see section 8.6.2
// of the ES 5.1 spec). The parenthesized expression prevents an
// unsafe transformation by the Closure Compiler.
var original = this.__proto__, result = property in (this.__proto__ = null, this);
// Restore the original prototype chain.
this.__proto__ = original;
return result;
};
} else {
// Capture a reference to the top-level `Object` constructor.
constructor = members.constructor;
// Use the `constructor` property to simulate `Object#hasOwnProperty` in
// other environments.
isProperty = function (property) {
var parent = (this.constructor || constructor).prototype;
return property in this && !(property in parent && this[property] === parent[property]);
};
}
members = null;
return isProperty.call(this, property);
};
}
// Internal: Normalizes the `for...in` iteration algorithm across
// environments. Each enumerated key is yielded to a `callback` function.
forEach = function (object, callback) {
var size = 0, Properties, members, property, forEach;
// Tests for bugs in the current environment's `for...in` algorithm. The
// `valueOf` property inherits the non-enumerable flag from
// `Object.prototype` in older versions of IE, Netscape, and Mozilla.
(Properties = function () {
this.valueOf = 0;
}).prototype.valueOf = 0;
// Iterate over a new instance of the `Properties` class.
members = new Properties();
for (property in members) {
// Ignore all properties inherited from `Object.prototype`.
if (isProperty.call(members, property)) {
size++;
}
}
Properties = members = null;
// Normalize the iteration algorithm.
if (!size) {
// A list of non-enumerable properties inherited from `Object.prototype`.
members = ["valueOf", "toString", "toLocaleString", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "constructor"];
// IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable
// properties.
forEach = function (object, callback) {
var isFunction = getClass.call(object) == "[object Function]", property, length;
for (property in object) {
// Gecko <= 1.0 enumerates the `prototype` property of functions under
// certain conditions; IE does not.
if (!(isFunction && property == "prototype") && isProperty.call(object, property)) {
callback(property);
}
}
// Manually invoke the callback for each non-enumerable property.
for (length = members.length; property = members[--length]; isProperty.call(object, property) && callback(property));
};
} else if (size == 2) {
// Safari <= 2.0.4 enumerates shadowed properties twice.
forEach = function (object, callback) {
// Create a set of iterated properties.
var members = {}, isFunction = getClass.call(object) == "[object Function]", property;
for (property in object) {
// Store each property name to prevent double enumeration. The
// `prototype` property of functions is not enumerated due to cross-
// environment inconsistencies.
if (!(isFunction && property == "prototype") && !isProperty.call(members, property) && (members[property] = 1) && isProperty.call(object, property)) {
callback(property);
}
}
};
} else {
// No bugs detected; use the standard `for...in` algorithm.
forEach = function (object, callback) {
var isFunction = getClass.call(object) == "[object Function]", property, isConstructor;
for (property in object) {
if (!(isFunction && property == "prototype") && isProperty.call(object, property) && !(isConstructor = property === "constructor")) {
callback(property);
}
}
// Manually invoke the callback for the `constructor` property due to
// cross-environment inconsistencies.
if (isConstructor || isProperty.call(object, (property = "constructor"))) {
callback(property);
}
};
}
return forEach(object, callback);
};
// Public: Serializes a JavaScript `value` as a JSON string. The optional
// `filter` argument may specify either a function that alters how object and
// array members are serialized, or an array of strings and numbers that
// indicates which properties should be serialized. The optional `width`
// argument may be either a string or number that specifies the indentation
// level of the output.
if (!has("json-stringify")) {
// Internal: A map of control characters and their escaped equivalents.
Escapes = {
"\\": "\\\\",
'"': '\\"',
"\b": "\\b",
"\f": "\\f",
"\n": "\\n",
"\r": "\\r",
"\t": "\\t"
};
// Internal: Converts `value` into a zero-padded string such that its
// length is at least equal to `width`. The `width` must be <= 6.
toPaddedString = function (width, value) {
// The `|| 0` expression is necessary to work around a bug in
// Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`.
return ("000000" + (value || 0)).slice(-width);
};
// Internal: Double-quotes a string `value`, replacing all ASCII control
// characters (characters with code unit values between 0 and 31) with
// their escaped equivalents. This is an implementation of the
// `Quote(value)` operation defined in ES 5.1 section 15.12.3.
quote = function (value) {
var result = '"', index = 0, symbol;
for (; symbol = value.charAt(index); index++) {
// Escape the reverse solidus, double quote, backspace, form feed, line
// feed, carriage return, and tab characters.
result += '\\"\b\f\n\r\t'.indexOf(symbol) > -1 ? Escapes[symbol] :
// If the character is a control character, append its Unicode escape
// sequence; otherwise, append the character as-is.
(Escapes[symbol] = symbol < " " ? "\\u00" + toPaddedString(2, symbol.charCodeAt(0).toString(16)) : symbol);
}
return result + '"';
};
// Internal: Recursively serializes an object. Implements the
// `Str(key, holder)`, `JO(value)`, and `JA(value)` operations.
serialize = function (property, object, callback, properties, whitespace, indentation, stack) {
var value = object[property], className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, any, result;
if (typeof value == "object" && value) {
className = getClass.call(value);
if (className == "[object Date]" && !isProperty.call(value, "toJSON")) {
if (value > -1 / 0 && value < 1 / 0) {
// Dates are serialized according to the `Date#toJSON` method
// specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15
// for the ISO 8601 date time string format.
if (getDay) {
// Manually compute the year, month, date, hours, minutes,
// seconds, and milliseconds if the `getUTC*` methods are
// buggy. Adapted from @Yaffle's `date-shim` project.
date = floor(value / 864e5);
for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++);
for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++);
date = 1 + date - getDay(year, month);
// The `time` value specifies the time within the day (see ES
// 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used
// to compute `A modulo B`, as the `%` operator does not
// correspond to the `modulo` operation for negative numbers.
time = (value % 864e5 + 864e5) % 864e5;
// The hours, minutes, seconds, and milliseconds are obtained by
// decomposing the time within the day. See section 15.9.1.10.
hours = floor(time / 36e5) % 24;
minutes = floor(time / 6e4) % 60;
seconds = floor(time / 1e3) % 60;
milliseconds = time % 1e3;
} else {
year = value.getUTCFullYear();
month = value.getUTCMonth();
date = value.getUTCDate();
hours = value.getUTCHours();
minutes = value.getUTCMinutes();
seconds = value.getUTCSeconds();
milliseconds = value.getUTCMilliseconds();
}
// Serialize extended years correctly.
value = (year <= 0 || year >= 1e4 ? (year < 0 ? "-" : "+") + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) +
"-" + toPaddedString(2, month + 1) + "-" + toPaddedString(2, date) +
// Months, dates, hours, minutes, and seconds should have two
// digits; milliseconds should have three.
"T" + toPaddedString(2, hours) + ":" + toPaddedString(2, minutes) + ":" + toPaddedString(2, seconds) +
// Milliseconds are optional in ES 5.0, but required in 5.1.
"." + toPaddedString(3, milliseconds) + "Z";
} else {
value = null;
}
} else if (typeof value.toJSON == "function" && ((className != "[object Number]" && className != "[object String]" && className != "[object Array]") || isProperty.call(value, "toJSON"))) {
// Prototype <= 1.6.1 adds non-standard `toJSON` methods to the
// `Number`, `String`, `Date`, and `Array` prototypes. JSON 3
// ignores all `toJSON` methods on these objects unless they are
// defined directly on an instance.
value = value.toJSON(property);
}
}
if (callback) {
// If a replacement function was provided, call it to obtain the value
// for serialization.
value = callback.call(object, property, value);
}
if (value === null) {
return "null";
}
className = getClass.call(value);
if (className == "[object Boolean]") {
// Booleans are represented literally.
return "" + value;
} else if (className == "[object Number]") {
// JSON numbers must be finite. `Infinity` and `NaN` are serialized as
// `"null"`.
return value > -1 / 0 && value < 1 / 0 ? "" + value : "null";
} else if (className == "[object String]") {
// Strings are double-quoted and escaped.
return quote(value);
}
// Recursively serialize objects and arrays.
if (typeof value == "object") {
// Check for cyclic structures. This is a linear search; performance
// is inversely proportional to the number of unique nested objects.
for (length = stack.length; length--;) {
if (stack[length] === value) {
// Cyclic structures cannot be serialized by `JSON.stringify`.
throw TypeError();
}
}
// Add the object to the stack of traversed objects.
stack.push(value);
results = [];
// Save the current indentation level and indent one additional level.
prefix = indentation;
indentation += whitespace;
if (className == "[object Array]") {
// Recursively serialize array elements.
for (index = 0, length = value.length; index < length; any || (any = true), index++) {
element = serialize(index, value, callback, properties, whitespace, indentation, stack);
results.push(element === undef ? "null" : element);
}
result = any ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]";
} else {
// Recursively serialize object members. Members are selected from
// either a user-specified list of property names, or the object
// itself.
forEach(properties || value, function (property) {
var element = serialize(property, value, callback, properties, whitespace, indentation, stack);
if (element !== undef) {
// According to ES 5.1 section 15.12.3: "If `gap` {whitespace}
// is not the empty string, let `member` {quote(property) + ":"}
// be the concatenation of `member` and the `space` character."
// The "`space` character" refers to the literal space
// character, not the `space` {width} argument provided to
// `JSON.stringify`.
results.push(quote(property) + ":" + (whitespace ? " " : "") + element);
}
any || (any = true);
});
result = any ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}";
}
// Remove the object from the traversed object stack.
stack.pop();
return result;
}
};
// Public: `JSON.stringify`. See ES 5.1 section 15.12.3.
JSON3.stringify = function (source, filter, width) {
var whitespace, callback, properties, index, length, value;
if (typeof filter == "function" || typeof filter == "object" && filter) {
if (getClass.call(filter) == "[object Function]") {
callback = filter;
} else if (getClass.call(filter) == "[object Array]") {
// Convert the property names array into a makeshift set.
properties = {};
for (index = 0, length = filter.length; index < length; value = filter[index++], ((getClass.call(value) == "[object String]" || getClass.call(value) == "[object Number]") && (properties[value] = 1)));
}
}
if (width) {
if (getClass.call(width) == "[object Number]") {
// Convert the `width` to an integer and create a string containing
// `width` number of space characters.
if ((width -= width % 1) > 0) {
for (whitespace = "", width > 10 && (width = 10); whitespace.length < width; whitespace += " ");
}
} else if (getClass.call(width) == "[object String]") {
whitespace = width.length <= 10 ? width : width.slice(0, 10);
}
}
// Opera <= 7.54u2 discards the values associated with empty string keys
// (`""`) only if they are used directly within an object member list
// (e.g., `!("" in { "": 1})`).
return serialize("", (value = {}, value[""] = source, value), callback, properties, whitespace, "", []);
};
}
// Public: Parses a JSON source string.
if (!has("json-parse")) {
fromCharCode = String.fromCharCode;
// Internal: A map of escaped control characters and their unescaped
// equivalents.
Unescapes = {
"\\": "\\",
'"': '"',
"/": "/",
"b": "\b",
"t": "\t",
"n": "\n",
"f": "\f",
"r": "\r"
};
// Internal: Resets the parser state and throws a `SyntaxError`.
abort = function() {
Index = Source = null;
throw SyntaxError();
};
// Internal: Returns the next token, or `"$"` if the parser has reached
// the end of the source string. A token may be a string, number, `null`
// literal, or Boolean literal.
lex = function () {
var source = Source, length = source.length, symbol, value, begin, position, sign;
while (Index < length) {
symbol = source.charAt(Index);
if ("\t\r\n ".indexOf(symbol) > -1) {
// Skip whitespace tokens, including tabs, carriage returns, line
// feeds, and space characters.
Index++;
} else if ("{}[]:,".indexOf(symbol) > -1) {
// Parse a punctuator token at the current position.
Index++;
return symbol;
} else if (symbol == '"') {
// Advance to the next character and parse a JSON string at the
// current position. String tokens are prefixed with the sentinel
// `@` character to distinguish them from punctuators.
for (value = "@", Index++; Index < length;) {
symbol = source.charAt(Index);
if (symbol < " ") {
// Unescaped ASCII control characters are not permitted.
abort();
} else if (symbol == "\\") {
// Parse escaped JSON control characters, `"`, `\`, `/`, and
// Unicode escape sequences.
symbol = source.charAt(++Index);
if ('\\"/btnfr'.indexOf(symbol) > -1) {
// Revive escaped control characters.
value += Unescapes[symbol];
Index++;
} else if (symbol == "u") {
// Advance to the first character of the escape sequence.
begin = ++Index;
// Validate the Unicode escape sequence.
for (position = Index + 4; Index < position; Index++) {
symbol = source.charAt(Index);
// A valid sequence comprises four hexdigits that form a
// single hexadecimal value.
if (!(symbol >= "0" && symbol <= "9" || symbol >= "a" && symbol <= "f" || symbol >= "A" && symbol <= "F")) {
// Invalid Unicode escape sequence.
abort();
}
}
// Revive the escaped character.
value += fromCharCode("0x" + source.slice(begin, Index));
} else {
// Invalid escape sequence.
abort();
}
} else {
if (symbol == '"') {
// An unescaped double-quote character marks the end of the
// string.
break;
}
// Append the original character as-is.
value += symbol;
Index++;
}
}
if (source.charAt(Index) == '"') {
Index++;
// Return the revived string.
return value;
}
// Unterminated string.
abort();
} else {
// Parse numbers and literals.
begin = Index;
// Advance the scanner's position past the sign, if one is
// specified.
if (symbol == "-") {
sign = true;
symbol = source.charAt(++Index);
}
// Parse an integer or floating-point value.
if (symbol >= "0" && symbol <= "9") {
// Leading zeroes are interpreted as octal literals.
if (symbol == "0" && (symbol = source.charAt(Index + 1), symbol >= "0" && symbol <= "9")) {
// Illegal octal literal.
abort();
}
sign = false;
// Parse the integer component.
for (; Index < length && (symbol = source.charAt(Index), symbol >= "0" && symbol <= "9"); Index++);
// Floats cannot contain a leading decimal point; however, this
// case is already accounted for by the parser.
if (source.charAt(Index) == ".") {
position = ++Index;
// Parse the decimal component.
for (; position < length && (symbol = source.charAt(position), symbol >= "0" && symbol <= "9"); position++);
if (position == Index) {
// Illegal trailing decimal.
abort();
}
Index = position;
}
// Parse exponents.
symbol = source.charAt(Index);
if (symbol == "e" || symbol == "E") {
// Skip past the sign following the exponent, if one is
// specified.
symbol = source.charAt(++Index);
if (symbol == "+" || symbol == "-") {
Index++;
}
// Parse the exponential component.
for (position = Index; position < length && (symbol = source.charAt(position), symbol >= "0" && symbol <= "9"); position++);
if (position == Index) {
// Illegal empty exponent.
abort();
}
Index = position;
}
// Coerce the parsed value to a JavaScript number.
return +source.slice(begin, Index);
}
// A negative sign may only precede numbers.
if (sign) {
abort();
}
// `true`, `false`, and `null` literals.
if (source.slice(Index, Index + 4) == "true") {
Index += 4;
return true;
} else if (source.slice(Index, Index + 5) == "false") {
Index += 5;
return false;
} else if (source.slice(Index, Index + 4) == "null") {
Index += 4;
return null;
}
// Unrecognized token.
abort();
}
}
// Return the sentinel `$` character if the parser has reached the end
// of the source string.
return "$";
};
// Internal: Parses a JSON `value` token.
get = function (value) {
var results, any, key;
if (value == "$") {
// Unexpected end of input.
abort();
}
if (typeof value == "string") {
if (value.charAt(0) == "@") {
// Remove the sentinel `@` character.
return value.slice(1);
}
// Parse object and array literals.
if (value == "[") {
// Parses a JSON array, returning a new JavaScript array.
results = [];
for (;; any || (any = true)) {
value = lex();
// A closing square bracket marks the end of the array literal.
if (value == "]") {
break;
}
// If the array literal contains elements, the current token
// should be a comma separating the previous element from the
// next.
if (any) {
if (value == ",") {
value = lex();
if (value == "]") {
// Unexpected trailing `,` in array literal.
abort();
}
} else {
// A `,` must separate each array element.
abort();
}
}
// Elisions and leading commas are not permitted.
if (value == ",") {
abort();
}
results.push(get(value));
}
return results;
} else if (value == "{") {
// Parses a JSON object, returning a new JavaScript object.
results = {};
for (;; any || (any = true)) {
value = lex();
// A closing curly brace marks the end of the object literal.
if (value == "}") {
break;
}
// If the object literal contains members, the current token
// should be a comma separator.
if (any) {
if (value == ",") {
value = lex();
if (value == "}") {
// Unexpected trailing `,` in object literal.
abort();
}
} else {
// A `,` must separate each object member.
abort();
}
}
// Leading commas are not permitted, object property names must be
// double-quoted strings, and a `:` must separate each property
// name and value.
if (value == "," || typeof value != "string" || value.charAt(0) != "@" || lex() != ":") {
abort();
}
results[value.slice(1)] = get(lex());
}
return results;
}
// Unexpected token encountered.
abort();
}
return value;
};
// Internal: Updates a traversed object member.
update = function(source, property, callback) {
var element = walk(source, property, callback);
if (element === undef) {
delete source[property];
} else {
source[property] = element;
}
};
// Internal: Recursively traverses a parsed JSON object, invoking the
// `callback` function for each value. This is an implementation of the
// `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2.
walk = function (source, property, callback) {
var value = source[property], length;
if (typeof value == "object" && value) {
if (getClass.call(value) == "[object Array]") {
for (length = value.length; length--;) {
update(value, length, callback);
}
} else {
// `forEach` can't be used to traverse an array in Opera <= 8.54,
// as `Object#hasOwnProperty` returns `false` for array indices
// (e.g., `![1, 2, 3].hasOwnProperty("0")`).
forEach(value, function (property) {
update(value, property, callback);
});
}
}
return callback.call(source, property, value);
};
// Public: `JSON.parse`. See ES 5.1 section 15.12.2.
JSON3.parse = function (source, callback) {
var result, value;
Index = 0;
Source = source;
result = get(lex());
// If a JSON string contains multiple tokens, it is invalid.
if (lex() != "$") {
abort();
}
// Reset the parser state.
Index = Source = null;
return callback && getClass.call(callback) == "[object Function]" ? walk((value = {}, value[""] = result, value), "", callback) : result;
};
}
}
// Export for asynchronous module loaders.
if (isLoader) {
define(function () {
return JSON3;
});
}
}).call(this);
\ No newline at end of file
/** vim: et:ts=4:sw=4:sts=4 /** vim: et:ts=4:sw=4:sts=4
* @license RequireJS 2.1.6 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. * @license RequireJS 2.1.20 Copyright (c) 2010-2015, The Dojo Foundation All Rights Reserved.
* Available via the MIT or new BSD license. * Available via the MIT or new BSD license.
* see: http://github.com/jrburke/requirejs for details * see: http://github.com/jrburke/requirejs for details
*/ */
...@@ -12,7 +12,7 @@ var requirejs, require, define; ...@@ -12,7 +12,7 @@ var requirejs, require, define;
(function (global) { (function (global) {
var req, s, head, baseElement, dataMain, src, var req, s, head, baseElement, dataMain, src,
interactiveScript, currentlyAddingScript, mainScript, subPath, interactiveScript, currentlyAddingScript, mainScript, subPath,
version = '2.1.6', version = '2.1.20',
commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
jsSuffixRegExp = /\.js$/, jsSuffixRegExp = /\.js$/,
...@@ -21,8 +21,7 @@ var requirejs, require, define; ...@@ -21,8 +21,7 @@ var requirejs, require, define;
ostring = op.toString, ostring = op.toString,
hasOwn = op.hasOwnProperty, hasOwn = op.hasOwnProperty,
ap = Array.prototype, ap = Array.prototype,
apsp = ap.splice, isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document),
isBrowser = !!(typeof window !== 'undefined' && navigator && window.document),
isWebWorker = !isBrowser && typeof importScripts !== 'undefined', isWebWorker = !isBrowser && typeof importScripts !== 'undefined',
//PS3 indicates loaded and complete, but need to wait for complete //PS3 indicates loaded and complete, but need to wait for complete
//specifically. Sequence is 'loading', 'loaded', execution, //specifically. Sequence is 'loading', 'loaded', execution,
...@@ -108,7 +107,10 @@ var requirejs, require, define; ...@@ -108,7 +107,10 @@ var requirejs, require, define;
if (source) { if (source) {
eachProp(source, function (value, prop) { eachProp(source, function (value, prop) {
if (force || !hasProp(target, prop)) { if (force || !hasProp(target, prop)) {
if (deepStringMixin && typeof value !== 'string') { if (deepStringMixin && typeof value === 'object' && value &&
!isArray(value) && !isFunction(value) &&
!(value instanceof RegExp)) {
if (!target[prop]) { if (!target[prop]) {
target[prop] = {}; target[prop] = {};
} }
...@@ -138,7 +140,7 @@ var requirejs, require, define; ...@@ -138,7 +140,7 @@ var requirejs, require, define;
throw err; throw err;
} }
//Allow getting a global that expressed in //Allow getting a global that is expressed in
//dot notation, like 'a.b.c'. //dot notation, like 'a.b.c'.
function getGlobal(value) { function getGlobal(value) {
if (!value) { if (!value) {
...@@ -177,7 +179,7 @@ var requirejs, require, define; ...@@ -177,7 +179,7 @@ var requirejs, require, define;
if (typeof requirejs !== 'undefined') { if (typeof requirejs !== 'undefined') {
if (isFunction(requirejs)) { if (isFunction(requirejs)) {
//Do not overwrite and existing requirejs instance. //Do not overwrite an existing requirejs instance.
return; return;
} }
cfg = requirejs; cfg = requirejs;
...@@ -201,6 +203,7 @@ var requirejs, require, define; ...@@ -201,6 +203,7 @@ var requirejs, require, define;
waitSeconds: 7, waitSeconds: 7,
baseUrl: './', baseUrl: './',
paths: {}, paths: {},
bundles: {},
pkgs: {}, pkgs: {},
shim: {}, shim: {},
config: {} config: {}
...@@ -214,6 +217,7 @@ var requirejs, require, define; ...@@ -214,6 +217,7 @@ var requirejs, require, define;
defQueue = [], defQueue = [],
defined = {}, defined = {},
urlFetched = {}, urlFetched = {},
bundlesMap = {},
requireCounter = 1, requireCounter = 1,
unnormalizedCounter = 1; unnormalizedCounter = 1;
...@@ -228,20 +232,19 @@ var requirejs, require, define; ...@@ -228,20 +232,19 @@ var requirejs, require, define;
*/ */
function trimDots(ary) { function trimDots(ary) {
var i, part; var i, part;
for (i = 0; ary[i]; i += 1) { for (i = 0; i < ary.length; i++) {
part = ary[i]; part = ary[i];
if (part === '.') { if (part === '.') {
ary.splice(i, 1); ary.splice(i, 1);
i -= 1; i -= 1;
} else if (part === '..') { } else if (part === '..') {
if (i === 1 && (ary[2] === '..' || ary[0] === '..')) { // If at the start, or previous value is still ..,
//End of the line. Keep at least one non-dot // keep them so that when converted to a path it may
//path segment at the front so it can be mapped // still work when converted to a path, even though
//correctly to disk. Otherwise, there is likely // as an ID it is less than ideal. In larger point
//no path mapping for a path starting with '..'. // releases, may be better to just kick out an error.
//This can still fail, but catches the most reasonable if (i === 0 || (i === 1 && ary[2] === '..') || ary[i - 1] === '..') {
//uses of .. continue;
break;
} else if (i > 0) { } else if (i > 0) {
ary.splice(i - 1, 2); ary.splice(i - 1, 2);
i -= 2; i -= 2;
...@@ -261,54 +264,45 @@ var requirejs, require, define; ...@@ -261,54 +264,45 @@ var requirejs, require, define;
* @returns {String} normalized name * @returns {String} normalized name
*/ */
function normalize(name, baseName, applyMap) { function normalize(name, baseName, applyMap) {
var pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment, var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex,
foundMap, foundI, foundStarMap, starI, foundMap, foundI, foundStarMap, starI, normalizedBaseParts,
baseParts = baseName && baseName.split('/'), baseParts = (baseName && baseName.split('/')),
normalizedBaseParts = baseParts,
map = config.map, map = config.map,
starMap = map && map['*']; starMap = map && map['*'];
//Adjust any relative paths. //Adjust any relative paths.
if (name && name.charAt(0) === '.') { if (name) {
//If have a base name, try to normalize against it, name = name.split('/');
//otherwise, assume it is a top-level require that will lastIndex = name.length - 1;
//be relative to baseUrl in the end.
if (baseName) { // If wanting node ID compatibility, strip .js from end
if (getOwn(config.pkgs, baseName)) { // of IDs. Have to do this here, and not in nameToUrl
//If the baseName is a package name, then just treat it as one // because node allows either .js or non .js to map
//name to concat the name with. // to same file.
normalizedBaseParts = baseParts = [baseName]; if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
} else { name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
}
// Starts with a '.' so need the baseName
if (name[0].charAt(0) === '.' && baseParts) {
//Convert baseName to array, and lop off the last part, //Convert baseName to array, and lop off the last part,
//so that . matches that 'directory' and not name of the baseName's //so that . matches that 'directory' and not name of the baseName's
//module. For instance, baseName of 'one/two/three', maps to //module. For instance, baseName of 'one/two/three', maps to
//'one/two/three.js', but we want the directory, 'one/two' for //'one/two/three.js', but we want the directory, 'one/two' for
//this normalization. //this normalization.
normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
name = normalizedBaseParts.concat(name);
} }
name = normalizedBaseParts.concat(name.split('/'));
trimDots(name); trimDots(name);
//Some use of packages may use a . path to reference the
//'main' module name, so normalize for that.
pkgConfig = getOwn(config.pkgs, (pkgName = name[0]));
name = name.join('/'); 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);
}
} }
//Apply map config if available. //Apply map config if available.
if (applyMap && map && (baseParts || starMap)) { if (applyMap && map && (baseParts || starMap)) {
nameParts = name.split('/'); nameParts = name.split('/');
for (i = nameParts.length; i > 0; i -= 1) { outerLoop: for (i = nameParts.length; i > 0; i -= 1) {
nameSegment = nameParts.slice(0, i).join('/'); nameSegment = nameParts.slice(0, i).join('/');
if (baseParts) { if (baseParts) {
...@@ -325,14 +319,10 @@ var requirejs, require, define; ...@@ -325,14 +319,10 @@ var requirejs, require, define;
//Match, update name to the new value. //Match, update name to the new value.
foundMap = mapValue; foundMap = mapValue;
foundI = i; foundI = i;
break; break outerLoop;
}
} }
} }
} }
if (foundMap) {
break;
} }
//Check for a star map match, but just hold on to it, //Check for a star map match, but just hold on to it,
...@@ -355,7 +345,11 @@ var requirejs, require, define; ...@@ -355,7 +345,11 @@ var requirejs, require, define;
} }
} }
return name; // If the name points to a package's name, use
// the package main instead.
pkgMain = getOwn(config.pkgs, name);
return pkgMain ? pkgMain : name;
} }
function removeScript(name) { function removeScript(name) {
...@@ -373,12 +367,17 @@ var requirejs, require, define; ...@@ -373,12 +367,17 @@ var requirejs, require, define;
function hasPathFallback(id) { function hasPathFallback(id) {
var pathConfig = getOwn(config.paths, id); var pathConfig = getOwn(config.paths, id);
if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) { if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) {
removeScript(id);
//Pop off the first array value, since it failed, and //Pop off the first array value, since it failed, and
//retry //retry
pathConfig.shift(); pathConfig.shift();
context.require.undef(id); context.require.undef(id);
context.require([id]);
//Custom require that does not do map translation, since
//ID is "absolute", already mapped/resolved.
context.makeRequire(null, {
skipMap: true
})([id]);
return true; return true;
} }
} }
...@@ -444,7 +443,16 @@ var requirejs, require, define; ...@@ -444,7 +443,16 @@ var requirejs, require, define;
return normalize(name, parentName, applyMap); return normalize(name, parentName, applyMap);
}); });
} else { } else {
normalizedName = normalize(name, parentName, applyMap); // If nested plugin references, then do not try to
// normalize, as it will not normalize correctly. This
// places a restriction on resourceIds, and the longer
// term solution is not to normalize until plugins are
// loaded and all normalizations to allow for async
// loading of a loader plugin. But for now, fixes the
// common uses. Details in #1131
normalizedName = name.indexOf('!') === -1 ?
normalize(name, parentName, applyMap) :
name;
} }
} else { } else {
//A regular module. //A regular module.
...@@ -545,11 +553,13 @@ var requirejs, require, define; ...@@ -545,11 +553,13 @@ var requirejs, require, define;
function takeGlobalQueue() { function takeGlobalQueue() {
//Push all the globalDefQueue items into the context's defQueue //Push all the globalDefQueue items into the context's defQueue
if (globalDefQueue.length) { if (globalDefQueue.length) {
//Array splice in the values since the context code has a each(globalDefQueue, function(queueItem) {
//local var ref to defQueue, so cannot just reassign the one var id = queueItem[0];
//on context. if (typeof id === 'string') {
apsp.apply(defQueue, context.defQueueMap[id] = true;
[defQueue.length - 1, 0].concat(globalDefQueue)); }
defQueue.push(queueItem);
});
globalDefQueue = []; globalDefQueue = [];
} }
} }
...@@ -566,7 +576,7 @@ var requirejs, require, define; ...@@ -566,7 +576,7 @@ var requirejs, require, define;
mod.usingExports = true; mod.usingExports = true;
if (mod.map.isDefine) { if (mod.map.isDefine) {
if (mod.exports) { if (mod.exports) {
return mod.exports; return (defined[mod.map.id] = mod.exports);
} else { } else {
return (mod.exports = defined[mod.map.id] = {}); return (mod.exports = defined[mod.map.id] = {});
} }
...@@ -580,15 +590,9 @@ var requirejs, require, define; ...@@ -580,15 +590,9 @@ var requirejs, require, define;
id: mod.map.id, id: mod.map.id,
uri: mod.map.url, uri: mod.map.url,
config: function () { config: function () {
var c, return getOwn(config.config, mod.map.id) || {};
pkg = getOwn(config.pkgs, mod.map.id);
// For packages, only support config targeted
// at the main module.
c = pkg ? getOwn(config.config, mod.map.id + '/' + pkg.main) :
getOwn(config.config, mod.map.id);
return c || {};
}, },
exports: defined[mod.map.id] exports: mod.exports || (mod.exports = {})
}); });
} }
} }
...@@ -629,7 +633,7 @@ var requirejs, require, define; ...@@ -629,7 +633,7 @@ var requirejs, require, define;
} }
function checkLoaded() { function checkLoaded() {
var map, modId, err, usingPathFallback, var err, usingPathFallback,
waitInterval = config.waitSeconds * 1000, waitInterval = config.waitSeconds * 1000,
//It is possible to disable the wait interval by using waitSeconds of 0. //It is possible to disable the wait interval by using waitSeconds of 0.
expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(),
...@@ -647,7 +651,7 @@ var requirejs, require, define; ...@@ -647,7 +651,7 @@ var requirejs, require, define;
//Figure out the state of all the modules. //Figure out the state of all the modules.
eachProp(enabledRegistry, function (mod) { eachProp(enabledRegistry, function (mod) {
map = mod.map; var map = mod.map,
modId = map.id; modId = map.id;
//Skip things that are not enabled or in error state. //Skip things that are not enabled or in error state.
...@@ -842,7 +846,10 @@ var requirejs, require, define; ...@@ -842,7 +846,10 @@ var requirejs, require, define;
factory = this.factory; factory = this.factory;
if (!this.inited) { if (!this.inited) {
// Only fetch if not already in the defQueue.
if (!hasProp(context.defQueueMap, id)) {
this.fetch(); this.fetch();
}
} else if (this.error) { } else if (this.error) {
this.emit('error', this.error); this.emit('error', this.error);
} else if (!this.defining) { } else if (!this.defining) {
...@@ -871,17 +878,14 @@ var requirejs, require, define; ...@@ -871,17 +878,14 @@ var requirejs, require, define;
exports = context.execCb(id, factory, depExports, exports); exports = context.execCb(id, factory, depExports, exports);
} }
if (this.map.isDefine) { // Favor return value over exports. If node/cjs in play,
//If setting exports via 'module' is in play, // then will not have a return value anyway. Favor
//favor that over return value and exports. After that, // module.exports assignment over exports object.
//favor a non-undefined return value over exports use. if (this.map.isDefine && exports === undefined) {
cjsModule = this.module; cjsModule = this.module;
if (cjsModule && if (cjsModule) {
cjsModule.exports !== undefined &&
//Make sure it is not already the exports value
cjsModule.exports !== this.exports) {
exports = cjsModule.exports; exports = cjsModule.exports;
} else if (exports === undefined && this.usingExports) { } else if (this.usingExports) {
//exports already set the defined value. //exports already set the defined value.
exports = this.exports; exports = this.exports;
} }
...@@ -941,6 +945,7 @@ var requirejs, require, define; ...@@ -941,6 +945,7 @@ var requirejs, require, define;
on(pluginMap, 'defined', bind(this, function (plugin) { on(pluginMap, 'defined', bind(this, function (plugin) {
var load, normalizedMap, normalizedMod, var load, normalizedMap, normalizedMod,
bundleId = getOwn(bundlesMap, this.map.id),
name = this.map.name, name = this.map.name,
parentName = this.map.parentMap ? this.map.parentMap.name : null, parentName = this.map.parentMap ? this.map.parentMap.name : null,
localRequire = context.makeRequire(map.parentMap, { localRequire = context.makeRequire(map.parentMap, {
...@@ -986,6 +991,14 @@ var requirejs, require, define; ...@@ -986,6 +991,14 @@ var requirejs, require, define;
return; return;
} }
//If a paths config, then just load that file instead to
//resolve the plugin, as it is built into that paths layer.
if (bundleId) {
this.map.url = context.nameToUrl(bundleId);
this.load();
return;
}
load = bind(this, function (value) { load = bind(this, function (value) {
this.init([], function () { return value; }, null, { this.init([], function () { return value; }, null, {
enabled: true enabled: true
...@@ -1108,12 +1121,22 @@ var requirejs, require, define; ...@@ -1108,12 +1121,22 @@ var requirejs, require, define;
this.depCount += 1; this.depCount += 1;
on(depMap, 'defined', bind(this, function (depExports) { on(depMap, 'defined', bind(this, function (depExports) {
if (this.undefed) {
return;
}
this.defineDep(i, depExports); this.defineDep(i, depExports);
this.check(); this.check();
})); }));
if (this.errback) { if (this.errback) {
on(depMap, 'error', bind(this, this.errback)); on(depMap, 'error', bind(this, this.errback));
} else if (this.events.error) {
// No direct errback on this module, but something
// else is listening for errors, so be sure to
// propagate the error correctly.
on(depMap, 'error', bind(this, function(err) {
this.emit('error', err);
}));
} }
} }
...@@ -1217,13 +1240,15 @@ var requirejs, require, define; ...@@ -1217,13 +1240,15 @@ var requirejs, require, define;
while (defQueue.length) { while (defQueue.length) {
args = defQueue.shift(); args = defQueue.shift();
if (args[0] === null) { if (args[0] === null) {
return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' +
args[args.length - 1]));
} else { } else {
//args are id, deps, factory. Should be normalized by the //args are id, deps, factory. Should be normalized by the
//define() function. //define() function.
callGetModule(args); callGetModule(args);
} }
} }
context.defQueueMap = {};
} }
context = { context = {
...@@ -1233,6 +1258,7 @@ var requirejs, require, define; ...@@ -1233,6 +1258,7 @@ var requirejs, require, define;
defined: defined, defined: defined,
urlFetched: urlFetched, urlFetched: urlFetched,
defQueue: defQueue, defQueue: defQueue,
defQueueMap: {},
Module: Module, Module: Module,
makeModuleMap: makeModuleMap, makeModuleMap: makeModuleMap,
nextTick: req.nextTick, nextTick: req.nextTick,
...@@ -1250,31 +1276,38 @@ var requirejs, require, define; ...@@ -1250,31 +1276,38 @@ var requirejs, require, define;
} }
} }
//Save off the paths and packages since they require special processing, //Save off the paths since they require special processing,
//they are additive. //they are additive.
var pkgs = config.pkgs, var shim = config.shim,
shim = config.shim,
objs = { objs = {
paths: true, paths: true,
bundles: true,
config: true, config: true,
map: true map: true
}; };
eachProp(cfg, function (value, prop) { eachProp(cfg, function (value, prop) {
if (objs[prop]) { if (objs[prop]) {
if (prop === 'map') { if (!config[prop]) {
if (!config.map) { config[prop] = {};
config.map = {};
} }
mixin(config[prop], value, true, true); mixin(config[prop], value, true, true);
} else {
mixin(config[prop], value, true);
}
} else { } else {
config[prop] = value; config[prop] = value;
} }
}); });
//Reverse map the bundles
if (cfg.bundles) {
eachProp(cfg.bundles, function (value, prop) {
each(value, function (v) {
if (v !== prop) {
bundlesMap[v] = prop;
}
});
});
}
//Merge shim //Merge shim
if (cfg.shim) { if (cfg.shim) {
eachProp(cfg.shim, function (value, id) { eachProp(cfg.shim, function (value, id) {
...@@ -1295,29 +1328,25 @@ var requirejs, require, define; ...@@ -1295,29 +1328,25 @@ var requirejs, require, define;
//Adjust packages if necessary. //Adjust packages if necessary.
if (cfg.packages) { if (cfg.packages) {
each(cfg.packages, function (pkgObj) { each(cfg.packages, function (pkgObj) {
var location; var location, name;
pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj; pkgObj = typeof pkgObj === 'string' ? {name: pkgObj} : pkgObj;
name = pkgObj.name;
location = pkgObj.location; location = pkgObj.location;
if (location) {
config.paths[name] = pkgObj.location;
}
//Create a brand new object on pkgs, since currentPackages can //Save pointer to main module ID for pkg name.
//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, //Remove leading dot in main, so main paths are normalized,
//and remove any trailing .js, since different package //and remove any trailing .js, since different package
//envs have different conventions: some use a module name, //envs have different conventions: some use a module name,
//some use a file name. //some use a file name.
main: (pkgObj.main || 'main') config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main')
.replace(currDirRegExp, '') .replace(currDirRegExp, '')
.replace(jsSuffixRegExp, '') .replace(jsSuffixRegExp, '');
};
}); });
//Done with modifications, assing packages back to context config
config.pkgs = pkgs;
} }
//If there are any "waiting to execute" modules in the registry, //If there are any "waiting to execute" modules in the registry,
...@@ -1328,7 +1357,7 @@ var requirejs, require, define; ...@@ -1328,7 +1357,7 @@ var requirejs, require, define;
//late to modify them, and ignore unnormalized ones //late to modify them, and ignore unnormalized ones
//since they are transient. //since they are transient.
if (!mod.inited && !mod.map.unnormalized) { if (!mod.inited && !mod.map.unnormalized) {
mod.map = makeModuleMap(id); mod.map = makeModuleMap(id, null, true);
} }
}); });
...@@ -1464,10 +1493,23 @@ var requirejs, require, define; ...@@ -1464,10 +1493,23 @@ var requirejs, require, define;
var map = makeModuleMap(id, relMap, true), var map = makeModuleMap(id, relMap, true),
mod = getOwn(registry, id); mod = getOwn(registry, id);
mod.undefed = true;
removeScript(id);
delete defined[id]; delete defined[id];
delete urlFetched[map.url]; delete urlFetched[map.url];
delete undefEvents[id]; delete undefEvents[id];
//Clean queued defines too. Go backwards
//in array so that the splices do not
//mess up the iteration.
eachReverse(defQueue, function(args, i) {
if (args[0] === id) {
defQueue.splice(i, 1);
}
});
delete context.defQueueMap[id];
if (mod) { if (mod) {
//Hold on to listeners in case the //Hold on to listeners in case the
//module will be attempted to be reloaded //module will be attempted to be reloaded
...@@ -1487,7 +1529,7 @@ var requirejs, require, define; ...@@ -1487,7 +1529,7 @@ var requirejs, require, define;
/** /**
* Called to enable a module if it is still in the registry * Called to enable a module if it is still in the registry
* awaiting enablement. A second arg, parent, the parent module, * awaiting enablement. A second arg, parent, the parent module,
* is passed in for context, when this method is overriden by * is passed in for context, when this method is overridden by
* the optimizer. Not shown here to keep code compact. * the optimizer. Not shown here to keep code compact.
*/ */
enable: function (depMap) { enable: function (depMap) {
...@@ -1528,6 +1570,7 @@ var requirejs, require, define; ...@@ -1528,6 +1570,7 @@ var requirejs, require, define;
callGetModule(args); callGetModule(args);
} }
context.defQueueMap = {};
//Do this after the cycle of callGetModule in case the result //Do this after the cycle of callGetModule in case the result
//of those calls/init calls changes the registry. //of those calls/init calls changes the registry.
...@@ -1561,8 +1604,19 @@ var requirejs, require, define; ...@@ -1561,8 +1604,19 @@ var requirejs, require, define;
* internal API, not a public one. Use toUrl for the public API. * internal API, not a public one. Use toUrl for the public API.
*/ */
nameToUrl: function (moduleName, ext, skipExt) { nameToUrl: function (moduleName, ext, skipExt) {
var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url, var paths, syms, i, parentModule, url,
parentPath; parentPath, bundleId,
pkgMain = getOwn(config.pkgs, moduleName);
if (pkgMain) {
moduleName = pkgMain;
}
bundleId = getOwn(bundlesMap, moduleName);
if (bundleId) {
return context.nameToUrl(bundleId, ext, skipExt);
}
//If a colon is in the URL, it indicates a protocol is used and it is just //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, contains a query arg (i.e. ?) //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?)
...@@ -1576,7 +1630,6 @@ var requirejs, require, define; ...@@ -1576,7 +1630,6 @@ var requirejs, require, define;
} else { } else {
//A module that needs to be converted to a path. //A module that needs to be converted to a path.
paths = config.paths; paths = config.paths;
pkgs = config.pkgs;
syms = moduleName.split('/'); syms = moduleName.split('/');
//For each module name segment, see if there is a path //For each module name segment, see if there is a path
...@@ -1584,7 +1637,7 @@ var requirejs, require, define; ...@@ -1584,7 +1637,7 @@ var requirejs, require, define;
//and work up from it. //and work up from it.
for (i = syms.length; i > 0; i -= 1) { for (i = syms.length; i > 0; i -= 1) {
parentModule = syms.slice(0, i).join('/'); parentModule = syms.slice(0, i).join('/');
pkg = getOwn(pkgs, parentModule);
parentPath = getOwn(paths, parentModule); parentPath = getOwn(paths, parentModule);
if (parentPath) { if (parentPath) {
//If an array, it means there are a few choices, //If an array, it means there are a few choices,
...@@ -1594,22 +1647,12 @@ var requirejs, require, define; ...@@ -1594,22 +1647,12 @@ var requirejs, require, define;
} }
syms.splice(0, i, parentPath); syms.splice(0, i, parentPath);
break; break;
} else if (pkg) {
//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. //Join the path parts together, then figure out if baseUrl is needed.
url = syms.join('/'); url = syms.join('/');
url += (ext || (/\?/.test(url) || skipExt ? '' : '.js')); url += (ext || (/^data\:|\?/.test(url) || skipExt ? '' : '.js'));
url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url;
} }
...@@ -1794,6 +1837,19 @@ var requirejs, require, define; ...@@ -1794,6 +1837,19 @@ var requirejs, require, define;
*/ */
req.onError = defaultOnError; req.onError = defaultOnError;
/**
* Creates the node for the load command. Only used in browser envs.
*/
req.createNode = function (config, moduleName, url) {
var node = config.xhtml ?
document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
document.createElement('script');
node.type = config.scriptType || 'text/javascript';
node.charset = 'utf-8';
node.async = true;
return node;
};
/** /**
* Does the request to load a module for the browser case. * Does the request to load a module for the browser case.
* Make this a separate function to allow other environments * Make this a separate function to allow other environments
...@@ -1808,12 +1864,10 @@ var requirejs, require, define; ...@@ -1808,12 +1864,10 @@ var requirejs, require, define;
node; node;
if (isBrowser) { if (isBrowser) {
//In the browser so use a script tag //In the browser so use a script tag
node = config.xhtml ? node = req.createNode(config, moduleName, url);
document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') : if (config.onNodeCreated) {
document.createElement('script'); config.onNodeCreated(node, config, moduleName, url);
node.type = config.scriptType || 'text/javascript'; }
node.charset = 'utf-8';
node.async = true;
node.setAttribute('data-requirecontext', context.contextName); node.setAttribute('data-requirecontext', context.contextName);
node.setAttribute('data-requiremodule', moduleName); node.setAttribute('data-requiremodule', moduleName);
...@@ -1910,7 +1964,7 @@ var requirejs, require, define; ...@@ -1910,7 +1964,7 @@ var requirejs, require, define;
} }
//Look for a data-main script attribute, which could also adjust the baseUrl. //Look for a data-main script attribute, which could also adjust the baseUrl.
if (isBrowser) { if (isBrowser && !cfg.skipDataMain) {
//Figure out baseUrl. Get it from the script tag with require.js in it. //Figure out baseUrl. Get it from the script tag with require.js in it.
eachReverse(scripts(), function (script) { eachReverse(scripts(), function (script) {
//Set the 'head' where we can append children by //Set the 'head' where we can append children by
...@@ -2021,14 +2075,18 @@ var requirejs, require, define; ...@@ -2021,14 +2075,18 @@ var requirejs, require, define;
//where the module name is not known until the script onload event //where the module name is not known until the script onload event
//occurs. If no context, use the global queue, and get it processed //occurs. If no context, use the global queue, and get it processed
//in the onscript load callback. //in the onscript load callback.
(context ? context.defQueue : globalDefQueue).push([name, deps, callback]); if (context) {
context.defQueue.push([name, deps, callback]);
context.defQueueMap[name] = true;
} else {
globalDefQueue.push([name, deps, callback]);
}
}; };
define.amd = { define.amd = {
jQuery: true jQuery: true
}; };
/** /**
* Executes the text. Normally just uses eval, but can be modified * Executes the text. Normally just uses eval, but can be modified
* to use a better, environment-specific call. Only used for transpiling * to use a better, environment-specific call. Only used for transpiling
......
...@@ -12,25 +12,27 @@ button { ...@@ -12,25 +12,27 @@ button {
font-size: 100%; font-size: 100%;
vertical-align: baseline; vertical-align: baseline;
font-family: inherit; font-family: inherit;
font-weight: inherit;
color: inherit; color: inherit;
-webkit-appearance: none; -webkit-appearance: none;
-ms-appearance: none;
-o-appearance: none;
appearance: none; appearance: none;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
font-smoothing: antialiased;
} }
body { body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em; line-height: 1.4em;
background: #eaeaea url('bg.png'); background: #f5f5f5;
color: #4d4d4d; color: #4d4d4d;
width: 550px; min-width: 230px;
max-width: 550px;
margin: 0 auto; margin: 0 auto;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased; -moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased; font-smoothing: antialiased;
font-weight: 300;
} }
button, button,
...@@ -38,85 +40,57 @@ input[type="checkbox"] { ...@@ -38,85 +40,57 @@ input[type="checkbox"] {
outline: none; outline: none;
} }
#todoapp { .hidden {
display: none;
}
.todoapp {
background: #fff; background: #fff;
background: rgba(255, 255, 255, 0.9);
margin: 130px 0 40px 0; margin: 130px 0 40px 0;
border: 1px solid #ccc;
position: relative; position: relative;
border-top-left-radius: 2px; box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
border-top-right-radius: 2px; 0 25px 50px 0 rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.15);
} }
#todoapp:before { .todoapp input::-webkit-input-placeholder {
content: ''; font-style: italic;
border-left: 1px solid #f5d6d6; font-weight: 300;
border-right: 1px solid #f5d6d6; color: #e6e6e6;
width: 2px;
position: absolute;
top: 0;
left: 40px;
height: 100%;
} }
#todoapp input::-webkit-input-placeholder { .todoapp input::-moz-placeholder {
font-style: italic; font-style: italic;
font-weight: 300;
color: #e6e6e6;
} }
#todoapp input::-moz-placeholder { .todoapp input::input-placeholder {
font-style: italic; font-style: italic;
color: #a9a9a9; font-weight: 300;
color: #e6e6e6;
} }
#todoapp h1 { .todoapp h1 {
position: absolute; position: absolute;
top: -120px; top: -155px;
width: 100%; width: 100%;
font-size: 70px; font-size: 100px;
font-weight: bold; font-weight: 100;
text-align: center; text-align: center;
color: #b3b3b3; color: rgba(175, 47, 47, 0.15);
color: rgba(255, 255, 255, 0.3);
text-shadow: -1px -1px rgba(0, 0, 0, 0.2);
-webkit-text-rendering: optimizeLegibility; -webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility; -moz-text-rendering: optimizeLegibility;
-ms-text-rendering: optimizeLegibility;
-o-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
} }
#header { .new-todo,
padding-top: 15px;
border-radius: inherit;
}
#header:before {
content: '';
position: absolute;
top: 0;
right: 0;
left: 0;
height: 15px;
z-index: 2;
border-bottom: 1px solid #6c615c;
background: #8d7d77;
background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8)));
background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670');
border-top-left-radius: 1px;
border-top-right-radius: 1px;
}
#new-todo,
.edit { .edit {
position: relative; position: relative;
margin: 0; margin: 0;
width: 100%; width: 100%;
font-size: 24px; font-size: 24px;
font-family: inherit; font-family: inherit;
font-weight: inherit;
line-height: 1.4em; line-height: 1.4em;
border: 0; border: 0;
outline: none; outline: none;
...@@ -124,89 +98,83 @@ input[type="checkbox"] { ...@@ -124,89 +98,83 @@ input[type="checkbox"] {
padding: 6px; padding: 6px;
border: 1px solid #999; border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased; -moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased; font-smoothing: antialiased;
} }
#new-todo { .new-todo {
padding: 16px 16px 16px 60px; padding: 16px 16px 16px 60px;
border: none; border: none;
background: rgba(0, 0, 0, 0.02); background: rgba(0, 0, 0, 0.003);
z-index: 2; box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
box-shadow: none;
} }
#main { .main {
position: relative; position: relative;
z-index: 2; z-index: 2;
border-top: 1px dotted #adadad; border-top: 1px solid #e6e6e6;
} }
label[for='toggle-all'] { label[for='toggle-all'] {
display: none; display: none;
} }
#toggle-all { .toggle-all {
position: absolute; position: absolute;
top: -42px; top: -55px;
left: -4px; left: -12px;
width: 40px; width: 60px;
height: 34px;
text-align: center; text-align: center;
/* Mobile Safari */ border: none; /* Mobile Safari */
border: none;
} }
#toggle-all:before { .toggle-all:before {
content: '»'; content: '';
font-size: 28px; font-size: 22px;
color: #d9d9d9; color: #e6e6e6;
padding: 0 25px 7px; padding: 10px 27px 10px 27px;
} }
#toggle-all:checked:before { .toggle-all:checked:before {
color: #737373; color: #737373;
} }
#todo-list { .todo-list {
margin: 0; margin: 0;
padding: 0; padding: 0;
list-style: none; list-style: none;
} }
#todo-list li { .todo-list li {
position: relative; position: relative;
font-size: 24px; font-size: 24px;
border-bottom: 1px dotted #ccc; border-bottom: 1px solid #ededed;
} }
#todo-list li:last-child { .todo-list li:last-child {
border-bottom: none; border-bottom: none;
} }
#todo-list li.editing { .todo-list li.editing {
border-bottom: none; border-bottom: none;
padding: 0; padding: 0;
} }
#todo-list li.editing .edit { .todo-list li.editing .edit {
display: block; display: block;
width: 506px; width: 506px;
padding: 13px 17px 12px 17px; padding: 13px 17px 12px 17px;
margin: 0 0 0 43px; margin: 0 0 0 43px;
} }
#todo-list li.editing .view { .todo-list li.editing .view {
display: none; display: none;
} }
#todo-list li .toggle { .todo-list li .toggle {
text-align: center; text-align: center;
width: 40px; width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */ /* auto, since non-WebKit browsers doesn't support input styling */
...@@ -215,47 +183,35 @@ label[for='toggle-all'] { ...@@ -215,47 +183,35 @@ label[for='toggle-all'] {
top: 0; top: 0;
bottom: 0; bottom: 0;
margin: auto 0; margin: auto 0;
/* Mobile Safari */ border: none; /* Mobile Safari */
border: none;
-webkit-appearance: none; -webkit-appearance: none;
-ms-appearance: none;
-o-appearance: none;
appearance: none; appearance: none;
} }
#todo-list li .toggle:after { .todo-list li .toggle:after {
content: '✔'; content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>');
/* 40 + a couple of pixels visual adjustment */
line-height: 43px;
font-size: 20px;
color: #d9d9d9;
text-shadow: 0 -1px 0 #bfbfbf;
} }
#todo-list li .toggle:checked:after { .todo-list li .toggle:checked:after {
color: #85ada7; content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>');
text-shadow: 0 1px 0 #669991;
bottom: 1px;
position: relative;
} }
#todo-list li label { .todo-list li label {
white-space: pre; white-space: pre;
word-break: break-word; word-break: break-word;
padding: 15px 60px 15px 15px; padding: 15px 60px 15px 15px;
margin-left: 45px; margin-left: 45px;
display: block; display: block;
line-height: 1.2; line-height: 1.2;
-webkit-transition: color 0.4s;
transition: color 0.4s; transition: color 0.4s;
} }
#todo-list li.completed label { .todo-list li.completed label {
color: #a9a9a9; color: #d9d9d9;
text-decoration: line-through; text-decoration: line-through;
} }
#todo-list li .destroy { .todo-list li .destroy {
display: none; display: none;
position: absolute; position: absolute;
top: 0; top: 0;
...@@ -264,68 +220,65 @@ label[for='toggle-all'] { ...@@ -264,68 +220,65 @@ label[for='toggle-all'] {
width: 40px; width: 40px;
height: 40px; height: 40px;
margin: auto 0; margin: auto 0;
font-size: 22px; font-size: 30px;
color: #a88a8a; color: #cc9a9a;
-webkit-transition: all 0.2s; margin-bottom: 11px;
transition: all 0.2s; transition: color 0.2s ease-out;
} }
#todo-list li .destroy:hover { .todo-list li .destroy:hover {
text-shadow: 0 0 1px #000, color: #af5b5e;
0 0 10px rgba(199, 107, 107, 0.8);
-webkit-transform: scale(1.3);
transform: scale(1.3);
} }
#todo-list li .destroy:after { .todo-list li .destroy:after {
content: ''; content: '×';
} }
#todo-list li:hover .destroy { .todo-list li:hover .destroy {
display: block; display: block;
} }
#todo-list li .edit { .todo-list li .edit {
display: none; display: none;
} }
#todo-list li.editing:last-child { .todo-list li.editing:last-child {
margin-bottom: -1px; margin-bottom: -1px;
} }
#footer { .footer {
color: #777; color: #777;
padding: 0 15px; padding: 10px 15px;
position: absolute;
right: 0;
bottom: -31px;
left: 0;
height: 20px; height: 20px;
z-index: 1;
text-align: center; text-align: center;
border-top: 1px solid #e6e6e6;
} }
#footer:before { .footer:before {
content: ''; content: '';
position: absolute; position: absolute;
right: 0; right: 0;
bottom: 31px; bottom: 0;
left: 0; left: 0;
height: 50px; height: 50px;
z-index: -1; overflow: hidden;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
0 6px 0 -3px rgba(255, 255, 255, 0.8), 0 8px 0 -3px #f6f6f6,
0 7px 1px -3px rgba(0, 0, 0, 0.3), 0 9px 1px -3px rgba(0, 0, 0, 0.2),
0 43px 0 -6px rgba(255, 255, 255, 0.8), 0 16px 0 -6px #f6f6f6,
0 44px 2px -6px rgba(0, 0, 0, 0.2); 0 17px 2px -6px rgba(0, 0, 0, 0.2);
} }
#todo-count { .todo-count {
float: left; float: left;
text-align: left; text-align: left;
} }
#filters { .todo-count strong {
font-weight: 300;
}
.filters {
margin: 0; margin: 0;
padding: 0; padding: 0;
list-style: none; list-style: none;
...@@ -334,69 +287,79 @@ label[for='toggle-all'] { ...@@ -334,69 +287,79 @@ label[for='toggle-all'] {
left: 0; left: 0;
} }
#filters li { .filters li {
display: inline; display: inline;
} }
#filters li a { .filters li a {
color: #83756f; color: inherit;
margin: 2px; margin: 3px;
padding: 3px 7px;
text-decoration: none; text-decoration: none;
border: 1px solid transparent;
border-radius: 3px;
}
.filters li a.selected,
.filters li a:hover {
border-color: rgba(175, 47, 47, 0.1);
} }
#filters li a.selected { .filters li a.selected {
font-weight: bold; border-color: rgba(175, 47, 47, 0.2);
} }
#clear-completed { .clear-completed,
html .clear-completed:active {
float: right; float: right;
position: relative; position: relative;
line-height: 20px; line-height: 20px;
text-decoration: none; text-decoration: none;
background: rgba(0, 0, 0, 0.1); cursor: pointer;
font-size: 11px; position: relative;
padding: 0 10px;
border-radius: 3px;
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2);
} }
#clear-completed:hover { .clear-completed:hover {
background: rgba(0, 0, 0, 0.15); text-decoration: underline;
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3);
} }
#info { .info {
margin: 65px auto 0; margin: 65px auto 0;
color: #a6a6a6; color: #bfbfbf;
font-size: 12px; font-size: 10px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
text-align: center; text-align: center;
} }
#info a { .info p {
line-height: 1;
}
.info a {
color: inherit; color: inherit;
text-decoration: none;
font-weight: 400;
}
.info a:hover {
text-decoration: underline;
} }
/* /*
Hack to remove background from Mobile Safari. Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox and Opera Can't use it globally since it destroys checkboxes in Firefox
*/ */
@media screen and (-webkit-min-device-pixel-ratio:0) { @media screen and (-webkit-min-device-pixel-ratio:0) {
#toggle-all, .toggle-all,
#todo-list li .toggle { .todo-list li .toggle {
background: none; background: none;
} }
#todo-list li .toggle { .todo-list li .toggle {
height: 40px; height: 40px;
} }
#toggle-all { .toggle-all {
top: -56px;
left: -15px;
width: 65px;
height: 41px;
-webkit-transform: rotate(90deg); -webkit-transform: rotate(90deg);
transform: rotate(90deg); transform: rotate(90deg);
-webkit-appearance: none; -webkit-appearance: none;
...@@ -404,151 +367,12 @@ label[for='toggle-all'] { ...@@ -404,151 +367,12 @@ label[for='toggle-all'] {
} }
} }
.hidden { @media (max-width: 430px) {
display: none; .footer {
} height: 50px;
hr {
margin: 20px 0;
border: 0;
border-top: 1px dashed #C5C5C5;
border-bottom: 1px dashed #F7F7F7;
}
.learn a {
font-weight: normal;
text-decoration: none;
color: #b83f45;
}
.learn a:hover {
text-decoration: underline;
color: #787e7e;
}
.learn h3,
.learn h4,
.learn h5 {
margin: 10px 0;
font-weight: 500;
line-height: 1.2;
color: #000;
}
.learn h3 {
font-size: 24px;
}
.learn h4 {
font-size: 18px;
}
.learn h5 {
margin-bottom: 0;
font-size: 14px;
}
.learn ul {
padding: 0;
margin: 0 0 30px 25px;
}
.learn li {
line-height: 20px;
}
.learn p {
font-size: 15px;
font-weight: 300;
line-height: 1.3;
margin-top: 0;
margin-bottom: 0;
}
.quote {
border: none;
margin: 20px 0 60px 0;
}
.quote p {
font-style: italic;
}
.quote p:before {
content: '“';
font-size: 50px;
opacity: .15;
position: absolute;
top: -20px;
left: 3px;
}
.quote p:after {
content: '”';
font-size: 50px;
opacity: .15;
position: absolute;
bottom: -42px;
right: 3px;
}
.quote footer {
position: absolute;
bottom: -40px;
right: 0;
}
.quote footer img {
border-radius: 3px;
}
.quote footer a {
margin-left: 5px;
vertical-align: middle;
}
.speech-bubble {
position: relative;
padding: 10px;
background: rgba(0, 0, 0, .04);
border-radius: 5px;
}
.speech-bubble:after {
content: '';
position: absolute;
top: 100%;
right: 30px;
border: 13px solid transparent;
border-top-color: rgba(0, 0, 0, .04);
}
.learn-bar > .learn {
position: absolute;
width: 272px;
top: 8px;
left: -300px;
padding: 10px;
border-radius: 5px;
background-color: rgba(255, 255, 255, .6);
-webkit-transition-property: left;
transition-property: left;
-webkit-transition-duration: 500ms;
transition-duration: 500ms;
}
@media (min-width: 899px) {
.learn-bar {
width: auto;
margin: 0 0 0 300px;
}
.learn-bar > .learn {
left: 8px;
} }
.learn-bar #todoapp { .filters {
width: 550px; bottom: 10px;
margin: 130px auto 40px auto;
} }
} }
hr {
margin: 20px 0;
border: 0;
border-top: 1px dashed #c5c5c5;
border-bottom: 1px dashed #f7f7f7;
}
.learn a {
font-weight: normal;
text-decoration: none;
color: #b83f45;
}
.learn a:hover {
text-decoration: underline;
color: #787e7e;
}
.learn h3,
.learn h4,
.learn h5 {
margin: 10px 0;
font-weight: 500;
line-height: 1.2;
color: #000;
}
.learn h3 {
font-size: 24px;
}
.learn h4 {
font-size: 18px;
}
.learn h5 {
margin-bottom: 0;
font-size: 14px;
}
.learn ul {
padding: 0;
margin: 0 0 30px 25px;
}
.learn li {
line-height: 20px;
}
.learn p {
font-size: 15px;
font-weight: 300;
line-height: 1.3;
margin-top: 0;
margin-bottom: 0;
}
#issue-count {
display: none;
}
.quote {
border: none;
margin: 20px 0 60px 0;
}
.quote p {
font-style: italic;
}
.quote p:before {
content: '“';
font-size: 50px;
opacity: .15;
position: absolute;
top: -20px;
left: 3px;
}
.quote p:after {
content: '”';
font-size: 50px;
opacity: .15;
position: absolute;
bottom: -42px;
right: 3px;
}
.quote footer {
position: absolute;
bottom: -40px;
right: 0;
}
.quote footer img {
border-radius: 3px;
}
.quote footer a {
margin-left: 5px;
vertical-align: middle;
}
.speech-bubble {
position: relative;
padding: 10px;
background: rgba(0, 0, 0, .04);
border-radius: 5px;
}
.speech-bubble:after {
content: '';
position: absolute;
top: 100%;
right: 30px;
border: 13px solid transparent;
border-top-color: rgba(0, 0, 0, .04);
}
.learn-bar > .learn {
position: absolute;
width: 272px;
top: 8px;
left: -300px;
padding: 10px;
border-radius: 5px;
background-color: rgba(255, 255, 255, .6);
transition-property: left;
transition-duration: 500ms;
}
@media (min-width: 899px) {
.learn-bar {
width: auto;
padding-left: 300px;
}
.learn-bar > .learn {
left: 8px;
}
}
/* global _ */
(function () { (function () {
'use strict'; 'use strict';
/* jshint ignore:start */
// Underscore's Template Module // Underscore's Template Module
// Courtesy of underscorejs.org // Courtesy of underscorejs.org
var _ = (function (_) { var _ = (function (_) {
...@@ -112,8 +114,14 @@ ...@@ -112,8 +114,14 @@
})({}); })({});
if (location.hostname === 'todomvc.com') { if (location.hostname === 'todomvc.com') {
window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')); (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-31081062-1', 'auto');
ga('send', 'pageview');
} }
/* jshint ignore:end */
function redirect() { function redirect() {
if (location.hostname === 'tastejs.github.io') { if (location.hostname === 'tastejs.github.io') {
...@@ -175,13 +183,17 @@ ...@@ -175,13 +183,17 @@
if (learnJSON.backend) { if (learnJSON.backend) {
this.frameworkJSON = learnJSON.backend; this.frameworkJSON = learnJSON.backend;
this.frameworkJSON.issueLabel = framework;
this.append({ this.append({
backend: true backend: true
}); });
} else if (learnJSON[framework]) { } else if (learnJSON[framework]) {
this.frameworkJSON = learnJSON[framework]; this.frameworkJSON = learnJSON[framework];
this.frameworkJSON.issueLabel = framework;
this.append(); this.append();
} }
this.fetchIssueCount();
} }
Learn.prototype.append = function (opts) { Learn.prototype.append = function (opts) {
...@@ -212,6 +224,26 @@ ...@@ -212,6 +224,26 @@
document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); document.body.insertAdjacentHTML('afterBegin', aside.outerHTML);
}; };
Learn.prototype.fetchIssueCount = function () {
var issueLink = document.getElementById('issue-count-link');
if (issueLink) {
var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos');
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = function (e) {
var parsedResponse = JSON.parse(e.target.responseText);
if (parsedResponse instanceof Array) {
var count = parsedResponse.length;
if (count !== 0) {
issueLink.innerHTML = 'This app has ' + count + ' open issues';
document.getElementById('issue-count').style.display = 'inline';
}
}
};
xhr.send();
}
};
redirect(); redirect();
getFile('learn.json', Learn); getFile('learn.json', Learn);
})(); })();
define('troopjs-route-hash/version',[], { 'toString': function () { return ; } });
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-route-hash/component',[
"troopjs-compose/factory",
"troopjs-dom/component",
"troopjs-hub/component",
"troopjs-hub/emitter",
"mu-jquery-hashchange/jquery.hashchange"
], function (Factory, DOMComponent, HUBComponent, hub) {
/**
* Component that attaches to the window object in order to handle `window.location.hash` changes.
* @class route.hash.component
* @extend dom.component
* @alias hash.component
*/
var $ELEMENT = "$element";
var HASH = "_hash";
var RE = /^#/;
/**
* Hash change event (global)
* @localdoc Triggered when a {@link #$element} with {@link route.hash.component} attached to it changes its hash
* @event hub/hash/change
* @param {String} hash The new hash
*/
/**
* Hash change event (local)
* @localdoc Triggered when the attached {@link #$element} hash is changed
* @event dom/hashchange
* @preventable
* @param {Object} $event {@link jQuery} event
* @param {String} hash The new hash
*/
/**
* Hash set event (global)
* @localdoc Triggered when a component wants to change the hash of an {@link #$element}'s with {@link route.hash.component} attached to it
* @event hub/hash/set
* @param {String} hash The new hash
* @param {Boolean} [silent=false] Change the hash silently without triggering {@link route.hash.component#event-hub/hash/change} event.
*/
/**
* Hash set event (local)
* @localdoc Triggered when a component wants to change the attached {@link #$element} hash
* @event dom/hashset
* @preventable
* @param {Object} $event {@link jQuery} event
* @param {String} hash The new hash
* @param {Boolean} [silent=false] Change the hash silently without triggering {@link route.hash.component#event-dom/hashchange} event.
*/
/**
* Fires whenever the browser route has changed
* @event hub/route/change
* @preventable
* @param {String} hash The new hash
*/
/**
* Hash change handler (global)
* @inheritdoc #event-hub/hash/change
* @handler hub/hash/change
* @template
* @return {Promise}
*/
return Factory(HUBComponent, DOMComponent, {
"displayName" : "route/hash/component",
/**
* @inheritdoc
* @handler
*/
"sig/start" : function () {
this[$ELEMENT].trigger("hashchange");
},
/**
* Hash change handler (local)
* @handler
* @inheritdoc #event-dom/hashchange
* @localdoc Handles changing hash of the attached {@link #$element}
* @param {Object} $event {@link jQuery} event
* @fires hub/route/change
*/
"dom/hashchange": function ($event) {
var me = this;
var hash = me[$ELEMENT].get(0).location.hash.replace(RE, "");
// Did anything change?
if (hash !== me[HASH]) {
// Store and publish new hash
hub.emit("route/change", me[HASH] = hash);
}
else {
// Prevent further hashchange handlers from receiving this
$event.stopImmediatePropagation();
}
},
/**
* Hash set handler (local)
* @inheritdoc #event-dom/hashset
* @localdoc Handles setting hash of the attached {@link #$element}
* @handler
*/
"dom/hashset": function ($event, hash, silent) {
hub.emit("route/set", hash, null, silent);
},
/**
* Route set handler (global), implicitly translates to {@link #event-dom/hashset} by setting the {@link #$element} hash
* @handler hub/route/set
* @return {Promise}
*/
"hub/route/set": function (path, data, silent) {
var me = this;
// If we are silent we update the local me[HASH] to prevent change detection
if (silent === true) {
me[HASH] = path;
}
me[$ELEMENT].get(0).location.hash = path;
}
});
});
define(['troopjs-route-hash/version'], function (version) {
return version;
});
\ No newline at end of file
define('troopjs-widget/version',[], { 'toString': function () { return ; } });
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-widget/config',[
"troopjs-dom/config",
"module",
"mu-merge/main"
], function (config, module, merge) {
/**
* @class widget.config.widget
* @enum
* @private
*/
var WIDGET = {
/**
* Property of the widget where the **weft** resides.
*/
"$weft": "$weft",
/**
* Attribute name of the element where the **weave** resides.
*/
"weave": "data-weave",
/**
* Attribute name of the element where the **unweave** resides.
*/
"unweave": "data-unweave",
/**
* Attribute name of the element where the **woven** resides.
*/
"woven": "data-woven"
};
/**
* Provides configuration for the widget package
* @class widget.config
* @extends dom.config
* @private
* @alias feature.config
*/
return merge.call({}, config, {
/**
* Widget related configuration
* @cfg {widget.config.widget}
* @protected
*/
"widget": WIDGET
}, module.config());
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-widget/weave',[
"./config",
"troopjs-core/component/signal/start",
"require",
"when/when",
"jquery",
"mu-getargs/main"
], function (config, start, parentRequire, when, $, getargs) {
/**
* @class widget.weave
* @mixin widget.config
* @mixin Function
* @static
*/
var UNDEFINED;
var NULL = null;
var ARRAY_PROTO = Array.prototype;
var ARRAY_MAP = ARRAY_PROTO.map;
var ARRAY_PUSH = ARRAY_PROTO.push;
var DEFERRED = "deferred";
var MODULE = "module";
var LENGTH = "length";
var $WEFT = config.widget.$weft;
var ATTR_WEAVE = config.widget.weave;
var ATTR_WOVEN = config.widget.woven;
var RE_SEPARATOR = /[\s,]+/;
/**
* Weaves `$element`
* @param {String} weave_attr
* @param {Array} constructor_args
* @return {Promise}
* @ignore
*/
function $weave (weave_attr, constructor_args) {
/*eslint consistent-this:0*/
// Let `$element` be `this`
var $element = this;
// Get all data from `$element`
var $data = $element.data();
// Let `$weft` be `$data[$WEFT]` or `$data[$WEFT] = []`
var $weft = $data.hasOwnProperty($WEFT)
? $data[$WEFT]
: $data[$WEFT] = [];
// Scope `weave_re` locally since we use the `g` flag
var weave_re = /[\s,]*(((?:\w+!)?([\w\/\.\-]+)(?:#[^(\s]+)?)(?:\((.*?)\))?)/g;
// Let `weave_args` be `[]`
var weave_args = [];
var weave_arg;
var args;
var matches;
/**
* Maps `value` to `$data[value]`
* @param {*} value
* @return {*}
* @private
*/
function $map (value) {
return $data.hasOwnProperty(value)
? $data[value]
: value;
}
// Iterate while `weave_re` matches
// matches[1] : max widget name with args - "mv!widget/name#1.x(1, 'string', false)"
// matches[2] : max widget name - "mv!widget/name#1.x"
// matches[3] : min widget name - "widget/name"
// matches[4] : widget arguments - "1, 'string', false"
while ((matches = weave_re.exec(weave_attr)) !== NULL) {
// Let `weave_arg` be [ $element, widget display name ].
// Push `weave_arg` on `weave_args`
ARRAY_PUSH.call(weave_args, weave_arg = [ $element, matches[3] ]);
// Let `weave_arg[MODULE]` be `matches[2]`
weave_arg[MODULE] = matches[2];
// If there were additional arguments ...
if ((args = matches[4]) !== UNDEFINED) {
// .. parse them using `getargs`, `.map` the values with `$map` and push to `weave_arg`
ARRAY_PUSH.apply(weave_arg, getargs.call(args).map($map));
}
// Let `weave_arg[DEFERRED]` be `when.defer()`
// Push `weave_arg[DEFERRED].promise` on `$weft`
ARRAY_PUSH.call($weft, (weave_arg[DEFERRED] = when.defer()).promise);
// Push `constructor_args` on `weave_arg`
ARRAY_PUSH.apply(weave_arg, constructor_args);
}
// Start async promise chain
return when
// Require, instantiate and start
.map(weave_args, function (widget_args) {
// Let `deferred` be `widget_args[DEFERRED]`
var deferred = widget_args[DEFERRED];
// Extract `resolve`, `reject` and `promise` from `deferred`
var resolve = deferred.resolve;
var reject = deferred.reject;
// Require `weave_arg[MODULE]`
parentRequire([ widget_args[MODULE] ], function (Widget) {
var $deferred;
// Create widget instance
var widget = Widget.apply(Widget, widget_args);
// TroopJS <= 1.x (detect presence of ComposeJS)
if (widget.constructor._getBases) {
// Let `$deferred` be `$.Deferred()`
$deferred = $.Deferred();
// Get trusted promise
when($deferred)
// Yield
.yield(widget)
// Link
.then(resolve, reject);
// Start widget
widget.start($deferred);
}
// TroopJS >= 2.x
else {
// Start widget
start.call(widget)
// Yield
.yield(widget)
// Link
.then(resolve, reject);
}
}, reject);
// Return `deferred.promise`
return deferred.promise;
})
// Update `ATTR_WOVEN`
.tap(function (widgets) {
// Bail fast if no widgets were woven
if (widgets[LENGTH] === 0) {
return;
}
// Map `Widget[]` to `String[]`
var woven = widgets.map(function (widget) {
return widget.toString();
});
// Update `$element` attribute `ATTR_WOVEN`
$element.attr(ATTR_WOVEN, function (index, attr) {
// Split `attr` and concat with `woven`
var values = (attr === UNDEFINED ? ARRAY_PROTO : attr.split(RE_SEPARATOR)).concat(woven);
// If `values[LENGTH]` is not `0` ...
return values[LENGTH] !== 0
// ... return `values.join(" ")`
? values.join(" ")
// ... otherwise return `NULL` to remove the attribute
: NULL;
});
});
}
/**
* Instantiate all {@link widget.component widgets} specified in the `data-weave` attribute
* of this element, and to signal the widget for start with the arguments.
*
* The weaving will result in:
*
* - Updates the `data-woven` attribute with the created widget instances names.
* - The `$weft` data property will reference the widget instances.
*
* @localdoc
*
* It also lives as a jquery plugin as {@link $#method-weave}.
*
* **Note:** It's not commonly to use this method directly, use instead {@link $#method-weave jQuery.fn.weave}.
*
* // Create element for weaving
* var $el = $('<div data-weave="my/widget(option)"></div>')
* // Populate `data`
* .data("option",{"foo":"bar"})
* // Instantiate the widget defined in "my/widget" module, with one param read from the element's custom data.
* .weave();
*
* @method constructor
* @param {...*} [args] Arguments that will be passed to the {@link core.component.signal.start start} signal
* @return {Promise} Promise for the completion of weaving all widgets.
*/
return function () {
// Let `constructor_args` be `arguments`
var constructor_args = arguments;
// Wait for map (sync) and weave (async)
return when.all(ARRAY_MAP.call(this, function (element) {
// Bless `$element` with `$`
var $element = $(element);
// Get ATTR_WEAVE attribute or default to `""`
var weave_attr = $element.attr(ATTR_WEAVE) || "";
// Make sure to remove ATTR_WEAVE asap in case someone else tries to `weave` again
$element.removeAttr(ATTR_WEAVE);
// Attempt weave
return $weave.call($element, weave_attr, constructor_args);
}));
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-widget/unweave',[
"./config",
"troopjs-core/component/signal/finalize",
"when/when",
"jquery"
], function (config, finalize, when, $) {
/**
* @class widget.unweave
* @mixin widget.config
* @mixin Function
* @static
*/
var UNDEFINED;
var NULL = null;
var ARRAY_PROTO = Array.prototype;
var ARRAY_MAP = ARRAY_PROTO.map;
var LENGTH = "length";
var $WEFT = config.widget.$weft;
var ATTR_WOVEN = config.widget.woven;
var ATTR_UNWEAVE = config.widget.unweave;
var RE_SEPARATOR = /[\s,]+/;
/**
* Unweaves `$element`
* @param {String} unweave_attr
* @param {Array} finalize_args
* @return {Promise}
* @ignore
*/
function $unweave (unweave_attr, finalize_args) {
/*eslint consistent-this:0*/
// Let `$element` be `this`
var $element = this;
// Get all data from `$element`
var $data = $element.data();
// Let `$weft` be `$data[$WEFT]` or `$data[$WEFT] = []`
var $weft = $data.hasOwnProperty($WEFT)
? $data[$WEFT]
: $data[$WEFT] = [];
// Scope `unweave_re` locally since we use the `g` flag
var unweave_re = /[\s,]*([\w\/\.\-]+)(?:@(\d+))?/g;
var unweave_res = [];
var unweave_res_length = 0;
var matches;
// Iterate unweave_attr (while unweave_re matches)
// matches[1] : widget name - "widget/name"
// matches[2] : widget instance id - "123"
while ((matches = unweave_re.exec(unweave_attr)) !== NULL) {
unweave_res[unweave_res_length++] = "^" + matches[1] + "@" + (matches[2] || "\\d+") + "$";
}
// Redefine `unweave_re` as a combined regexp
unweave_re = new RegExp(unweave_res.join("|"));
// Start async promise chain
return when
// Filter $weft
.filter($weft, function (widget, index) {
// Bail fast if we don't want to unweave
if (!unweave_re.test(widget.toString())) {
return false;
}
// Let `deferred` be `when.defer()`
var deferred = when.defer();
// Extract `resolve`, `reject` from `deferred`
var resolve = deferred.resolve;
var reject = deferred.reject;
// Let `$weft[index]` be `deferred.promise`
// Let `promise` be `$weft[index]`
var promise = $weft[index] = deferred.promise;
var $deferred;
// TroopJS <= 1.x
if (widget.trigger) {
// Let `$deferred` be `$.Deferred()`
$deferred = $.Deferred();
// Get trusted promise
when($deferred)
// Yield
.yield(widget)
// Link
.then(resolve, reject);
// Stop widget
widget.stop($deferred);
}
// TroopJS >= 2.x
else {
// Finalize widget
finalize.apply(widget, finalize_args)
// Yield
.yield(widget)
// Link
.then(resolve, reject);
}
return promise
// Make sure to remove the promise from `$weft`
.tap(function () {
delete $weft[index];
})
.yield(true);
})
.tap(function (widgets) {
// Bail fast if no widgets were unwoven
if (widgets[LENGTH] === 0) {
return;
}
// Let `unwoven` be a combined regexp of unwoven `widget.toString()`
var unwoven = new RegExp(
widgets
.map(function (widget) {
return "^" + widget.toString() + "$";
})
.join("|")
);
/**
* Filters values using `unwoven`
* @param {String} value
* @return {boolean}
* @ignore
*/
function filter (value) {
return !unwoven.test(value);
}
// Update `$element` attribute `ATTR_WOVEN`
$element.attr(ATTR_WOVEN, function (index, attr) {
// Split `attr` and filter with `filter`
var values = (attr === UNDEFINED ? ARRAY_PROTO : attr.split(RE_SEPARATOR)).filter(filter);
// If `values[LENGTH]` is not `0` ...
return values[LENGTH] !== 0
// ... return `values.join(" ")`
? values.join(" ")
// ... otherwise return `NULL` to remove the attribute
: NULL;
});
});
}
/**
* Destroy all {@link widget.component widget} instances living on this element, that are created
* by {@link widget.weave}, it is also to clean up the attributes
* and data references to the previously instantiated widgets.
*
* @localdoc
*
* It also lives as a jquery plugin as {@link $#method-unweave}.
*
* @method constructor
* @param {...*} [args] Arguments that will be passed to the {@link core.component.signal.finalize finalize} signal
* @return {Promise} Promise to the completion of unweaving all woven widgets.
*/
return function () {
// Let `finalize_args` be `arguments`
var finalize_args = arguments;
// Wait for map (sync) and weave (async)
return when.all(ARRAY_MAP.call(this, function (element) {
// Bless `$element` with `$`
var $element = $(element);
// Get ATTR_WEAVE attribute or default to `""`
var unweave_attr = $element.attr(ATTR_UNWEAVE) || "";
// Make sure to remove ATTR_UNWEAVE asap in case someone else tries to `unweave` again
$element.removeAttr(ATTR_UNWEAVE);
// Attempt weave
return $unweave.call($element, unweave_attr, finalize_args);
}));
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-widget/woven',[
"./config",
"when/when",
"jquery"
], function (config, when, $) {
/**
* @class widget.woven
* @mixin widget.config
* @mixin Function
* @static
*/
var NULL = null;
var ARRAY_MAP = Array.prototype.map;
var LENGTH = "length";
var $WEFT = config.widget.$weft;
var RE_ANY = /.*/;
var RE_WIDGET = /([\w\/\.\-]+)(?:@(\d+))?/;
/**
* Retrieve all or specific {@link widget.component widget} instances living on this element, that are
* created by {@link widget.weave}.
*
* It also lives as a jquery plugin as {@link $#method-woven}.
* @method constructor
* @param {...String} [selector] One or more widget selectors to narrow down the returned ones.
*
* * (empty string) retrieves all woven widgets
* * `module/name` retrieves widgets matching module name
* * `module/name@instance` retrieves widgets matching both module name and instance id
* @return {Promise} Promise to the completion of retrieving the woven widgets array.
*/
return function () {
var woven_re = arguments[LENGTH] > 0
? new RegExp(
ARRAY_MAP
.call(arguments, function (arg) {
var matches;
// matches[1] : widget name - "widget/name"
// matches[2] : widget instance id - "123"
return (matches = RE_WIDGET.exec(arg)) !== NULL
? "^" + matches[1] + "@" + (matches[2] || "\\d+") + "$"
: NULL;
})
.filter(function (arg) {
return arg !== NULL;
})
.join("|")
)
: RE_ANY;
return when.all(ARRAY_MAP.call(this, function (element) {
return when.filter($.data(element, $WEFT) || false, function (widget) {
return woven_re.test(widget);
});
}));
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-widget/component',[
"troopjs-dom/component",
"./config",
"./weave",
"./unweave",
"./woven"
], function (Component, config, weave, unweave, woven) {
/**
* @class widget.component
* @extend dom.component
* @alias feature.component
* @mixin widget.config
* @localdoc Adds functionality for working with the loom
*/
var ARRAY_SLICE = Array.prototype.slice;
var $ELEMENT = "$element";
var SELECTOR_WEAVE = "[" + config.widget.weave + "]";
var SELECTOR_WOVEN = "[" + config.widget.woven + "]";
var FINALIZE = config.phase.finalize;
/**
* @method constructor
* @inheritdoc
*/
return Component.extend({
"displayName": "widget/component",
/**
* Handles component render
* @handler
* @inheritdoc
* @localdoc Calls {@link #method-weave} to ensure newly rendered html is woven
*/
"sig/render": function ($target) {
return weave.apply($target.find(SELECTOR_WEAVE).addBack(SELECTOR_WEAVE), ARRAY_SLICE.call(arguments, 1));
},
/**
* @handler
* @inheritdoc
* @localdoc Calls {@link #method-unweave} to ensure this element is unwoven
*/
"dom/destroy": function () {
if (this.phase !== FINALIZE) {
unweave.call(this[$ELEMENT]);
}
},
/**
* @method
* @inheritdoc widget.weave#constructor
*/
"weave": function () {
return weave.apply(this[$ELEMENT].find(SELECTOR_WEAVE), ARRAY_SLICE.call(arguments));
},
/**
* @inheritdoc widget.unweave#constructor
*/
"unweave": function () {
return unweave.apply(this[$ELEMENT].find(SELECTOR_WOVEN), arguments);
},
/**
* @inheritdoc widget.woven#constructor
*/
"woven": function () {
return woven.apply(this[$ELEMENT].find(SELECTOR_WOVEN), arguments);
}
});
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-hub/config',[
"troopjs-core/config",
"module",
"mu-merge/main"
], function (config, module, merge) {
/**
* @class hub.config.emitter
* @extends core.config.emitter
* @private
*/
var EMITTER = {
/**
* Property name for `memory`
*/
"memory": "memory"
};
/**
* HUB component configuration
* @class hub.config
* @extends core.config
* @private
* @alias feature.config
*/
return merge.call({}, config, {
/**
* @cfg {hub.config.emitter}
* @inheritdoc
* @protected
*/
"emitter": EMITTER
}, module.config());
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-hub/executor',[
"./config",
"when/when"
], function (config, when) {
/**
* @class hub.executor
* @mixin Function
* @private
* @static
* @alias feature.executor
*/
var UNDEFINED;
var OBJECT_TOSTRING = Object.prototype.toString;
var ARRAY_SLICE = Array.prototype.slice;
var TOSTRING_ARGUMENTS = "[object Arguments]";
var TOSTRING_ARRAY = "[object Array]";
var SKIP = config.phase.skip;
var SCOPE = config.emitter.scope;
var CALLBACK = config.emitter.callback;
var HEAD = config.emitter.head;
var NEXT = config.emitter.next;
var MEMORY = config.emitter.memory;
var PHASE = "phase";
/**
* @method constructor
* @inheritdoc core.emitter.executor#constructor
* @localdoc
* - Skips handlers who's scope.{@link core.component.emitter#property-phase phase} matches {@link core.config.phase#skip}.
* - Executes handlers passing each handler the result from the previous.
* - If a handler returns `undefined` the result from the previous is used.
* - When all handlers are completed the end result is memorized on `handlers`
*
* @return {Promise} Promise for `[*]`
*/
return function (event, handlers, args) {
var _handlers = [];
var _handlersCount = 0;
var scope = event[SCOPE];
var callback = event[CALLBACK];
var handler;
// Iterate handlers
for (handler = handlers[HEAD]; handler !== UNDEFINED; handler = handler[NEXT]) {
if (callback && handler[CALLBACK] !== callback) {
continue;
}
if (scope && handler[SCOPE] !== scope) {
continue;
}
_handlers[_handlersCount++] = handler;
}
return when
// Reduce `_handlers`
.reduce(_handlers, function (current, _handler) {
// Let `_scope` be `handler[SCOPE]`
var _scope = _handler[SCOPE];
// Return early if `_scope[PHASE]` matches a blocked phase
if (_scope !== UNDEFINED && SKIP.test(_scope[PHASE])) {
return current;
}
// Run `handler` passing `args`
// Pass to `when` to (potentially) update `result`
return when(_handler.handle(current), function (result) {
// If `result` is `UNDEFINED` ...
if (result === UNDEFINED) {
// ... return `current` ...
return current;
}
// Detect `result` type
switch (OBJECT_TOSTRING.call(result)) {
// `arguments` should be converted to an array
case TOSTRING_ARGUMENTS:
return ARRAY_SLICE.call(result);
// `array` can be passed as-is
case TOSTRING_ARRAY:
return result;
// everything else should be wrapped in an array
default:
return [ result ];
}
});
}, args)
// Memorize
.tap(function (result) {
// Store `result` in `handlers[MEMORY]`
handlers[MEMORY] = result;
});
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-hub/emitter',[
"troopjs-core/emitter/composition",
"./config",
"./executor"
], function (Emitter, config, executor) {
/**
* A static version of {@link core.emitter.composition} with memorization.
*
* ## Memorized emitting
*
* A emitter event will memorize the "current" value of each event. Each executor may have it's own interpretation
* of what "current" means.
*
* @class hub.emitter
* @extend core.emitter.composition
* @mixin hub.config
* @inheritdoc
* @singleton
*/
/**
* @method create
* @static
* @hide
*/
/**
* @method extend
* @static
* @hide
*/
/**
* @method constructor
* @hide
*/
var UNDEFINED;
var MEMORY = config.emitter.memory;
var HANDLERS = config.emitter.handlers;
var EXECUTOR = config.emitter.executor;
return Emitter.create(function (key, value) {
var me = this;
me[key] = value;
return me;
}.call({
"displayName": "hub/emitter",
/**
* Returns value in handlers MEMORY
* @param {String} type event type to peek at
* @param {*} [value] Value to use _only_ if no memory has been recorder
* @return {*} Value in MEMORY
*/
"peek": function (type, value) {
var handlers;
return (handlers = this[HANDLERS][type]) === UNDEFINED || !handlers.hasOwnProperty(MEMORY)
? value
: handlers[MEMORY];
}
}, EXECUTOR, executor));
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-widget/application',[
"./component",
"troopjs-core/component/signal/initialize",
"troopjs-core/component/signal/start",
"troopjs-core/component/signal/stop",
"troopjs-core/component/signal/finalize",
"troopjs-hub/emitter",
"when/when"
], function (Widget, initialize, start, stop, finalize, hub, when) {
/**
* The application widget serves as a container for all troop components that bootstrap the page.
* @class widget.application
* @extend widget.component
* @alias widget.application
*/
/**
* Application start event
* @event hub/application/start
* @param {widget.application} application The started application
*/
/**
* Application stop event
* @event hub/application/stop
* @param {widget.application} application The stopped application
*/
var ARRAY_SLICE = Array.prototype.slice;
var COMPONENTS = "components";
/**
* @method constructor
* @inheritdoc
* @param {jQuery|HTMLElement} $element The element that this widget should be attached to
* @param {String} displayName A friendly name for this widget
* @param {...core.component.emitter} component List of components to start before starting the application.
*/
return Widget.extend(function () {
/**
* Application components
* @private
* @readonly
* @property {core.component.emitter[]} components
*/
this[COMPONENTS] = ARRAY_SLICE.call(arguments, 2);
}, {
"displayName": "widget/application",
/**
* @handler
* @localdoc Initialize all registered components (widgets and services) that are passed in from the {@link #method-constructor}.
* @inheritdoc
*/
"sig/initialize": function () {
var args = arguments;
return when.map(this[COMPONENTS], function (component) {
return initialize.apply(component, args);
});
},
/**
* @handler
* @localdoc weave this and all widgets that are within this element.
* @fires hub/application/start
* @inheritdoc
*/
"sig/start": function () {
var me = this;
var args = arguments;
return when
.map(me[COMPONENTS], function (component) {
return start.apply(component, args);
})
.then(function () {
return me.weave.apply(me, args);
})
.then(function () {
return hub.emit("application/start", me);
});
},
/**
* @handler
* @localdoc stop this and all woven widgets that are within this element.
* @fires hub/application/stop
* @inheritdoc
*/
"sig/stop": function () {
var me = this;
var args = arguments;
return me
.unweave.apply(me, args).then(function () {
return when.map(me[COMPONENTS], function (child) {
return stop.apply(child, args);
});
})
.then(function () {
return hub.emit("application/stop", me);
});
},
/**
* @handler
* @localdoc finalize all registered components (widgets and services) that are registered from the {@link #method-constructor}.
* @inheritdoc
*/
"sig/finalize": function () {
var args = arguments;
return when.map(this[COMPONENTS], function (component) {
return finalize.apply(component, args);
});
},
/**
* Start the component life-cycle, sends out {@link #event-sig/initialize} and then {@link #event-sig/start}.
* @param {...*} [args] arguments
* @return {Promise}
* @fires sig/initialize
* @fires sig/start
*/
"start": start,
/**
* Stops the component life-cycle, sends out {@link #event-sig/stop} and then {@link #event-sig/finalize}.
* @param {...*} [args] arguments
* @return {Promise}
* @fires sig/stop
* @fires sig/finalize
*/
"stop": finalize
});
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-widget/plugin',[
"jquery",
"when/when",
"./config",
"./weave",
"./unweave",
"./woven"
], function ($, when, config, weave, unweave, woven) {
/**
* Extends {@link jQuery} with:
*
* - {@link $#property-woven} property
* - {@link $#method-weave}, {@link $#method-unweave} and {@link $#method-woven} methods
*
* @class widget.plugin
* @static
* @alias plugin.jquery
*/
var UNDEFINED;
var $FN = $.fn;
var $EXPR = $.expr;
var WEAVE = "weave";
var UNWEAVE = "unweave";
var WOVEN = "woven";
var ATTR_WOVEN = config.widget.woven;
/**
* Tests if element has a data-woven attribute
* @param element to test
* @return {boolean}
* @ignore
*/
function hasDataWovenAttr (element) {
return $(element).attr(ATTR_WOVEN) !== UNDEFINED;
}
/**
* @class $
*/
/**
* jQuery `:woven` expression
* @property woven
*/
$EXPR[":"][WOVEN] = $EXPR.createPseudo(function (widgets) {
// If we don't have widgets to test, quick return optimized expression
if (widgets === UNDEFINED) {
return hasDataWovenAttr;
}
// Scope `woven_re` locally since we use the `g` flag
var woven_re = /[\s,]*([\w\d_\/\.\-]+)(?:@(\d+))?/g;
var woven_res = [];
var woven_res_length = 0;
var matches;
// Iterate `widgets` (while woven_re matches)
// matches[1] : widget name - "widget/name"
// matches[2] : widget instance id - "123"
while ((matches = woven_re.exec(widgets)) !== null) {
woven_res[woven_res_length++] = "(?:^|[\\s,]+)" + matches[1] + "@" + (matches[2] || "\\d+") + "($|[\\s,]+)";
}
// Redefine `woven_re` as a combined regexp
woven_re = new RegExp(woven_res.join("|"));
// Return expression
return function (element) {
var attr_woven = $.attr(element, ATTR_WOVEN);
// Check that attr_woven is not UNDEFINED, and that widgets test against a processed attr_woven
return attr_woven !== UNDEFINED && woven_re.test(attr_woven);
};
});
/**
* @method weave
* @inheritdoc widget.weave#constructor
*/
$FN[WEAVE] = weave;
/**
* @method unweave
* @inheritdoc widget.unweave#constructor
*/
$FN[UNWEAVE] = unweave;
/**
* @method woven
* @inheritdoc widget.woven#constructor
*/
$FN[WOVEN] = woven;
});
define(['troopjs-widget/version'], function (version) {
return version;
});
\ No newline at end of file
/*!
* ____ . ____ ____ ____ ____.
* \ (/_\___\ (__\ (__\ (\___\ (/
* / ._/ ( . _ \ . / . / . _ \_
* _/ ___/ /____ / \_ / \_ ____/
* \ _/ \____/ \____________/ /
* \_t:_____r:_______o:____o:___p:/ [ troopjs - 3.0.0-rc+726cd7f ]
*
* @license http://troopjs.mit-license.org/ © Mikael Karon, Garry Yao, Eyal Arubas
*/
define('troopjs/version',[], { 'toString': function () { return "3.0.0-rc+726cd7f"; } });
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-compose/decorator',[],function () {
/**
* Decorator provides customized way to add properties/methods to object created by {@link compose.factory}.
* @class compose.decorator
* @protected
*/
/**
* Creates a new decorator
* @method constructor
* @param {Function} decorate Function that defines how to override the original one.
* @param {Object} decorate.descriptor The object descriptor that is the current property.
* @param {String} decorate.name The property name.
* @param {Object} decorate.descriptors List of all property descriptors of the host object.
*/
return function (decorate) {
// Define properties
Object.defineProperties(this, {
/**
* Function that decides what decoration is to make.
* @method decorate
* @param {Object} descriptor The object descriptor that is the current property.
* @param {String} name The property name.
* @param {Object} descriptors List of all property descriptors of the host object.
*/
"decorate": {
"value": decorate
}
});
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-compose/decorator/after',[ "../decorator" ], function (Decorator) {
/**
* @class compose.decorator.after
* @static
* @alias feature.decorator
*/
var UNDEFINED;
var VALUE = "value";
/**
* Create a decorator method that is to add code that will be executed after the original method.
* @method constructor
* @param {Function} func The decorator function which receives the arguments of the original, it's return value (if
* not undefined) will be the used as the new return value.
* @return {compose.decorator}
*/
return function (func) {
return new Decorator(function (descriptor) {
var previous = descriptor[VALUE];
descriptor[VALUE] = previous
? function () {
var me = this;
var retval = previous.apply(me, arguments);
var newRet = func.apply(me, arguments);
return newRet !== UNDEFINED ? newRet : retval;
}
: func;
return descriptor;
});
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-compose/decorator/around',[ "../decorator" ], function (Decorator) {
/**
* @class compose.decorator.around
* @static
* @alias feature.decorator
*/
var VALUE = "value";
var NOP = function () {};
/**
* Create a decorator that is to override an existing method.
* @method constructor
* @param {Function} func The decorator function which receives the original function as parameter and is supposed to
* return a function that is to replace the original.
* @return {compose.decorator}
*/
return function (func) {
return new Decorator(function (descriptor) {
descriptor[VALUE] = func(descriptor[VALUE] || NOP);
return descriptor;
});
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-compose/decorator/before',[ "../decorator" ], function (Decorator) {
/**
* @class compose.decorator.before
* @static
* @alias feature.decorator
*/
var UNDEFINED;
var VALUE = "value";
/**
* Create a decorator method that is to add code that will be executed before the original method.
* @method constructor
* @param {Function} func The decorator function which receives the same arguments as with the original, it's return
* value (if not undefined) will be send as the arguments of original function.
* @return {compose.decorator}
*/
return function (func) {
return new Decorator(function (descriptor) {
var next = descriptor[VALUE];
descriptor[VALUE] = next
? function () {
var me = this;
var retval = func.apply(me, arguments);
return next.apply(me, retval !== UNDEFINED ? retval : arguments);
}
: func;
return descriptor;
});
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-compose/decorator/extend',[
"../decorator",
"mu-merge/main"
], function (Decorator, merge) {
/**
* @class compose.decorator.extend
* @static
* @alias feature.decorator
*/
var UNDEFINED;
var VALUE = "value";
var ARRAY_CONCAT = Array.prototype.concat;
/**
* Create a decorator that is to augment an existing Object property.
* @method constructor
* @param {Function|Object...} ext One or more objects to merge into this property, or a function that returns a new object to be used.
* @return {compose.decorator}
*/
return function (ext) {
var args = arguments;
return new Decorator(function (descriptor, name, descriptors) {
var previous = descriptors[name][VALUE];
var val;
if (typeof ext === "function") {
val = ext(previous);
}
else if (previous !== UNDEFINED) {
val = merge.apply({}, ARRAY_CONCAT.apply([ previous ], args));
}
descriptor[VALUE] = val;
return descriptor;
});
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-compose/decorator/from',[ "../decorator" ], function (Decorator) {
/**
* @class compose.decorator.from
* @static
* @alias feature.decorator
*/
var UNDEFINED;
var VALUE = "value";
var PROTOTYPE = "prototype";
/**
* Create a decorator that is to lend from a particular property from this own or the other factory.
* @method constructor
* @param {Function} [which] The other class from which to borrow the method, otherwise to borrow from the host class.
* @param {String} [prop] The property name to borrow from, otherwise to borrow the same property name.
* @return {compose.decorator}
*/
return function (which, prop) {
// Shifting arguments.
if (typeof which === "string") {
prop = which;
which = UNDEFINED;
}
return new Decorator(function (descriptor, name, descriptors) {
// To override a specified property, otherwise simply this property.
name = prop || name;
// Property is from the the other's prototype, otherwise from own descriptor.
descriptor[VALUE] = which
? which[PROTOTYPE][name]
: descriptors[name][VALUE];
return descriptor;
});
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-compose/config',[
"module",
"mu-merge/main"
], function (module, merge) {
/**
* Pragma interface.
* @class compose.config.pragma
* @interface
* @private
*/
/**
* @property {RegExp} pattern Matching pattern
*/
/**
* @property {String|Function} replace Replacement String or function
*/
/**
* Provides configuration for the {@link compose.factory}
* @class compose.config
* @private
* @alias feature.config
*/
return merge.call({
/**
* @cfg {compose.config.pragma[]}
* Pragmas used to rewrite methods before processing
* @protected
*/
"pragmas": [],
/**
* @cfg {RegExp}
* Regular Expression used parse 'specials'.
* ````
* <special>/<type>[(<arguments>)]
* ````
* @protected
*/
"specials": /^([^\/]+)\/(.+?)(?:\((.*)\))?$/
}, module.config());
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-core/config',[
"module",
"troopjs-compose/config",
"mu-emitter/config",
"mu-merge/main"
], function (module, config, emitterConfig, merge) {
/**
* @class core.config.emitter
* @enum {String}
* @private
*/
/**
* @property handlers Property name for `handlers`
*/
/**
* @property emitter Property name for `emitter`
*/
/**
* @property type Property name for `type`
*/
/**
* @property callback Property name for `callback`
*/
/**
* @property data Property name for `data`
*/
/**
* @property scope Property name for `scope`
*/
/**
* @property executor Property name for `executor`
*/
/**
* @property head Property name for `head`
*/
/**
* @property tail Property name for `tail`
*/
/**
* @property next Property name for `next`
*/
/**
* @property count Property name for `count`
*/
/**
* @property limit Property name for `limit`
*/
/**
* @property on Property name for `on`
*/
/**
* @property off Property name for `off`
*/
/**
* @class core.config.phase
* @enum
* @private
*/
var PHASE = {
/**
* Protected phases
*/
"skip": /^(?:initi|fin)alized?$/,
/**
* Phase while component is initializing.
*/
"initialize": "initialize",
/**
* Phase when component is initialized.
*/
"initialized": "initialized",
/**
* Phase while component is starting.
*/
"start": "start",
/**
* Phase when component is started.
*/
"started": "started",
/**
* Phase while component is stopping.
*/
"stop": "stop",
/**
* Phase when component is stopped.
*/
"stopped": "stopped",
/**
* Phase while component is finalizing.
*/
"finalize": "finalize",
/**
* Phase when component is finalized.
*/
"finalized": "finalized"
};
/**
* @class core.config.signal
* @enum {String}
* @private
*/
var SIGNAL = {
/**
* Signal emitted first time an event handler is added.
*/
"setup": "sig/setup",
/**
* Signal emitted before each time an event handler is added.
*/
"add": "sig/add",
/**
* Signal emitted each time an event handler is added.
*/
"added": "sig/added",
/**
* Signal emitted before each time an event handler is removed.
*/
"remove": "sig/remove",
/**
* Signal emitted each time an event handler is removed.
*/
"removed": "sig/removed",
/**
* Signal emitted last time an event handler is removed.
*/
"teardown": "sig/teardown",
/**
* Signal emitted when component initializes.
*/
"initialize": "sig/initialize",
/**
* Signal emitted when component starts.
*/
"start": "sig/start",
/**
* Signal emitted when component stops.
*/
"stop": "sig/stop",
/**
* Signal emitted when component finalizes.
*/
"finalize": "sig/finalize",
/**
* Signal emitted during registration.
*/
"register": "sig/register",
/**
* Signal emitted during un-registeration.
*/
"unregister": "sig/unregister",
/**
* Signal emitted when component starts a task.
*/
"task": "sig/task"
};
/**
* Component configuration
* @class core.config
* @extends compose.config
* @private
* @alias feature.config
*/
return merge.call({}, config, {
/**
* Component signals
* @cfg {core.config.signal}
* @protected
*/
"signal": SIGNAL,
/**
* Component phases
* @cfg {core.config.phase}
* @protected
*/
"phase": PHASE,
/**
* Emitter properties
* @cfg {core.config.emitter}
* @protected
*/
"emitter": emitterConfig
}, module.config());
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-core/component/signal/initialize',[
"../../config",
"when/when"
], function (config, when) {
var UNDEFINED;
var ARRAY_PUSH = Array.prototype.push;
var PHASE = "phase";
var INITIALIZE = config.phase.initialize;
var INITIALIZED = config.phase.initialized;
var SIG_INITIALIZE = config.signal.initialize;
/**
* @class core.component.signal.initialize
* @implement core.component.signal
* @mixin core.config
* @static
* @alias feature.signal
* @private
*/
/**
* @method constructor
* @inheritdoc
* @localdoc Transitions the component {@link core.component.emitter#property-phase} to `initialized`
*/
return function () {
var me = this;
var args = arguments;
return when(me[PHASE], function (phase) {
var _args;
if (phase === UNDEFINED) {
// Let `me[PHASE]` be `INITIALIZE`
me[PHASE] = INITIALIZE;
// Let `_args` be `[ SIG_INITIALIZE ]`
// Push `args` on `_args`
ARRAY_PUSH.apply(_args = [ SIG_INITIALIZE ], args);
return me
.emit.apply(me, _args)
.then(function () {
/*eslint no-return-assign:0*/
// Let `me[PHASE]` be `INITIALIZED`
return me[PHASE] = INITIALIZED;
});
}
else {
return phase;
}
});
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-core/component/signal/start',[
"./initialize",
"../../config",
"when/when"
], function (initialize, config, when) {
var ARRAY_PUSH = Array.prototype.push;
var PHASE = "phase";
var INITIALIZED = config.phase.initialized;
var START = config.phase.start;
var STARTED = config.phase.started;
var SIG_START = config.signal.start;
/**
* @class core.component.signal.start
* @implement core.component.signal
* @mixin core.config
* @static
* @alias feature.signal
* @private
*/
/**
* @method constructor
* @inheritdoc
* @localdoc Transitions the component {@link core.component.emitter#property-phase} to `started`
*/
return function () {
var me = this;
var args = arguments;
return when(initialize.apply(me, args), function (phase) {
var _args;
if (phase === INITIALIZED) {
// Let `me[PHASE]` be `START`
me[PHASE] = START;
// Let `_args` be `[ SIG_START ]`
// Push `args` on `_args`
ARRAY_PUSH.apply(_args = [ SIG_START ], args);
return me
.emit.apply(me, _args)
.then(function () {
/*eslint no-return-assign:0*/
// Let `me[PHASE]` be `STARTED`
return me[PHASE] = STARTED;
});
}
else {
return phase;
}
});
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-core/component/signal/stop',[
"./start",
"../../config",
"when/when"
], function (start, config, when) {
var ARRAY_PUSH = Array.prototype.push;
var PHASE = "phase";
var STARTED = config.phase.started;
var STOP = config.phase.stop;
var STOPPED = config.phase.stopped;
var SIG_STOP = config.signal.stop;
/**
* @class core.component.signal.stop
* @implement core.component.signal
* @mixin core.config
* @static
* @alias feature.signal
* @private
*/
/**
* @method constructor
* @inheritdoc
* @localdoc Transitions the component {@link core.component.emitter#property-phase} to `stopped`
*/
return function () {
var me = this;
var args = arguments;
return when(start.apply(me, args), function (phase) {
var _args;
if (phase === STARTED) {
// Let `me[PHASE]` be `"stop"`
me[PHASE] = STOP;
// Let `_args` be `[ SIG_STOP ]`
// Push `args` on `_args`
ARRAY_PUSH.apply(_args = [ SIG_STOP ], args);
return me
.emit.apply(me, _args)
.then(function () {
/*eslint no-return-assign:0*/
// Let `me[PHASE]` be `STOPPED`
return me[PHASE] = STOPPED;
});
}
else {
return phase;
}
});
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-core/component/signal/finalize',[
"./stop",
"../../config",
"when/when"
], function (stop, config, when) {
var ARRAY_PUSH = Array.prototype.push;
var PHASE = "phase";
var STOPPED = config.phase.stopped;
var FINALIZE = config.phase.finalize;
var FINALIZED = config.phase.finalized;
var SIG_FINALIZE = config.signal.finalize;
/**
* @class core.component.signal.finalize
* @implement core.component.signal
* @mixin core.config
* @static
* @alias feature.signal
* @private
*/
/**
* @method constructor
* @inheritdoc
* @localdoc Transitions the component {@link core.component.emitter#property-phase} to `finalized`
*/
return function () {
var me = this;
var args = arguments;
return when(stop.apply(me, args), function (phase) {
var _args;
if (phase === STOPPED) {
// Let `me[PHASE]` be `FINALIZE`
me[PHASE] = FINALIZE;
// Let `_args` be `[ SIG_FINALIZE ]`
// Push `args` on `_args`
ARRAY_PUSH.apply(_args = [ SIG_FINALIZE ], args);
return me
.emit.apply(me, _args)
.then(function () {
/*eslint no-return-assign:0*/
// Let `me[PHASE]` be `FINALIZED`
return me[PHASE] = FINALIZED;
});
}
else {
return phase;
}
});
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-compose/factory',[
"./config",
"./decorator",
"mu-getargs/main"
], function (config, Decorator, getargs) {
/**
* The factory module establishes the fundamental object composition in TroopJS:
*
* - **First-class mixin** based on prototype, that supports deterministic multiple inheritance that:
* - Eliminating the frustrating issues from multi-tiered, single-rooted ancestry;
* - Avoid occasionally unexpected modification from prototype chain, from the prototype-based inheritance;
* - Reduced the function creation overhead in classical inheritance pattern;
* - **Advice decorator** for method overriding without the need for super call;
* - **Declarative** "special" functions that never overrides parent ones reserved for event emission
*
* Basically Factory takes objects or constructors as arguments and returns a new constructor, the arguments are
* composed from left to right, later arguments taken precedence (overriding) former arguments,
* and any functions be executed on construction from left to right.
*
* // Define the constructor.
* var MyClass = Factory(function() {
* // initialize the object...
* this.baz = "quz";
* },
* {
* foo: "bar",
* do: function(){
* return "work";
* },
*
* // a special handler for "signal" type with value "foo".
* "signal/foo": function() {}
*
* });
*
* var MyBehavior = Factory({
* somethingElse: function(){}
* });
*
* // SubClass extends from MyClass and mixin MyBehavior
* var SubClass = MyClass.extend(function() {
* // initialize the object...
* },
*
* MyBehavior,
* {
* // Overwrite parent.
* foo: "baz",
*
* // Override parent with after call.
* do: Factory.after(function(retval) {
* return retval + "," + "play";
* }),
*
* move: function(){}
* });
*
* // Instantiate the subClass.
* var instance = SubClass.create({
* evenMore: function(){}
* });
*
* // "baz"
* instance.foo;
*
* // "quz"
* instance.baz;
*
* // "work play"
* instance.do();
*
* instance.somethingElse();
* instance.evenMore();
*
* @class compose.factory
* @mixin compose.config
* @static
*/
var UNDEFINED;
var NULL = null;
var PROTOTYPE = "prototype";
var TOSTRING = "toString";
var ARRAY_PROTO = Array[PROTOTYPE];
var ARRAY_PUSH = ARRAY_PROTO.push;
var ARRAY_UNSHIFT = ARRAY_PROTO.unshift;
var ARRAY_SLICE = ARRAY_PROTO.slice;
var OBJECT_TOSTRING = Object[PROTOTYPE][TOSTRING];
var TYPEOF_FUNCTION = "function";
var DISPLAYNAME = "displayName";
var LENGTH = "length";
var EXTEND = "extend";
var CREATE = "create";
var DECORATE = "decorate";
var CONSTRUCTOR = "constructor";
var CONSTRUCTORS = "constructors";
var SPECIALS = "specials";
var GROUP = "group";
var VALUE = "value";
var ARGS = "args";
var TYPE = "type";
var TYPES = "types";
var NAME = "name";
var PRAGMAS = config.pragmas;
var PATTERN = config[SPECIALS];
/**
* Instantiate immediately after extending this constructor from multiple others constructors/objects.
* @method create
* @static
* @param {...(Function|Object)} composition One or more constructors or objects to be mixed in.
* @return {compose.composition} Object instance created out of the composition of constructors and objects.
*/
function create () {
return extend.apply(this, arguments)();
}
function extend () {
var args = [ this ];
ARRAY_PUSH.apply(args, arguments);
return Factory.apply(NULL, args);
}
function unique () {
/*eslint no-labels:0, block-scoped-var:0, no-return-assign:0*/
var me = this;
var o;
var i;
var j;
var k;
var length;
outer: for (i = k = 0, length = me[LENGTH]; i < length; i++) {
o = me[i];
for (j = 0; j < i; j++) {
if (o === me[j]) {
continue outer;
}
}
me[k++] = o;
}
return me[LENGTH] = k;
}
function ConstructorToString () {
var me = this;
var prototype = me[PROTOTYPE];
return DISPLAYNAME in prototype
? prototype[DISPLAYNAME]
: OBJECT_TOSTRING.call(me);
}
/**
* Create a new constructor or to extend an existing one from multiple others constructors/objects.
* @method constructor
* @static
* @param {...(Function|Object)} composition One or more constructors or objects to be mixed in.
* @return {compose.composition} Object class created out of the composition of constructors and objects.
*/
function Factory () {
var special;
var specials = [];
var specialsLength;
var arg;
var args = ARRAY_SLICE.call(arguments);
var argsLength = args[LENGTH];
var constructors = [];
var constructorsLength;
var name;
var nameRaw;
var names;
var namesLength;
var i;
var j;
var k;
var pragma;
var replace;
var groups = specials[TYPES] = [];
var group;
var types;
var type;
var matches;
var value;
var descriptor;
var prototype = {};
var prototypeDescriptors = {};
var constructorDescriptors = {};
// Iterate arguments
for (i = 0; i < argsLength; i++) {
// Get arg
arg = args[i];
// If this is a function we're going to add it as a constructor candidate
if (typeof arg === TYPEOF_FUNCTION) {
// If this is a synthetic constructor then add (child) constructors
if (CONSTRUCTORS in arg) {
ARRAY_PUSH.apply(constructors, arg[CONSTRUCTORS]);
}
// Otherwise add as usual
else {
ARRAY_PUSH.call(constructors, arg);
}
// If we have SPECIALS then unshift arg[SPECIALS] onto specials
if (SPECIALS in arg) {
ARRAY_UNSHIFT.apply(specials, arg[SPECIALS]);
}
// Continue if this is a dead cause
if (arg === arg[PROTOTYPE][CONSTRUCTOR]) {
continue;
}
// Arg is now arg prototype
arg = arg[PROTOTYPE];
}
// Get arg names
names = Object.getOwnPropertyNames(arg);
// Iterate names
for (j = 0, namesLength = names[LENGTH]; j < namesLength; j++) {
// Get name
name = nameRaw = names[j];
// Iterate PRAGMAS
for (k = 0; k < PRAGMAS[LENGTH]; k++) {
// Get pragma
pragma = PRAGMAS[k];
// Process name with pragma, break if replacement occurred
if ((name = name.replace(pragma.pattern, typeof (replace = pragma.replace) === TYPEOF_FUNCTION ? replace.bind(arg) : replace)) !== nameRaw) {
break;
}
}
// Check if this matches a SPECIAL signature
if ((matches = PATTERN.exec(name)) !== NULL) {
// Create special
special = {};
// Set special properties
special[GROUP] = group = matches[1];
// An optional type.
if ((type = matches[2]) !== UNDEFINED) {
special[TYPE] = type;
}
special[NAME] = group + (type ? "/" + type : "");
special[ARGS] = getargs.call(matches[3] || "");
// If the VALUE of the special does not duck-type Function, we should not store it
if (OBJECT_TOSTRING.call(special[VALUE] = arg[nameRaw]) !== "[object Function]") {
continue;
}
// Unshift special onto specials
ARRAY_UNSHIFT.call(specials, special);
}
// Otherwise just add to prototypeDescriptors
else {
// Get descriptor for arg
descriptor = Object.getOwnPropertyDescriptor(arg, nameRaw);
// Get value
value = descriptor[VALUE];
// If value is instanceof Advice, we should re-describe, otherwise just use the original descriptor
prototypeDescriptors[name] = value instanceof Decorator
? value[DECORATE](prototypeDescriptors[name] || {
"enumerable": true,
"configurable": true,
"writable": true
}, name, prototypeDescriptors)
: descriptor;
}
}
}
// Define properties on prototype
Object.defineProperties(prototype, prototypeDescriptors);
// Reduce constructors to unique values
constructorsLength = unique.call(constructors);
// Reduce specials to unique values
specialsLength = unique.call(specials);
// Iterate specials
for (i = 0; i < specialsLength; i++) {
// Get special
special = specials[i];
// Get special properties
group = special[GROUP];
type = special[TYPE];
name = special[NAME];
// Get or create group object
group = group in specials
? specials[group]
: specials[groups[groups[LENGTH]] = group] = [];
// Create an index for each type.
// TODO: In the future we might want to index each nested sub type.
if (type) {
// Get or create types object
types = TYPES in group
? group[TYPES]
: group[TYPES] = [];
// Get or create type object
type = type in group
? group[type]
: group[types[types[LENGTH]] = type] = specials[name] = [];
type[type[LENGTH]] = special;
}
// Store special in group/type
group[group[LENGTH]] = special;
}
function Constructor () {
// Allow to be created either via 'new' or direct invocation
var instance = this instanceof Constructor
? this
: Object.create(prototype);
var _args = arguments;
var _i;
// Set the constructor on instance
Object.defineProperty(instance, CONSTRUCTOR, {
"value": Constructor
});
// Iterate constructors
for (_i = 0; _i < constructorsLength; _i++) {
// Capture result as _args to pass to next constructor
_args = constructors[_i].apply(instance, _args) || _args;
}
return instance;
}
// Add PROTOTYPE to constructorDescriptors
constructorDescriptors[PROTOTYPE] = {
"value": prototype
};
// Add CONSTRUCTORS to constructorDescriptors
constructorDescriptors[CONSTRUCTORS] = {
"value": constructors
};
// Add SPECIALS to constructorDescriptors
constructorDescriptors[SPECIALS] = {
"value": specials
};
constructorDescriptors[TOSTRING] = {
"value": ConstructorToString
};
// Add EXTEND to constructorDescriptors
constructorDescriptors[EXTEND] = {
"value": extend
};
// Add CREATE to constructorDescriptors
constructorDescriptors[CREATE] = {
"value": create
};
// Define prototypeDescriptors on Constructor
Object.defineProperties(Constructor, constructorDescriptors);
// Return Constructor
return Constructor;
}
// Add CREATE to factoryDescriptors
Object.defineProperty(Factory, CREATE, {
"value": function () {
return Factory.apply(NULL, arguments)();
}
});
return Factory;
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-core/composition',[ "troopjs-compose/factory" ], function (Factory) {
var INSTANCE_COUNTER = 0;
var INSTANCE_COUNT = "instanceCount";
/**
* Base composition with instance count.
* @class core.composition
* @implement compose.composition
*/
/**
* Instance counter
* @private
* @readonly
* @property {Number} instanceCount
*/
/**
* @method create
* @static
* @inheritable
* @inheritdoc
* @return {core.composition} Instance of this class
*/
/**
* @method extend
* @static
* @inheritable
* @inheritdoc
* @return {core.composition} The extended subclass
*/
/**
* Creates a new component instance
* @method constructor
*/
return Factory(function () {
this[INSTANCE_COUNT] = ++INSTANCE_COUNTER;
}, {
/**
* The hierarchical namespace for this component that indicates it's functionality.
* @protected
* @readonly
* @property {String}
*/
"displayName": "core/composition",
/**
* Gives string representation of this component instance.
* @return {String} {@link #displayName}`@`{@link #instanceCount}
* @protected
*/
"toString": function () {
var me = this;
return me.displayName + "@" + me[INSTANCE_COUNT];
}
});
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-core/emitter/executor',[
"../config",
"when/when"
], function (config, when) {
/**
* @class core.emitter.executor
* @mixin Function
* @private
* @static
* @alias feature.executor
*/
var UNDEFINED;
var CALLBACK = config.emitter.callback;
var SCOPE = config.emitter.scope;
var HEAD = config.emitter.head;
var NEXT = config.emitter.next;
/**
* Executes an emission
* @method constructor
* @param {Object} event Event object
* @param {Object} handlers List of handlers
* @param {*[]} [args] Handler arguments
* @localdoc
* - Executes event handlers asynchronously passing each handler `args`.
*
* @return {*} Array of handler results
*/
return function (event, handlers, args) {
var _handlers = [];
var _handlersCount = 0;
var callback = event[CALLBACK];
var scope = event[SCOPE];
var handler;
for (handler = handlers[HEAD]; handler !== UNDEFINED; handler = handler[NEXT]) {
if (callback && handler[CALLBACK] !== callback) {
continue;
}
if (scope && handler[SCOPE] !== scope) {
continue;
}
_handlers[_handlersCount++] = handler;
}
return when.reduce(_handlers, function (results, _handler, index) {
return when(_handler.handle(args), function (result) {
results[index] = result;
})
.yield(results);
}, _handlers);
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-core/emitter/composition',[
"mu-emitter/main",
"../composition",
"../config",
"./executor",
"troopjs-compose/decorator/from",
"when/when"
], function (Emitter, Composition, config, executor, from) {
/**
* This event module is heart of all TroopJS event-based whistles, from the API names it's aligned with Node's events module,
* while behind the regularity it's powered by a highly customizable **event executor** mechanism.
*
* @class core.emitter.composition
* @extend core.composition
*/
var EXECUTOR = config.emitter.executor;
var HANDLERS = config.emitter.handlers;
/**
* Event executor
* @private
* @readonly
* @property {core.emitter.executor} executor
*/
/**
* Event handlers
* @protected
* @readonly
* @property {Object} handlers Object where the key represents the event type and the value is a list of {@link core.emitter.handler handlers} for that type.
*/
/**
* @method constructor
* @inheritdoc
*/
return Composition.extend(function () {
this[HANDLERS] = {};
}, function (key, value) {
var me = this;
me[key] = value;
return me;
}.call({
"displayName": "core/emitter/composition",
/**
* Adds a listener for the specified event type.
* @method
* @param {String} type The event type to subscribe to.
* @param {Function|Object} callback The event callback to add. If callback is a function defaults from below will be used:
* @param {Function} callback.callback Callback method.
* @param {Object} [callback.scope=this] Callback scope.
* @param {Number} [callback.limit=0] Callback limit.
* @param {*} [data] Handler data
* @returns {core.emitter.handler} Handler that was added.
*/
"on": from(Emitter),
/**
* Remove callback(s) from a subscribed event type, if no callback is specified,
* remove all callbacks of this type.
* @method
* @param {String} type The event type subscribed to
* @param {Function|Object} [callback] The event callback to remove. If callback is a function scope will be this, otherwise:
* @param {Function} [callback.callback] Callback method to match.
* @param {Object} [callback.scope=this] Callback scope to match.
* @returns {core.emitter.handler[]} Handlers that were removed.
*/
"off": from(Emitter),
/**
* Adds a listener for the specified event type exactly once.
* @method
* @inheritdoc #on
*/
"one": from(Emitter),
/**
* Trigger an event which notifies each of the listeners of their subscribing,
* optionally pass data values to the listeners.
*
* A sequential runner, is the default runner for this module, in which all handlers are running
* with the same argument data specified by the {@link #emit} function.
* Each handler will wait for the completion for the previous one if it returns a promise.
*
* @method
* @param {String|Object} event The event type to emit, or an event object
* @param {String} [event.type] The event type name.
* @param {Function} [event.runner] The runner function that determinate how the handlers are executed, overrides the
* default behaviour of the event emitting.
* @param {...*} [args] Data params that are passed to the listener function.
* @return {*} Result returned from runner.
*/
"emit": from(Emitter)
}, EXECUTOR, executor));
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-core/component/executor',[ "../config" ], function (config) {
/**
* @class core.component.executor
* @mixin Function
* @private
* @static
* @alias feature.executor
*/
var UNDEFINED;
var FALSE = false;
var HEAD = config.emitter.head;
var NEXT = config.emitter.next;
var CALLBACK = config.emitter.callback;
var SCOPE = config.emitter.scope;
/**
* @method constructor
* @inheritdoc core.emitter.executor#constructor
* @localdoc
* - Executes event handlers synchronously passing each handler `args`.
* - Anything returned from a handler except `undefined` will be stored as `result`
* - If a handler returns `undefined` the current `result` will be kept
* - If a handler returns `false` no more handlers will be executed.
*
* @return {*} Stored `result`
*/
return function (event, handlers, args) {
var _handlers = [];
var _handlersCount = 0;
var scope = event[SCOPE];
var callback = event[CALLBACK];
var handler;
// Iterate handlers
for (handler = handlers[HEAD]; handler !== UNDEFINED; handler = handler[NEXT]) {
if (callback && handler[CALLBACK] !== callback) {
continue;
}
if (scope && handler[SCOPE] !== scope) {
continue;
}
_handlers[_handlersCount++] = handler;
}
// Reduce and return
return _handlers.reduce(function (current, _handler) {
var result = current !== FALSE
? _handler.handle(args)
: current;
return result === UNDEFINED
? current
: result;
}, UNDEFINED);
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-core/registry/emitter',[
"../emitter/composition",
"../config",
"../component/executor"
], function (Emitter, config, executor) {
/**
* A light weight implementation to register key/value pairs by key and index
* @class core.registry.emitter
* @extend core.emitter.composition
*/
var LENGTH = "length";
var INDEX = "index";
var OBJECT_TOSTRING = Object.prototype.toString;
var TOSTRING_REGEXP = "[object RegExp]";
var SIG_REGISTER = config.signal.register;
var SIG_UNREGISTER = config.signal.unregister;
var EXECUTOR = config.emitter.executor;
/**
* Register signal
* @event sig/register
* @localdoc Triggered when something is registered via {@link #register}.
* @since 3.0
* @param {String} key
* @param {*} value
*/
/**
* Un-register signal
* @event sig/unregister
* @localdoc Triggered when something is un-registered via {@link #unregister}.
* @since 3.0
* @param {String} key
* @param {*} value
*/
/**
* @method constructor
* @inheritdoc
*/
return Emitter.extend(function () {
/**
* Registry index
* @private
* @readonly
*/
this[INDEX] = {};
}, function (key, value) {
var me = this;
me[key] = value;
return me;
}.call({
"displayName": "core/registry/emitter",
/**
* Gets value by key
* @param {String|RegExp} [key] key to filter by
* - If `String` get value exactly registered for key.
* - If `RegExp` get value where key matches.
* - If not provided all values registered are returned
* @return {*|*[]} result(s)
*/
"get": function (key) {
var index = this[INDEX];
var result;
if (arguments[LENGTH] === 0) {
result = Object
.keys(index)
.map(function (_key) {
return index[_key];
});
}
else if (OBJECT_TOSTRING.call(key) === TOSTRING_REGEXP) {
result = Object
.keys(index)
.filter(function (_key) {
return key.test(_key);
}).map(function (_key) {
return index[_key];
});
}
else {
result = index[key];
}
return result;
},
/**
* Registers value with key
* @param {String} key Key
* @param {*} value Value
* @fires sig/register
* @return {*} value registered
*/
"register": function (key, value) {
var me = this;
var index = me[INDEX];
if (index[key] !== value) {
if (index.hasOwnProperty(key)) {
me.unregister(key);
}
me.emit(SIG_REGISTER, key, index[key] = value);
}
return value;
},
/**
* Un-registers key
* @param {String} key Key
* @fires sig/unregister
* @return {*} value unregistered
*/
"unregister": function (key) {
var me = this;
var index = me[INDEX];
var value;
if (index.hasOwnProperty(key)) {
value = index[key];
if (delete index[key]) {
me.emit(SIG_UNREGISTER, key, value);
}
}
return value;
}
}, EXECUTOR, executor));
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-core/component/registry',[ "../registry/emitter" ], function (Registry) {
/**
* @class core.component.registry
* @extend core.registry.emitter
* @singleton
*/
/**
* @method create
* @static
* @hide
*/
/**
* @method extend
* @static
* @hide
*/
/**
* @method constructor
* @hide
*/
return Registry.create({
"displayName": "core/component/registry"
});
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-core/task/registry',[ "../registry/emitter" ], function (Registry) {
/**
* @class core.task.registry
* @extend core.registry.emitter
* @singleton
*/
/**
* @method create
* @static
* @hide
*/
/**
* @method extend
* @static
* @hide
*/
/**
* @method constructor
* @hide
*/
return Registry.create({
"displayName": "core/task/registry"
});
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-core/task/factory',[
"./registry",
"when/when"
], function (registry, when) {
/**
* @class core.task.factory
* @mixin Function
* @static
*/
var TASK_COUNTER = 0;
/**
* Creates and registers a task
* @method constructor
* @param {Promise|Function} promiseOrResolver The task resolver.
* @param {String} [name=task] Task name
* @return {Promise}
*/
return function (promiseOrResolver, name) {
// Get promise
var promise = when.isPromiseLike(promiseOrResolver)
? when(promiseOrResolver)
: when.promise(promiseOrResolver);
// Create key
var key = (name || "task") + "@" + ++TASK_COUNTER;
// Ensure un-registration
// Register task
// Return
return registry.register(key, promise.ensure(function () {
registry.unregister(key);
}));
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-core/component/emitter',[
"../emitter/composition",
"../config",
"./registry",
"./executor",
"../task/factory",
"mu-merge/main",
"troopjs-compose/decorator/around"
], function (Emitter, config, registry, executor, taskFactory, merge, around) {
/**
* Component emitter
* @class core.component.emitter
* @extend core.emitter.composition
* @mixin core.config
* @alias feature.component
*/
var UNDEFINED;
var FALSE = false;
var EXECUTOR = config.emitter.executor;
var HANDLERS = config.emitter.handlers;
var HEAD = config.emitter.head;
var TAIL = config.emitter.tail;
var SIG_SETUP = config.signal.setup;
var SIG_ADD = config.signal.add;
var SIG_ADDED = config.signal.added;
var SIG_REMOVE = config.signal.remove;
var SIG_REMOVED = config.signal.removed;
var SIG_TEARDOWN = config.signal.teardown;
var SIG_TASK = config.signal.task;
var PHASE = "phase";
var NAME = "name";
var TYPE = "type";
var VALUE = "value";
var ON = "on";
var ONE = "one";
var SIG = "sig";
var SIG_PATTERN = new RegExp("^" + SIG + "/(.+)");
/**
* Current lifecycle phase
* @protected
* @readonly
* @property {core.config.phase} phase
*/
/**
* Setup signal
* @event sig/setup
* @localdoc Triggered when the first event handler of a particular type is added via {@link #method-on}.
* @since 3.0
* @preventable
* @param {Object} handlers
* @param {String} type
* @param {Function} callback
* @param {*} [data]
*/
/**
* Add signal
* @event sig/add
* @localdoc Triggered before an event handler of a particular type is added via {@link #method-on}.
* @since 3.0
* @preventable
* @param {Object} handlers
* @param {String} type
* @param {Function} callback
* @param {*} [data]
*/
/**
* Added signal
* @event sig/added
* @localdoc Triggered when a event handler of a particular type is added via {@link #method-on}.
* @since 3.0
* @param {Object} handlers The list of handlers the handler was added to.
* @param {core.emitter.handler} handler The handler that was just added.
*/
/**
* Remove signal
* @event sig/remove
* @localdoc Triggered before an event handler of a particular type is removed via {@link #method-off}.
* @since 3.0
* @preventable
* @param {Object} handlers
* @param {String} type
* @param {Function} callback
*/
/**
* Removed signal
* @event sig/removed
* @localdoc Triggered when a event handler of a particular type is removed via {@link #method-off}.
* @since 3.0
* @param {Object} handlers The list of handlers the handler was removed from.
* @param {core.emitter.handler} handler The handler that was just removed.
*/
/**
* Teardown signal
* @event sig/teardown
* @localdoc Triggered when the last event handler of type is removed for a particular type via {@link #method-off}.
* @since 3.0
* @preventable
* @param {Object} handlers
* @param {String} type
* @param {Function} callback
*/
/**
* Initialize signal
* @event sig/initialize
* @localdoc Triggered when this component enters the initialize {@link #property-phase}
* @param {...*} [args] Initialize arguments
*/
/**
* Start signal
* @event sig/start
* @localdoc Triggered when this component enters the start {@link #property-phase}
* @param {...*} [args] Initialize arguments
*/
/**
* Stop signal
* @event sig/stop
* @localdoc Triggered when this component enters the stop {@link #property-phase}
* @param {...*} [args] Stop arguments
*/
/**
* Finalize signal
* @event sig/finalize
* @localdoc Triggered when this component enters the finalize {@link #property-phase}
* @param {...*} [args] Finalize arguments
*/
/**
* Task signal
* @event sig/task
* @localdoc Triggered when this component starts a {@link #method-task}.
* @param {Promise} task Task
* @param {String} name Task name
* @return {Promise}
*/
/**
* Handles the component start
* @handler sig/start
* @inheritdoc #event-sig/start
* @template
* @return {Promise}
*/
/**
* Handles the component stop
* @handler sig/stop
* @inheritdoc #event-sig/stop
* @template
* @return {Promise}
*/
/**
* Handles an event setup
* @handler sig/setup
* @inheritdoc #event-sig/setup
* @template
* @return {*|Boolean}
*/
/**
* Handles an event add
* @handler sig/add
* @inheritdoc #event-sig/add
* @template
* @return {*|Boolean}
*/
/**
* Handles an event remove
* @handler sig/remove
* @inheritdoc #event-sig/remove
* @template
* @return {*|Boolean}
*/
/**
* Handles an event teardown
* @handler sig/teardown
* @inheritdoc #event-sig/teardown
* @template
* @return {*|Boolean}
*/
/**
* @method one
* @inheritdoc
* @localdoc Adds support for {@link #event-sig/setup} and {@link #event-sig/add}.
* @fires sig/setup
* @fires sig/add
*/
/**
* @method constructor
* @inheritdoc
*/
return Emitter.extend(function () {
var me = this;
var specials = me.constructor.specials;
// Set initial phase to `UNDEFINED`
me[PHASE] = UNDEFINED;
// Iterate SIG specials
if (specials.hasOwnProperty(SIG)) {
specials[SIG].forEach(function (special) {
me.on(special[NAME], special[VALUE]);
});
}
}, {
"displayName": "core/component/base",
/**
* Handles the component initialization.
* @inheritdoc #event-sig/initialize
* @localdoc Registers event handlers declared ON specials
* @handler
* @return {Promise}
*/
"sig/initialize": function () {
var me = this;
var specials = me.constructor.specials;
// Register component
registry.register(me.toString(), me);
// Initialize ON specials
if (specials.hasOwnProperty(ON)) {
specials[ON].forEach(function (special) {
me.on(special[TYPE], special[VALUE]);
});
}
// Initialize ONE specials
if (specials.hasOwnProperty(ONE)) {
specials[ONE].forEach(function (special) {
me.one(special[TYPE], special[VALUE]);
});
}
},
/**
* Handles the component finalization.
* @inheritdoc #event-sig/finalize
* @localdoc Un-registers all handlers
* @handler
* @return {Promise}
*/
"sig/finalize": function () {
var me = this;
// Un-register component
registry.unregister(me.toString(), me);
// Finalize all handlers
Object
.keys(me[HANDLERS])
.forEach(function (type) {
me.off(type);
});
},
/**
* @method
* @inheritdoc
* @localdoc Adds support for {@link #event-sig/setup}, {@link #event-sig/add} and {@link #event-sig/added}.
* @fires sig/setup
* @fires sig/add
* @fires sig/added
*/
"on": around(function (fn) {
return function (type, callback, data) {
var me = this;
var handlers = me[HANDLERS];
var event;
var result;
var _handlers;
// If this type is NOT a signal we don't have to event try ...
if (!SIG_PATTERN.test(type)) {
// Get or initialize the handlers for this type
if (handlers.hasOwnProperty(type)) {
_handlers = handlers[type];
}
else {
_handlers = {};
_handlers[TYPE] = type;
}
// Initialize event
event = {};
event[EXECUTOR] = executor;
// If this is the first handler signal SIG_SETUP
if (!_handlers.hasOwnProperty(HEAD)) {
event[TYPE] = SIG_SETUP;
result = me.emit(event, _handlers, type, callback, data);
}
// If we were not interrupted
if (result !== FALSE) {
// Signal SIG_ADD
event[TYPE] = SIG_ADD;
result = me.emit(event, _handlers, type, callback, data);
// If we were not interrupted
if (result !== FALSE) {
// If `type` is not in `handlers` put it there
if (!handlers.hasOwnProperty(type)) {
handlers[type] = _handlers;
}
// Call `super.on`
result = fn.call(me, type, callback, data);
// Signal SIG_ADDED
event[TYPE] = SIG_ADDED;
me.emit(event, _handlers, result);
}
}
}
// .. just call `super.on`
else {
result = fn.call(me, type, callback, data);
}
// Return `result`
return result;
};
}),
/**
* @method
* @inheritdoc
* @localdoc Adds support for {@link #event-sig/remove}, {@link #event-sig/removed} and {@link #event-sig/teardown}.
* @fires sig/remove
* @fires sig/removed
* @fires sig/teardown
*/
"off": around(function (fn) {
return function (type, callback) {
var me = this;
var handlers = me[HANDLERS];
var event;
var result;
var _handlers;
if (!SIG_PATTERN.test(type)) {
// Get or initialize the handlers for this type
if (handlers.hasOwnProperty(type)) {
_handlers = handlers[type];
}
else {
_handlers = {};
_handlers[TYPE] = type;
}
// Initialize event
event = {};
event[EXECUTOR] = executor;
// Signal SIG_REMOVE
event[TYPE] = SIG_REMOVE;
result = me.emit(event, _handlers, type, callback);
// If we were not interrupted
if (result !== FALSE) {
// If this is the last handler signal SIG_TEARDOWN
if (_handlers[HEAD] === _handlers[TAIL]) {
event[TYPE] = SIG_TEARDOWN;
result = me.emit(event, _handlers, type, callback);
}
// If we were not interrupted
if (result !== FALSE) {
// If `type` is not in `handlers` put it there
if (!handlers.hasOwnProperty(type)) {
handlers[type] = _handlers;
}
// Call `super.off`
result = fn.call(me, type, callback);
// Signal SIG_REMOVED
event[TYPE] = SIG_REMOVED;
result.forEach(function (handler) {
me.emit(event, _handlers, handler);
});
}
}
}
// ... just call `super.off`
else {
result = fn.call(me, type, callback);
}
// Return `result`
return result;
};
}),
/**
* @inheritdoc core.task.factory#constructor
* @fires sig/task
*/
"task": function (promiseOrResolver, name) {
var me = this;
// Create task
var task = taskFactory.call(me, promiseOrResolver, name);
// Signal `SIG_TASK` and yield `task`
return me.emit(SIG_TASK, task, name).yield(task);
}
});
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-hub/config',[
"troopjs-core/config",
"module",
"mu-merge/main"
], function (config, module, merge) {
/**
* @class hub.config.emitter
* @extends core.config.emitter
* @private
*/
var EMITTER = {
/**
* Property name for `memory`
*/
"memory": "memory"
};
/**
* HUB component configuration
* @class hub.config
* @extends core.config
* @private
* @alias feature.config
*/
return merge.call({}, config, {
/**
* @cfg {hub.config.emitter}
* @inheritdoc
* @protected
*/
"emitter": EMITTER
}, module.config());
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-hub/executor',[
"./config",
"when/when"
], function (config, when) {
/**
* @class hub.executor
* @mixin Function
* @private
* @static
* @alias feature.executor
*/
var UNDEFINED;
var OBJECT_TOSTRING = Object.prototype.toString;
var ARRAY_SLICE = Array.prototype.slice;
var TOSTRING_ARGUMENTS = "[object Arguments]";
var TOSTRING_ARRAY = "[object Array]";
var SKIP = config.phase.skip;
var SCOPE = config.emitter.scope;
var CALLBACK = config.emitter.callback;
var HEAD = config.emitter.head;
var NEXT = config.emitter.next;
var MEMORY = config.emitter.memory;
var PHASE = "phase";
/**
* @method constructor
* @inheritdoc core.emitter.executor#constructor
* @localdoc
* - Skips handlers who's scope.{@link core.component.emitter#property-phase phase} matches {@link core.config.phase#skip}.
* - Executes handlers passing each handler the result from the previous.
* - If a handler returns `undefined` the result from the previous is used.
* - When all handlers are completed the end result is memorized on `handlers`
*
* @return {Promise} Promise for `[*]`
*/
return function (event, handlers, args) {
var _handlers = [];
var _handlersCount = 0;
var scope = event[SCOPE];
var callback = event[CALLBACK];
var handler;
// Iterate handlers
for (handler = handlers[HEAD]; handler !== UNDEFINED; handler = handler[NEXT]) {
if (callback && handler[CALLBACK] !== callback) {
continue;
}
if (scope && handler[SCOPE] !== scope) {
continue;
}
_handlers[_handlersCount++] = handler;
}
return when
// Reduce `_handlers`
.reduce(_handlers, function (current, _handler) {
// Let `_scope` be `handler[SCOPE]`
var _scope = _handler[SCOPE];
// Return early if `_scope` has `PHASE` _and_ `_scope[PHASE]` is `UNDEFINED` or matches a skipped phase
if (_scope.hasOwnProperty(PHASE) && (_scope[PHASE] === UNDEFINED || SKIP.test(_scope[PHASE]))) {
return current;
}
// Run `handler` passing `args`
// Pass to `when` to (potentially) update `result`
return when(_handler.handle(current), function (result) {
// If `result` is `UNDEFINED` ...
if (result === UNDEFINED) {
// ... return `current` ...
return current;
}
// Detect `result` type
switch (OBJECT_TOSTRING.call(result)) {
// `arguments` should be converted to an array
case TOSTRING_ARGUMENTS:
return ARRAY_SLICE.call(result);
// `array` can be passed as-is
case TOSTRING_ARRAY:
return result;
// everything else should be wrapped in an array
default:
return [ result ];
}
});
}, args)
// Memorize
.tap(function (result) {
// Store `result` in `handlers[MEMORY]`
handlers[MEMORY] = result;
});
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-hub/emitter',[
"troopjs-core/emitter/composition",
"./config",
"./executor"
], function (Emitter, config, executor) {
/**
* A static version of {@link core.emitter.composition} with memorization.
*
* ## Memorized emitting
*
* A emitter event will memorize the "current" value of each event. Each executor may have it's own interpretation
* of what "current" means.
*
* @class hub.emitter
* @extend core.emitter.composition
* @mixin hub.config
* @inheritdoc
* @singleton
*/
/**
* @method create
* @static
* @hide
*/
/**
* @method extend
* @static
* @hide
*/
/**
* @method constructor
* @hide
*/
var UNDEFINED;
var MEMORY = config.emitter.memory;
var HANDLERS = config.emitter.handlers;
var EXECUTOR = config.emitter.executor;
return Emitter.create(function (key, value) {
var me = this;
me[key] = value;
return me;
}.call({
"displayName": "hub/emitter",
/**
* Returns value in handlers MEMORY
* @param {String} type event type to peek at
* @param {*} [value] Value to use _only_ if no memory has been recorder
* @return {*} Value in MEMORY
*/
"peek": function (type, value) {
var handlers;
return (handlers = this[HANDLERS][type]) === UNDEFINED || !handlers.hasOwnProperty(MEMORY)
? value
: handlers[MEMORY];
}
}, EXECUTOR, executor));
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-hub/component',[
"troopjs-core/component/emitter",
"./config",
"./emitter",
"when/when"
], function (Emitter, config, emitter, when) {
/**
* Component that provides hub features.
* @class hub.component
* @extend core.component.emitter
* @mixin hub.config
* @alias feature.component
*/
var UNDEFINED;
var NULL = null;
var TRUE = true;
var DATA = config.emitter.data;
var ARGS = "args";
var NAME = "name";
var TYPE = "type";
var VALUE = "value";
var HUB = "hub";
var RE = new RegExp("^" + HUB + "/(.+)");
/**
* @handler sig/start
* @localdoc Triggers memorized values on HUB specials
* @inheritdoc
*/
/**
* @handler sig/added
* @localdoc Registers subscription on the {@link hub.emitter hub emitter} for matching callbacks
* @inheritdoc #event-sig/added
*/
/**
* @handler sig/removed
* @localdoc Removes remote subscription from the {@link hub.emitter hub emitter} that was previously registered in {@link #handler-sig/added}
* @inheritdoc #event-sig/removed
*/
/**
* @method constructor
* @inheritdoc
*/
return Emitter.extend(function () {
var me = this;
var memorized = [];
// Intercept added handlers
me.on("sig/added", function (handlers, handler) {
var _empty = {};
var _matches;
var _type;
var _memory;
// If we've added a HUB handler ...
if ((_matches = RE.exec(handler[TYPE])) !== NULL) {
// Let `_type` be `_matches[1]`
_type = _matches[1];
// Let `handler[HUB]` be `_type`
handler[HUB] = _type;
// Subscribe to the hub
emitter.on(_type, handler);
// If re-emit was requested ...
if (handler[DATA] === TRUE) {
// If memorization is "open"
if (memorized !== UNDEFINED) {
memorized.push(handler);
}
// .. otherwise try to `emit` if `emitter` memory for `_type` is not `_empty`
else if ((_memory = emitter.peek(_type, _empty)) !== _empty) {
me.emit.apply(me, [ handler ].concat(_memory));
}
}
}
});
// Intercept removed handlers
me.on("sig/removed", function (handlers, handler) {
var _matches;
// If we've removed a HUB callback ...
if ((_matches = RE.exec(handler[TYPE])) !== NULL) {
// Unsubscribe from the hub
emitter.off(_matches[1], handler);
// If re-emit was requested and there are `memorized` callbacks ...
if (handler[DATA] === TRUE && memorized !== UNDEFINED) {
// TODO in place filtering for performance
// Filter matching `_handler`
memorized = memorized.filter(function (_handler) {
return _handler !== handler;
});
}
}
});
// Intercept component start
me.on("sig/start", function () {
return when
// Map `memorized` ...
.map(memorized, function (_handler) {
var _memory;
var _empty = {};
// If `emitter` memory for `_handler[HUB]` is not `_empty` re-emit ...
return (_memory = emitter.peek(_handler[HUB], _empty)) !== _empty
? me.emit.apply(me, [ _handler ].concat(_memory))
: UNDEFINED;
})
// ... and reset to `UNDEFINED`
.tap(function () {
memorized = UNDEFINED;
});
});
}, {
"displayName": "hub/component",
/**
* @inheritdoc
* @localdoc Registers event handlers declared HUB specials
* @handler
*/
"sig/initialize": function () {
var me = this;
var specials = me.constructor.specials;
if (specials.hasOwnProperty(HUB)) {
specials[HUB].forEach(function (special) {
me.on(special[NAME], special[VALUE], special[ARGS][0]);
});
}
}
});
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-dom/config',[
"troopjs-core/config",
"module",
"mu-merge/main"
], function (config, module, merge) {
/**
* @class dom.config.signal
* @extends core.config.signal
* @private
*/
var SIGNAL = {
/**
* Signal emitted when component renders.
*/
"render": "sig/render"
};
/**
* DOM component configuration
* @class dom.config
* @extends core.config
* @private
* @alias feature.config
*/
return merge.call({}, config, {
/**
* @cfg {dom.config.signal}
* @inheritdoc
* @protected
*/
"signal": SIGNAL
}, module.config());
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-dom/executor',[
"mu-selector-set/main",
"jquery"
], function (SelectorSet, $) {
/**
* @class dom.executor
* @mixin Function
* @private
* @static
* @alias feature.executor
*/
var UNDEFINED;
var FALSE = false;
var LEFT_BUTTON = 0;
function map (match) {
return match[1];
}
// Use `$.find.matchesSelector` for wider browser support
SelectorSet.prototype.matchesSelector = $.find.matchesSelector;
/**
* @method constructor
* @inheritdoc core.emitter.executor#constructor
* @localdoc
* - Executes handlers synchronously passing each handler `args`.
* - If a handler returns `false` no more handlers will be executed.
* - If a handler stops propagation no more handlers will be executed.
*
* @return {*} Result from last handler
*/
return function (event, handlers, args) {
var result = UNDEFINED;
var direct = handlers.direct;
var delegated = handlers.delegated;
var $event = args[0];
var $delegate = $event.delegateTarget;
var $target = $event.target;
var $document = $target.ownerDocument;
var $notClick = $event.type !== "click";
// Bubble the event up the dom if
// ... this is not a black-holed element (jQuery #13180)
// ... and this is the left button or it's a not a click event (jQuery #3861)
var $bubble = $target.nodeType !== UNDEFINED && ($event.button === LEFT_BUTTON || $notClick);
function reduce (_result, handler) {
// If immediate propagation is stopped we should just return last _result
if ($event.isImmediatePropagationStopped()) {
return _result;
}
// If the previous handler return false we should stopPropagation and preventDefault
if (_result === FALSE) {
$event.stopPropagation();
$event.preventDefault();
}
return handler.handle(args);
}
// Loop ...
do {
// Don't process clicks on disabled elements (jQuery #6911, #8165, #11382, #11764)
if ($target.disabled !== true || $notClick) {
// Run delegated handlers which match this element
result = delegated
.matches($event.currentTarget = $target)
.map(map)
.reduce(reduce, result);
}
// Bubble if ...
$bubble = $bubble
// ... we were not told to stop propagation
&& !$event.isPropagationStopped()
// ... we are not at the delegate element
&& $target !== $delegate
// ... we have a parent node
&& ($target = $target.parentNode) !== null
// ... the new target is not the document
&& $target !== $document;
}
// ... while we are still bubbling
while ($bubble);
// Run all the direct (non-delegated) handlers of the root element
if (result !== FALSE) {
result = direct.reduce(reduce, result);
}
return result;
};
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-dom/error',[ "mu-error/factory" ], function (Factory) {
/**
* DOM error
* @class dom.error
* @extend Error
* @alias feature.error
*/
return Factory("DOMError");
});
/**
* @license MIT http://troopjs.mit-license.org/
*/
define('troopjs-dom/component',[
"troopjs-core/component/emitter",
"./config",
"./executor",
"./error",
"troopjs-compose/decorator/before",
"jquery",
"when/when",
"mu-selector-set/main",
"mu-jquery-destroy/main"
], function (Component, config, executor, DOMError, before, $, when, SelectorSet) {
/**
* Component that manages all DOM manipulation and integration.
* @class dom.component
* @extend core.component.emitter
* @mixin dom.config
* @alias feature.component
*/
var UNDEFINED;
var NULL = null;
var OBJECT_TOSTRING = Object.prototype.toString;
var TOSTRING_FUNCTION = "[object Function]";
var $ELEMENT = "$element";
var LENGTH = "length";
var PROXY = "proxy";
var DOM = "dom";
var ARGS = "args";
var NAME = "name";
var VALUE = "value";
var TYPE = config.emitter.type;
var EXECUTOR = config.emitter.executor;
var SCOPE = config.emitter.scope;
var DATA = config.emitter.data;
var DIRECT = "direct";
var DELEGATED = "delegated";
var SIG_RENDER = config.signal.render;
var RE = new RegExp("^" + DOM + "/(.+)");
function $render ($element, method, args) {
var me = this;
var length = args[LENGTH];
return when(args[0], function (content) {
var _length;
var _args;
// If `content` is a string we can return fast
if (OBJECT_TOSTRING.call(content) !== TOSTRING_FUNCTION) {
return content;
}
// Initialize `_length` and `_args`
_length = length;
_args = new Array(_length - 1);
// Copy `args` to `_args`
while (_length-- > 1) {
_args[_length - 1] = args[_length];
}
// Return result of applying `content` on `$element` with `_args`
return content.apply($element, _args);
})
.then(function (content) {
// Let `args[0]` be `$(content)`
// Let `$content` be `args[0]`
var $content = args[0] = $(content);
// Initialize `_length` and `_args`
var _length = length;
var _args = new Array(_length + 1);
// Let `_args[0]` be `SIG_RENDER`
_args[0] = SIG_RENDER;
// Copy `args` to `_args`
while (_length-- > 0) {
_args[_length + 1] = args[_length];
}
// Determine direction of manipulation\
switch (method) {
case "appendTo":
case "prependTo":
$content[method]($element);
break;
default:
$element[method]($content);
}
// Emit `_args`
// Yield `args`
return me.emit
.apply(me, _args)
.yield(args);
});
}
/**
* Render signal
* @event sig/render
* @localdoc Triggered after {@link #before}, {@link #after}, {@link #html}, {@link #text}, {@link #append}, {@link #appendTo}, {@link #prepend} and {@link #prependTo}
* @since 3.0
* @param {jQuery} $target Element rendered into
* @param {...*} [args] Render arguments
*/
/**
* Handles component render
* @handler sig/render
* @template
* @inheritdoc #event-sig/render
* @return {Promise}
*/
/**
* Destroy DOM event
* @localdoc Triggered when {@link #$element} of this widget is removed from the DOM tree.
* @event dom/destroy
* @param {jQuery} $event jQuery event object
* @param {...*} [args] Event arguments
* @preventable
*/
/**
* Handles widget destruction from a DOM perspective
* @handler dom/destroy
* @template
* @inheritdoc #event-dom/destroy
* @localdoc Triggered when this widget is removed from the DOM
*/
/**
* Renders content and replaces {@link #$element} html contents
* @method html
* @param {Function|String|Promise} [contentOrPromise] Contents to render or a Promise for contents
* @param {...*} [args] Template arguments
* @fires sig/render
* @return {String|Promise} The returned content string or promise of rendering.
*/
/**
* Renders content and replaces {@link #$element} text contents
* @method text
* @inheritdoc #html
* @fires sig/render
*/
/**
* Renders content and inserts it before {@link #$element}
* @method before
* @inheritdoc #html
* @fires sig/render
* @return {Promise} The promise of rendering.
*/
/**
* Renders content and inserts it after {@link #$element}
* @method after
* @inheritdoc #html
* @fires sig/render
*/
/**
* Renders content and appends it to {@link #$element}
* @method append
* @inheritdoc #html
* @fires sig/render
*/
/**
* Renders content and appends it to the provided $element
* @method appendTo
* @param {jQuery} $element Target element
* @param {Function|String|Promise} [contentOrPromise] Contents to render or a Promise for contents
* @param {...*} [args] Template arguments
* @fires sig/render
* @return {Promise} The promise of rendering.
*/
/**
* Renders content and prepends it to {@link #$element}
* @method prepend
* @inheritdoc #html
* @fires sig/render
*/
/**
* Renders content and prepends it to the provided $element
* @method prependTo
* @inheritdoc #appendTo
* @fires sig/render
*/
/**
* Creates a new component that attaches to a specified (jQuery) DOM element.
* @method constructor
* @param {jQuery|HTMLElement} $element The element that this component should be attached to
* @param {String} [displayName] A friendly name for this component
* @throws {dom.error} If no $element is provided
*/
return Component.extend(
function ($element, displayName) {
var me = this;
var length = arguments[LENGTH];
var args = new Array(length);
var $get;
// No $element
if ($element === UNDEFINED || $element === NULL) {
throw new DOMError("No '$element' provided");
}
// Let `args` be `ARRAY_SLICE.call(arguments)` without deop
while (length-- > 0) {
args[length] = arguments[length];
}
// Is _not_ a jQuery element
if (!$element.jquery) {
// Let `$element` be `$($element)`
// Let `args[0]` be `$element`
args[0] = $element = $($element);
}
// From a different jQuery instance
else if (($get = $element.get) !== $.fn.get) {
// Let `$element` be `$($get.call($element, 0))`
// Let `args[0]` be `$element`
args[0] = $element = $($get.call($element, 0));
}
/**
* jQuery element this widget is attached to
* @property {jQuery} $element
* @readonly
*/
me[$ELEMENT] = $element;
// Update `me.displayName` if `displayName` was supplied
if (displayName !== UNDEFINED) {
me.displayName = displayName;
}
// Return potentially modified `args`
return args;
},
{
"displayName": "dom/component",
/**
* @handler
* @localdoc Registers event handlers that are declared as DOM specials.
* @inheritdoc
*/
"sig/initialize": function () {
var me = this;
var specials = me.constructor.specials;
if (specials.hasOwnProperty(DOM)) {
specials[DOM].forEach(function (special) {
me.on(special[NAME], special[VALUE], special[ARGS][0]);
});
}
},
/**
* @handler
* @localdoc Registers DOM event proxies on {@link #$element}.
* @inheritdoc
*/
"sig/setup": function (handlers, type) {
var me = this;
var matches;
// Check that this is a DOM handler
if ((matches = RE.exec(type)) !== NULL) {
// Create delegated and direct event stores
handlers[DIRECT] = [];
handlers[DELEGATED] = new SelectorSet();
// `$element.on` `handlers[PROXY]`
me[$ELEMENT].on(matches[1], NULL, me, handlers[PROXY] = function () {
var length = arguments[LENGTH];
var args = new Array(length + 1);
var _args = args[0] = {};
_args[TYPE] = type;
_args[EXECUTOR] = executor;
_args[SCOPE] = me;
while (length > 0) {
args[length] = arguments[--length];
}
// Return result of emit
return me.emit.apply(me, args);
});
}
},
/**
* @handler
* @localdoc Adds handler to `handlers[DELEGATED]` or `handlers[DIRECT]` depending on `handler[DATA]`.
* @inheritdoc #event-sig/added
*/
"sig/added": function (handlers, handler) {
var data;
// Check that this is a DOM handler
if (RE.test(handler[TYPE])) {
data = handler[DATA];
if (data !== UNDEFINED) {
handlers[DELEGATED].add(data, handler);
}
else {
handlers[DIRECT].push(handler);
}
}
},
/**
* @handler
* @localdoc Removes the DOM event proxies that are registered on {@link #$element}.
* @inheritdoc
*/
"sig/teardown": function (handlers, type) {
var me = this;
var matches;
// Check that this is a DOM handler
if ((matches = RE.exec(type)) !== NULL) {
// $element.off handlers[PROXY]
me[$ELEMENT].off(matches[1], NULL, handlers[PROXY]);
}
},
/**
* @handler
* @localdoc Removes handle from `handlers[DELEGATED]` or `handlers[DIRECT]` depending on `handler[DATA]`.
* @inheritdoc #event-sig/removed
*/
"sig/removed": function (handlers, handler) {
var data;
// Check that this is a DOM handler
if (RE.test(handler[TYPE])) {
data = handler[DATA];
if (data !== UNDEFINED) {
handlers[DELEGATED].remove(data, handler);
}
else {
handlers[DIRECT] = handlers[DIRECT].filter(function (_handler) {
return _handler !== handler;
});
}
}
}
},
// Create spec for render methods targeting `me[$ELEMENT]` that can be executed without args
[ "html", "text" ].reduce(function (spec, method) {
// Create `spec[method]`
spec[method] = function () {
var me = this;
var $element = me[$ELEMENT];
var length = arguments[LENGTH];
var args;
var result;
// If there were no arguments ...
if (length === 0) {
// ... call `$element[method]` without arguments ...
result = $element[method]();
}
// ... otherwise ...
else {
// Create `args`
args = new Array(length);
// Let `args` be `ARRAY_SLICE.call(arguments)` without deop
while (length-- > 0) {
args[length] = arguments[length];
}
result = $render.call(me, $element, method, args);
}
return result;
};
// Return spec for next iteration
return spec;
}, {}),
// Create spec for render methods targeting `me[$ELEMENT]`
[ "before", "after", "append", "prepend" ].reduce(function (spec, method) {
// Create `spec[method]`
spec[method] = function () {
var me = this;
var length = arguments[LENGTH];
var args = new Array(length);
// Let `args` be `ARRAY_SLICE.call(arguments)` without deop
while (length-- > 0) {
args[length] = arguments[length];
}
return $render.call(me, me[$ELEMENT], method, args);
};
// Return spec for next iteration
return spec;
}, {}),
// Create spec for render methods targeting provided `$element`
[ "appendTo", "prependTo" ].reduce(function (spec, method) {
// Create `spec[method]`
spec[method] = function ($element) {
var length = arguments[LENGTH];
var args = new Array(length - 1);
// Let `args` be `ARRAY_SLICE.call(arguments, 1)` without deop
while (length-- > 1) {
args[length - 1] = arguments[length];
}
return $render.call(this, $element, method, args);
};
// Return spec for next iteration
return spec;
}, {})
);
});
define(['troopjs/version'], function (version) {
return version;
});
\ No newline at end of file
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
(function(define) { 'use strict';
define(function (require) {
var makePromise = require('./makePromise');
var Scheduler = require('./Scheduler');
var async = require('./env').asap;
return makePromise({
scheduler: new Scheduler(async)
});
});
})(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); });
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
(function(define) { 'use strict';
define(function() {
// Credit to Twisol (https://github.com/Twisol) for suggesting
// this type of extensible queue + trampoline approach for next-tick conflation.
/**
* Async task scheduler
* @param {function} async function to schedule a single async function
* @constructor
*/
function Scheduler(async) {
this._async = async;
this._running = false;
this._queue = this;
this._queueLen = 0;
this._afterQueue = {};
this._afterQueueLen = 0;
var self = this;
this.drain = function() {
self._drain();
};
}
/**
* Enqueue a task
* @param {{ run:function }} task
*/
Scheduler.prototype.enqueue = function(task) {
this._queue[this._queueLen++] = task;
this.run();
};
/**
* Enqueue a task to run after the main task queue
* @param {{ run:function }} task
*/
Scheduler.prototype.afterQueue = function(task) {
this._afterQueue[this._afterQueueLen++] = task;
this.run();
};
Scheduler.prototype.run = function() {
if (!this._running) {
this._running = true;
this._async(this.drain);
}
};
/**
* Drain the handler queue entirely, and then the after queue
*/
Scheduler.prototype._drain = function() {
var i = 0;
for (; i < this._queueLen; ++i) {
this._queue[i].run();
this._queue[i] = void 0;
}
this._queueLen = 0;
this._running = false;
for (i = 0; i < this._afterQueueLen; ++i) {
this._afterQueue[i].run();
this._afterQueue[i] = void 0;
}
this._afterQueueLen = 0;
};
return Scheduler;
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
(function(define) { 'use strict';
define(function() {
/**
* Custom error type for promises rejected by promise.timeout
* @param {string} message
* @constructor
*/
function TimeoutError (message) {
Error.call(this);
this.message = message;
this.name = TimeoutError.name;
if (typeof Error.captureStackTrace === 'function') {
Error.captureStackTrace(this, TimeoutError);
}
}
TimeoutError.prototype = Object.create(Error.prototype);
TimeoutError.prototype.constructor = TimeoutError;
return TimeoutError;
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
\ No newline at end of file
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
(function(define) { 'use strict';
define(function() {
makeApply.tryCatchResolve = tryCatchResolve;
return makeApply;
function makeApply(Promise, call) {
if(arguments.length < 2) {
call = tryCatchResolve;
}
return apply;
function apply(f, thisArg, args) {
var p = Promise._defer();
var l = args.length;
var params = new Array(l);
callAndResolve({ f:f, thisArg:thisArg, args:args, params:params, i:l-1, call:call }, p._handler);
return p;
}
function callAndResolve(c, h) {
if(c.i < 0) {
return call(c.f, c.thisArg, c.params, h);
}
var handler = Promise._handler(c.args[c.i]);
handler.fold(callAndResolveNext, c, void 0, h);
}
function callAndResolveNext(c, x, h) {
c.params[c.i] = x;
c.i -= 1;
callAndResolve(c, h);
}
}
function tryCatchResolve(f, thisArg, args, resolver) {
try {
resolver.resolve(f.apply(thisArg, args));
} catch(e) {
resolver.reject(e);
}
}
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
(function(define) { 'use strict';
define(function(require) {
var state = require('../state');
var applier = require('../apply');
return function array(Promise) {
var applyFold = applier(Promise);
var toPromise = Promise.resolve;
var all = Promise.all;
var ar = Array.prototype.reduce;
var arr = Array.prototype.reduceRight;
var slice = Array.prototype.slice;
// Additional array combinators
Promise.any = any;
Promise.some = some;
Promise.settle = settle;
Promise.map = map;
Promise.filter = filter;
Promise.reduce = reduce;
Promise.reduceRight = reduceRight;
/**
* When this promise fulfills with an array, do
* onFulfilled.apply(void 0, array)
* @param {function} onFulfilled function to apply
* @returns {Promise} promise for the result of applying onFulfilled
*/
Promise.prototype.spread = function(onFulfilled) {
return this.then(all).then(function(array) {
return onFulfilled.apply(this, array);
});
};
return Promise;
/**
* One-winner competitive race.
* Return a promise that will fulfill when one of the promises
* in the input array fulfills, or will reject when all promises
* have rejected.
* @param {array} promises
* @returns {Promise} promise for the first fulfilled value
*/
function any(promises) {
var p = Promise._defer();
var resolver = p._handler;
var l = promises.length>>>0;
var pending = l;
var errors = [];
for (var h, x, i = 0; i < l; ++i) {
x = promises[i];
if(x === void 0 && !(i in promises)) {
--pending;
continue;
}
h = Promise._handler(x);
if(h.state() > 0) {
resolver.become(h);
Promise._visitRemaining(promises, i, h);
break;
} else {
h.visit(resolver, handleFulfill, handleReject);
}
}
if(pending === 0) {
resolver.reject(new RangeError('any(): array must not be empty'));
}
return p;
function handleFulfill(x) {
/*jshint validthis:true*/
errors = null;
this.resolve(x); // this === resolver
}
function handleReject(e) {
/*jshint validthis:true*/
if(this.resolved) { // this === resolver
return;
}
errors.push(e);
if(--pending === 0) {
this.reject(errors);
}
}
}
/**
* N-winner competitive race
* Return a promise that will fulfill when n input promises have
* fulfilled, or will reject when it becomes impossible for n
* input promises to fulfill (ie when promises.length - n + 1
* have rejected)
* @param {array} promises
* @param {number} n
* @returns {Promise} promise for the earliest n fulfillment values
*
* @deprecated
*/
function some(promises, n) {
/*jshint maxcomplexity:7*/
var p = Promise._defer();
var resolver = p._handler;
var results = [];
var errors = [];
var l = promises.length>>>0;
var nFulfill = 0;
var nReject;
var x, i; // reused in both for() loops
// First pass: count actual array items
for(i=0; i<l; ++i) {
x = promises[i];
if(x === void 0 && !(i in promises)) {
continue;
}
++nFulfill;
}
// Compute actual goals
n = Math.max(n, 0);
nReject = (nFulfill - n + 1);
nFulfill = Math.min(n, nFulfill);
if(n > nFulfill) {
resolver.reject(new RangeError('some(): array must contain at least '
+ n + ' item(s), but had ' + nFulfill));
} else if(nFulfill === 0) {
resolver.resolve(results);
}
// Second pass: observe each array item, make progress toward goals
for(i=0; i<l; ++i) {
x = promises[i];
if(x === void 0 && !(i in promises)) {
continue;
}
Promise._handler(x).visit(resolver, fulfill, reject, resolver.notify);
}
return p;
function fulfill(x) {
/*jshint validthis:true*/
if(this.resolved) { // this === resolver
return;
}
results.push(x);
if(--nFulfill === 0) {
errors = null;
this.resolve(results);
}
}
function reject(e) {
/*jshint validthis:true*/
if(this.resolved) { // this === resolver
return;
}
errors.push(e);
if(--nReject === 0) {
results = null;
this.reject(errors);
}
}
}
/**
* Apply f to the value of each promise in a list of promises
* and return a new list containing the results.
* @param {array} promises
* @param {function(x:*, index:Number):*} f mapping function
* @returns {Promise}
*/
function map(promises, f) {
return Promise._traverse(f, promises);
}
/**
* Filter the provided array of promises using the provided predicate. Input may
* contain promises and values
* @param {Array} promises array of promises and values
* @param {function(x:*, index:Number):boolean} predicate filtering predicate.
* Must return truthy (or promise for truthy) for items to retain.
* @returns {Promise} promise that will fulfill with an array containing all items
* for which predicate returned truthy.
*/
function filter(promises, predicate) {
var a = slice.call(promises);
return Promise._traverse(predicate, a).then(function(keep) {
return filterSync(a, keep);
});
}
function filterSync(promises, keep) {
// Safe because we know all promises have fulfilled if we've made it this far
var l = keep.length;
var filtered = new Array(l);
for(var i=0, j=0; i<l; ++i) {
if(keep[i]) {
filtered[j++] = Promise._handler(promises[i]).value;
}
}
filtered.length = j;
return filtered;
}
/**
* Return a promise that will always fulfill with an array containing
* the outcome states of all input promises. The returned promise
* will never reject.
* @param {Array} promises
* @returns {Promise} promise for array of settled state descriptors
*/
function settle(promises) {
return all(promises.map(settleOne));
}
function settleOne(p) {
var h = Promise._handler(p);
if(h.state() === 0) {
return toPromise(p).then(state.fulfilled, state.rejected);
}
h._unreport();
return state.inspect(h);
}
/**
* Traditional reduce function, similar to `Array.prototype.reduce()`, but
* input may contain promises and/or values, and reduceFunc
* may return either a value or a promise, *and* initialValue may
* be a promise for the starting value.
* @param {Array|Promise} promises array or promise for an array of anything,
* may contain a mix of promises and values.
* @param {function(accumulated:*, x:*, index:Number):*} f reduce function
* @returns {Promise} that will resolve to the final reduced value
*/
function reduce(promises, f /*, initialValue */) {
return arguments.length > 2 ? ar.call(promises, liftCombine(f), arguments[2])
: ar.call(promises, liftCombine(f));
}
/**
* Traditional reduce function, similar to `Array.prototype.reduceRight()`, but
* input may contain promises and/or values, and reduceFunc
* may return either a value or a promise, *and* initialValue may
* be a promise for the starting value.
* @param {Array|Promise} promises array or promise for an array of anything,
* may contain a mix of promises and values.
* @param {function(accumulated:*, x:*, index:Number):*} f reduce function
* @returns {Promise} that will resolve to the final reduced value
*/
function reduceRight(promises, f /*, initialValue */) {
return arguments.length > 2 ? arr.call(promises, liftCombine(f), arguments[2])
: arr.call(promises, liftCombine(f));
}
function liftCombine(f) {
return function(z, x, i) {
return applyFold(f, void 0, [z,x,i]);
};
}
};
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
(function(define) { 'use strict';
define(function() {
return function flow(Promise) {
var resolve = Promise.resolve;
var reject = Promise.reject;
var origCatch = Promise.prototype['catch'];
/**
* Handle the ultimate fulfillment value or rejection reason, and assume
* responsibility for all errors. If an error propagates out of result
* or handleFatalError, it will be rethrown to the host, resulting in a
* loud stack track on most platforms and a crash on some.
* @param {function?} onResult
* @param {function?} onError
* @returns {undefined}
*/
Promise.prototype.done = function(onResult, onError) {
this._handler.visit(this._handler.receiver, onResult, onError);
};
/**
* Add Error-type and predicate matching to catch. Examples:
* promise.catch(TypeError, handleTypeError)
* .catch(predicate, handleMatchedErrors)
* .catch(handleRemainingErrors)
* @param onRejected
* @returns {*}
*/
Promise.prototype['catch'] = Promise.prototype.otherwise = function(onRejected) {
if (arguments.length < 2) {
return origCatch.call(this, onRejected);
}
if(typeof onRejected !== 'function') {
return this.ensure(rejectInvalidPredicate);
}
return origCatch.call(this, createCatchFilter(arguments[1], onRejected));
};
/**
* Wraps the provided catch handler, so that it will only be called
* if the predicate evaluates truthy
* @param {?function} handler
* @param {function} predicate
* @returns {function} conditional catch handler
*/
function createCatchFilter(handler, predicate) {
return function(e) {
return evaluatePredicate(e, predicate)
? handler.call(this, e)
: reject(e);
};
}
/**
* Ensures that onFulfilledOrRejected will be called regardless of whether
* this promise is fulfilled or rejected. onFulfilledOrRejected WILL NOT
* receive the promises' value or reason. Any returned value will be disregarded.
* onFulfilledOrRejected may throw or return a rejected promise to signal
* an additional error.
* @param {function} handler handler to be called regardless of
* fulfillment or rejection
* @returns {Promise}
*/
Promise.prototype['finally'] = Promise.prototype.ensure = function(handler) {
if(typeof handler !== 'function') {
return this;
}
return this.then(function(x) {
return runSideEffect(handler, this, identity, x);
}, function(e) {
return runSideEffect(handler, this, reject, e);
});
};
function runSideEffect (handler, thisArg, propagate, value) {
var result = handler.call(thisArg);
return maybeThenable(result)
? propagateValue(result, propagate, value)
: propagate(value);
}
function propagateValue (result, propagate, x) {
return resolve(result).then(function () {
return propagate(x);
});
}
/**
* Recover from a failure by returning a defaultValue. If defaultValue
* is a promise, it's fulfillment value will be used. If defaultValue is
* a promise that rejects, the returned promise will reject with the
* same reason.
* @param {*} defaultValue
* @returns {Promise} new promise
*/
Promise.prototype['else'] = Promise.prototype.orElse = function(defaultValue) {
return this.then(void 0, function() {
return defaultValue;
});
};
/**
* Shortcut for .then(function() { return value; })
* @param {*} value
* @return {Promise} a promise that:
* - is fulfilled if value is not a promise, or
* - if value is a promise, will fulfill with its value, or reject
* with its reason.
*/
Promise.prototype['yield'] = function(value) {
return this.then(function() {
return value;
});
};
/**
* Runs a side effect when this promise fulfills, without changing the
* fulfillment value.
* @param {function} onFulfilledSideEffect
* @returns {Promise}
*/
Promise.prototype.tap = function(onFulfilledSideEffect) {
return this.then(onFulfilledSideEffect)['yield'](this);
};
return Promise;
};
function rejectInvalidPredicate() {
throw new TypeError('catch predicate must be a function');
}
function evaluatePredicate(e, predicate) {
return isError(predicate) ? e instanceof predicate : predicate(e);
}
function isError(predicate) {
return predicate === Error
|| (predicate != null && predicate.prototype instanceof Error);
}
function maybeThenable(x) {
return (typeof x === 'object' || typeof x === 'function') && x !== null;
}
function identity(x) {
return x;
}
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
/** @author Jeff Escalante */
(function(define) { 'use strict';
define(function() {
return function fold(Promise) {
Promise.prototype.fold = function(f, z) {
var promise = this._beget();
this._handler.fold(function(z, x, to) {
Promise._handler(z).fold(function(x, z, to) {
to.resolve(f.call(this, z, x));
}, x, this, to);
}, z, promise._handler.receiver, promise._handler);
return promise;
};
return Promise;
};
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
(function(define) { 'use strict';
define(function(require) {
var inspect = require('../state').inspect;
return function inspection(Promise) {
Promise.prototype.inspect = function() {
return inspect(Promise._handler(this));
};
return Promise;
};
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
(function(define) { 'use strict';
define(function() {
return function generate(Promise) {
var resolve = Promise.resolve;
Promise.iterate = iterate;
Promise.unfold = unfold;
return Promise;
/**
* @deprecated Use github.com/cujojs/most streams and most.iterate
* Generate a (potentially infinite) stream of promised values:
* x, f(x), f(f(x)), etc. until condition(x) returns true
* @param {function} f function to generate a new x from the previous x
* @param {function} condition function that, given the current x, returns
* truthy when the iterate should stop
* @param {function} handler function to handle the value produced by f
* @param {*|Promise} x starting value, may be a promise
* @return {Promise} the result of the last call to f before
* condition returns true
*/
function iterate(f, condition, handler, x) {
return unfold(function(x) {
return [x, f(x)];
}, condition, handler, x);
}
/**
* @deprecated Use github.com/cujojs/most streams and most.unfold
* Generate a (potentially infinite) stream of promised values
* by applying handler(generator(seed)) iteratively until
* condition(seed) returns true.
* @param {function} unspool function that generates a [value, newSeed]
* given a seed.
* @param {function} condition function that, given the current seed, returns
* truthy when the unfold should stop
* @param {function} handler function to handle the value produced by unspool
* @param x {*|Promise} starting value, may be a promise
* @return {Promise} the result of the last value produced by unspool before
* condition returns true
*/
function unfold(unspool, condition, handler, x) {
return resolve(x).then(function(seed) {
return resolve(condition(seed)).then(function(done) {
return done ? seed : resolve(unspool(seed)).spread(next);
});
});
function next(item, newSeed) {
return resolve(handler(item)).then(function() {
return unfold(unspool, condition, handler, newSeed);
});
}
}
};
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
(function(define) { 'use strict';
define(function() {
return function progress(Promise) {
/**
* @deprecated
* Register a progress handler for this promise
* @param {function} onProgress
* @returns {Promise}
*/
Promise.prototype.progress = function(onProgress) {
return this.then(void 0, void 0, onProgress);
};
return Promise;
};
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
(function(define) { 'use strict';
define(function(require) {
var env = require('../env');
var TimeoutError = require('../TimeoutError');
function setTimeout(f, ms, x, y) {
return env.setTimer(function() {
f(x, y, ms);
}, ms);
}
return function timed(Promise) {
/**
* Return a new promise whose fulfillment value is revealed only
* after ms milliseconds
* @param {number} ms milliseconds
* @returns {Promise}
*/
Promise.prototype.delay = function(ms) {
var p = this._beget();
this._handler.fold(handleDelay, ms, void 0, p._handler);
return p;
};
function handleDelay(ms, x, h) {
setTimeout(resolveDelay, ms, x, h);
}
function resolveDelay(x, h) {
h.resolve(x);
}
/**
* Return a new promise that rejects after ms milliseconds unless
* this promise fulfills earlier, in which case the returned promise
* fulfills with the same value.
* @param {number} ms milliseconds
* @param {Error|*=} reason optional rejection reason to use, defaults
* to a TimeoutError if not provided
* @returns {Promise}
*/
Promise.prototype.timeout = function(ms, reason) {
var p = this._beget();
var h = p._handler;
var t = setTimeout(onTimeout, ms, reason, p._handler);
this._handler.visit(h,
function onFulfill(x) {
env.clearTimer(t);
this.resolve(x); // this = h
},
function onReject(x) {
env.clearTimer(t);
this.reject(x); // this = h
},
h.notify);
return p;
};
function onTimeout(reason, h, ms) {
var e = typeof reason === 'undefined'
? new TimeoutError('timed out after ' + ms + 'ms')
: reason;
h.reject(e);
}
return Promise;
};
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
(function(define) { 'use strict';
define(function(require) {
var setTimer = require('../env').setTimer;
var format = require('../format');
return function unhandledRejection(Promise) {
var logError = noop;
var logInfo = noop;
var localConsole;
if(typeof console !== 'undefined') {
// Alias console to prevent things like uglify's drop_console option from
// removing console.log/error. Unhandled rejections fall into the same
// category as uncaught exceptions, and build tools shouldn't silence them.
localConsole = console;
logError = typeof localConsole.error !== 'undefined'
? function (e) { localConsole.error(e); }
: function (e) { localConsole.log(e); };
logInfo = typeof localConsole.info !== 'undefined'
? function (e) { localConsole.info(e); }
: function (e) { localConsole.log(e); };
}
Promise.onPotentiallyUnhandledRejection = function(rejection) {
enqueue(report, rejection);
};
Promise.onPotentiallyUnhandledRejectionHandled = function(rejection) {
enqueue(unreport, rejection);
};
Promise.onFatalRejection = function(rejection) {
enqueue(throwit, rejection.value);
};
var tasks = [];
var reported = [];
var running = null;
function report(r) {
if(!r.handled) {
reported.push(r);
logError('Potentially unhandled rejection [' + r.id + '] ' + format.formatError(r.value));
}
}
function unreport(r) {
var i = reported.indexOf(r);
if(i >= 0) {
reported.splice(i, 1);
logInfo('Handled previous rejection [' + r.id + '] ' + format.formatObject(r.value));
}
}
function enqueue(f, x) {
tasks.push(f, x);
if(running === null) {
running = setTimer(flush, 0);
}
}
function flush() {
running = null;
while(tasks.length > 0) {
tasks.shift()(tasks.shift());
}
}
return Promise;
};
function throwit(e) {
throw e;
}
function noop() {}
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
(function(define) { 'use strict';
define(function() {
return function addWith(Promise) {
/**
* Returns a promise whose handlers will be called with `this` set to
* the supplied receiver. Subsequent promises derived from the
* returned promise will also have their handlers called with receiver
* as `this`. Calling `with` with undefined or no arguments will return
* a promise whose handlers will again be called in the usual Promises/A+
* way (no `this`) thus safely undoing any previous `with` in the
* promise chain.
*
* WARNING: Promises returned from `with`/`withThis` are NOT Promises/A+
* compliant, specifically violating 2.2.5 (http://promisesaplus.com/#point-41)
*
* @param {object} receiver `this` value for all handlers attached to
* the returned promise.
* @returns {Promise}
*/
Promise.prototype['with'] = Promise.prototype.withThis = function(receiver) {
var p = this._beget();
var child = p._handler;
child.receiver = receiver;
this._handler.chain(child, receiver);
return p;
};
return Promise;
};
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
/*global process,document,setTimeout,clearTimeout,MutationObserver,WebKitMutationObserver*/
(function(define) { 'use strict';
define(function(require) {
/*jshint maxcomplexity:6*/
// Sniff "best" async scheduling option
// Prefer process.nextTick or MutationObserver, then check for
// setTimeout, and finally vertx, since its the only env that doesn't
// have setTimeout
var MutationObs;
var capturedSetTimeout = typeof setTimeout !== 'undefined' && setTimeout;
// Default env
var setTimer = function(f, ms) { return setTimeout(f, ms); };
var clearTimer = function(t) { return clearTimeout(t); };
var asap = function (f) { return capturedSetTimeout(f, 0); };
// Detect specific env
if (isNode()) { // Node
asap = function (f) { return process.nextTick(f); };
} else if (MutationObs = hasMutationObserver()) { // Modern browser
asap = initMutationObserver(MutationObs);
} else if (!capturedSetTimeout) { // vert.x
var vertxRequire = require;
var vertx = vertxRequire('vertx');
setTimer = function (f, ms) { return vertx.setTimer(ms, f); };
clearTimer = vertx.cancelTimer;
asap = vertx.runOnLoop || vertx.runOnContext;
}
return {
setTimer: setTimer,
clearTimer: clearTimer,
asap: asap
};
function isNode () {
return typeof process !== 'undefined' &&
Object.prototype.toString.call(process) === '[object process]';
}
function hasMutationObserver () {
return (typeof MutationObserver === 'function' && MutationObserver) ||
(typeof WebKitMutationObserver === 'function' && WebKitMutationObserver);
}
function initMutationObserver(MutationObserver) {
var scheduled;
var node = document.createTextNode('');
var o = new MutationObserver(run);
o.observe(node, { characterData: true });
function run() {
var f = scheduled;
scheduled = void 0;
f();
}
var i = 0;
return function (f) {
scheduled = f;
node.data = (i ^= 1);
};
}
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); }));
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
(function(define) { 'use strict';
define(function() {
return {
formatError: formatError,
formatObject: formatObject,
tryStringify: tryStringify
};
/**
* Format an error into a string. If e is an Error and has a stack property,
* it's returned. Otherwise, e is formatted using formatObject, with a
* warning added about e not being a proper Error.
* @param {*} e
* @returns {String} formatted string, suitable for output to developers
*/
function formatError(e) {
var s = typeof e === 'object' && e !== null && e.stack ? e.stack : formatObject(e);
return e instanceof Error ? s : s + ' (WARNING: non-Error used)';
}
/**
* Format an object, detecting "plain" objects and running them through
* JSON.stringify if possible.
* @param {Object} o
* @returns {string}
*/
function formatObject(o) {
var s = String(o);
if(s === '[object Object]' && typeof JSON !== 'undefined') {
s = tryStringify(o, s);
}
return s;
}
/**
* Try to return the result of JSON.stringify(x). If that fails, return
* defaultValue
* @param {*} x
* @param {*} defaultValue
* @returns {String|*} JSON.stringify(x) or defaultValue
*/
function tryStringify(x, defaultValue) {
try {
return JSON.stringify(x);
} catch(e) {
return defaultValue;
}
}
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
(function(define) { 'use strict';
define(function() {
return function makePromise(environment) {
var tasks = environment.scheduler;
var emitRejection = initEmitRejection();
var objectCreate = Object.create ||
function(proto) {
function Child() {}
Child.prototype = proto;
return new Child();
};
/**
* Create a promise whose fate is determined by resolver
* @constructor
* @returns {Promise} promise
* @name Promise
*/
function Promise(resolver, handler) {
this._handler = resolver === Handler ? handler : init(resolver);
}
/**
* Run the supplied resolver
* @param resolver
* @returns {Pending}
*/
function init(resolver) {
var handler = new Pending();
try {
resolver(promiseResolve, promiseReject, promiseNotify);
} catch (e) {
promiseReject(e);
}
return handler;
/**
* Transition from pre-resolution state to post-resolution state, notifying
* all listeners of the ultimate fulfillment or rejection
* @param {*} x resolution value
*/
function promiseResolve (x) {
handler.resolve(x);
}
/**
* Reject this promise with reason, which will be used verbatim
* @param {Error|*} reason rejection reason, strongly suggested
* to be an Error type
*/
function promiseReject (reason) {
handler.reject(reason);
}
/**
* @deprecated
* Issue a progress event, notifying all progress listeners
* @param {*} x progress event payload to pass to all listeners
*/
function promiseNotify (x) {
handler.notify(x);
}
}
// Creation
Promise.resolve = resolve;
Promise.reject = reject;
Promise.never = never;
Promise._defer = defer;
Promise._handler = getHandler;
/**
* Returns a trusted promise. If x is already a trusted promise, it is
* returned, otherwise returns a new trusted Promise which follows x.
* @param {*} x
* @return {Promise} promise
*/
function resolve(x) {
return isPromise(x) ? x
: new Promise(Handler, new Async(getHandler(x)));
}
/**
* Return a reject promise with x as its reason (x is used verbatim)
* @param {*} x
* @returns {Promise} rejected promise
*/
function reject(x) {
return new Promise(Handler, new Async(new Rejected(x)));
}
/**
* Return a promise that remains pending forever
* @returns {Promise} forever-pending promise.
*/
function never() {
return foreverPendingPromise; // Should be frozen
}
/**
* Creates an internal {promise, resolver} pair
* @private
* @returns {Promise}
*/
function defer() {
return new Promise(Handler, new Pending());
}
// Transformation and flow control
/**
* Transform this promise's fulfillment value, returning a new Promise
* for the transformed result. If the promise cannot be fulfilled, onRejected
* is called with the reason. onProgress *may* be called with updates toward
* this promise's fulfillment.
* @param {function=} onFulfilled fulfillment handler
* @param {function=} onRejected rejection handler
* @param {function=} onProgress @deprecated progress handler
* @return {Promise} new promise
*/
Promise.prototype.then = function(onFulfilled, onRejected, onProgress) {
var parent = this._handler;
var state = parent.join().state();
if ((typeof onFulfilled !== 'function' && state > 0) ||
(typeof onRejected !== 'function' && state < 0)) {
// Short circuit: value will not change, simply share handler
return new this.constructor(Handler, parent);
}
var p = this._beget();
var child = p._handler;
parent.chain(child, parent.receiver, onFulfilled, onRejected, onProgress);
return p;
};
/**
* If this promise cannot be fulfilled due to an error, call onRejected to
* handle the error. Shortcut for .then(undefined, onRejected)
* @param {function?} onRejected
* @return {Promise}
*/
Promise.prototype['catch'] = function(onRejected) {
return this.then(void 0, onRejected);
};
/**
* Creates a new, pending promise of the same type as this promise
* @private
* @returns {Promise}
*/
Promise.prototype._beget = function() {
return begetFrom(this._handler, this.constructor);
};
function begetFrom(parent, Promise) {
var child = new Pending(parent.receiver, parent.join().context);
return new Promise(Handler, child);
}
// Array combinators
Promise.all = all;
Promise.race = race;
Promise._traverse = traverse;
/**
* Return a promise that will fulfill when all promises in the
* input array have fulfilled, or will reject when one of the
* promises rejects.
* @param {array} promises array of promises
* @returns {Promise} promise for array of fulfillment values
*/
function all(promises) {
return traverseWith(snd, null, promises);
}
/**
* Array<Promise<X>> -> Promise<Array<f(X)>>
* @private
* @param {function} f function to apply to each promise's value
* @param {Array} promises array of promises
* @returns {Promise} promise for transformed values
*/
function traverse(f, promises) {
return traverseWith(tryCatch2, f, promises);
}
function traverseWith(tryMap, f, promises) {
var handler = typeof f === 'function' ? mapAt : settleAt;
var resolver = new Pending();
var pending = promises.length >>> 0;
var results = new Array(pending);
for (var i = 0, x; i < promises.length && !resolver.resolved; ++i) {
x = promises[i];
if (x === void 0 && !(i in promises)) {
--pending;
continue;
}
traverseAt(promises, handler, i, x, resolver);
}
if(pending === 0) {
resolver.become(new Fulfilled(results));
}
return new Promise(Handler, resolver);
function mapAt(i, x, resolver) {
if(!resolver.resolved) {
traverseAt(promises, settleAt, i, tryMap(f, x, i), resolver);
}
}
function settleAt(i, x, resolver) {
results[i] = x;
if(--pending === 0) {
resolver.become(new Fulfilled(results));
}
}
}
function traverseAt(promises, handler, i, x, resolver) {
if (maybeThenable(x)) {
var h = getHandlerMaybeThenable(x);
var s = h.state();
if (s === 0) {
h.fold(handler, i, void 0, resolver);
} else if (s > 0) {
handler(i, h.value, resolver);
} else {
resolver.become(h);
visitRemaining(promises, i+1, h);
}
} else {
handler(i, x, resolver);
}
}
Promise._visitRemaining = visitRemaining;
function visitRemaining(promises, start, handler) {
for(var i=start; i<promises.length; ++i) {
markAsHandled(getHandler(promises[i]), handler);
}
}
function markAsHandled(h, handler) {
if(h === handler) {
return;
}
var s = h.state();
if(s === 0) {
h.visit(h, void 0, h._unreport);
} else if(s < 0) {
h._unreport();
}
}
/**
* Fulfill-reject competitive race. Return a promise that will settle
* to the same state as the earliest input promise to settle.
*
* WARNING: The ES6 Promise spec requires that race()ing an empty array
* must return a promise that is pending forever. This implementation
* returns a singleton forever-pending promise, the same singleton that is
* returned by Promise.never(), thus can be checked with ===
*
* @param {array} promises array of promises to race
* @returns {Promise} if input is non-empty, a promise that will settle
* to the same outcome as the earliest input promise to settle. if empty
* is empty, returns a promise that will never settle.
*/
function race(promises) {
if(typeof promises !== 'object' || promises === null) {
return reject(new TypeError('non-iterable passed to race()'));
}
// Sigh, race([]) is untestable unless we return *something*
// that is recognizable without calling .then() on it.
return promises.length === 0 ? never()
: promises.length === 1 ? resolve(promises[0])
: runRace(promises);
}
function runRace(promises) {
var resolver = new Pending();
var i, x, h;
for(i=0; i<promises.length; ++i) {
x = promises[i];
if (x === void 0 && !(i in promises)) {
continue;
}
h = getHandler(x);
if(h.state() !== 0) {
resolver.become(h);
visitRemaining(promises, i+1, h);
break;
} else {
h.visit(resolver, resolver.resolve, resolver.reject);
}
}
return new Promise(Handler, resolver);
}
// Promise internals
// Below this, everything is @private
/**
* Get an appropriate handler for x, without checking for cycles
* @param {*} x
* @returns {object} handler
*/
function getHandler(x) {
if(isPromise(x)) {
return x._handler.join();
}
return maybeThenable(x) ? getHandlerUntrusted(x) : new Fulfilled(x);
}
/**
* Get a handler for thenable x.
* NOTE: You must only call this if maybeThenable(x) == true
* @param {object|function|Promise} x
* @returns {object} handler
*/
function getHandlerMaybeThenable(x) {
return isPromise(x) ? x._handler.join() : getHandlerUntrusted(x);
}
/**
* Get a handler for potentially untrusted thenable x
* @param {*} x
* @returns {object} handler
*/
function getHandlerUntrusted(x) {
try {
var untrustedThen = x.then;
return typeof untrustedThen === 'function'
? new Thenable(untrustedThen, x)
: new Fulfilled(x);
} catch(e) {
return new Rejected(e);
}
}
/**
* Handler for a promise that is pending forever
* @constructor
*/
function Handler() {}
Handler.prototype.when
= Handler.prototype.become
= Handler.prototype.notify // deprecated
= Handler.prototype.fail
= Handler.prototype._unreport
= Handler.prototype._report
= noop;
Handler.prototype._state = 0;
Handler.prototype.state = function() {
return this._state;
};
/**
* Recursively collapse handler chain to find the handler
* nearest to the fully resolved value.
* @returns {object} handler nearest the fully resolved value
*/
Handler.prototype.join = function() {
var h = this;
while(h.handler !== void 0) {
h = h.handler;
}
return h;
};
Handler.prototype.chain = function(to, receiver, fulfilled, rejected, progress) {
this.when({
resolver: to,
receiver: receiver,
fulfilled: fulfilled,
rejected: rejected,
progress: progress
});
};
Handler.prototype.visit = function(receiver, fulfilled, rejected, progress) {
this.chain(failIfRejected, receiver, fulfilled, rejected, progress);
};
Handler.prototype.fold = function(f, z, c, to) {
this.when(new Fold(f, z, c, to));
};
/**
* Handler that invokes fail() on any handler it becomes
* @constructor
*/
function FailIfRejected() {}
inherit(Handler, FailIfRejected);
FailIfRejected.prototype.become = function(h) {
h.fail();
};
var failIfRejected = new FailIfRejected();
/**
* Handler that manages a queue of consumers waiting on a pending promise
* @constructor
*/
function Pending(receiver, inheritedContext) {
Promise.createContext(this, inheritedContext);
this.consumers = void 0;
this.receiver = receiver;
this.handler = void 0;
this.resolved = false;
}
inherit(Handler, Pending);
Pending.prototype._state = 0;
Pending.prototype.resolve = function(x) {
this.become(getHandler(x));
};
Pending.prototype.reject = function(x) {
if(this.resolved) {
return;
}
this.become(new Rejected(x));
};
Pending.prototype.join = function() {
if (!this.resolved) {
return this;
}
var h = this;
while (h.handler !== void 0) {
h = h.handler;
if (h === this) {
return this.handler = cycle();
}
}
return h;
};
Pending.prototype.run = function() {
var q = this.consumers;
var handler = this.handler;
this.handler = this.handler.join();
this.consumers = void 0;
for (var i = 0; i < q.length; ++i) {
handler.when(q[i]);
}
};
Pending.prototype.become = function(handler) {
if(this.resolved) {
return;
}
this.resolved = true;
this.handler = handler;
if(this.consumers !== void 0) {
tasks.enqueue(this);
}
if(this.context !== void 0) {
handler._report(this.context);
}
};
Pending.prototype.when = function(continuation) {
if(this.resolved) {
tasks.enqueue(new ContinuationTask(continuation, this.handler));
} else {
if(this.consumers === void 0) {
this.consumers = [continuation];
} else {
this.consumers.push(continuation);
}
}
};
/**
* @deprecated
*/
Pending.prototype.notify = function(x) {
if(!this.resolved) {
tasks.enqueue(new ProgressTask(x, this));
}
};
Pending.prototype.fail = function(context) {
var c = typeof context === 'undefined' ? this.context : context;
this.resolved && this.handler.join().fail(c);
};
Pending.prototype._report = function(context) {
this.resolved && this.handler.join()._report(context);
};
Pending.prototype._unreport = function() {
this.resolved && this.handler.join()._unreport();
};
/**
* Wrap another handler and force it into a future stack
* @param {object} handler
* @constructor
*/
function Async(handler) {
this.handler = handler;
}
inherit(Handler, Async);
Async.prototype.when = function(continuation) {
tasks.enqueue(new ContinuationTask(continuation, this));
};
Async.prototype._report = function(context) {
this.join()._report(context);
};
Async.prototype._unreport = function() {
this.join()._unreport();
};
/**
* Handler that wraps an untrusted thenable and assimilates it in a future stack
* @param {function} then
* @param {{then: function}} thenable
* @constructor
*/
function Thenable(then, thenable) {
Pending.call(this);
tasks.enqueue(new AssimilateTask(then, thenable, this));
}
inherit(Pending, Thenable);
/**
* Handler for a fulfilled promise
* @param {*} x fulfillment value
* @constructor
*/
function Fulfilled(x) {
Promise.createContext(this);
this.value = x;
}
inherit(Handler, Fulfilled);
Fulfilled.prototype._state = 1;
Fulfilled.prototype.fold = function(f, z, c, to) {
runContinuation3(f, z, this, c, to);
};
Fulfilled.prototype.when = function(cont) {
runContinuation1(cont.fulfilled, this, cont.receiver, cont.resolver);
};
var errorId = 0;
/**
* Handler for a rejected promise
* @param {*} x rejection reason
* @constructor
*/
function Rejected(x) {
Promise.createContext(this);
this.id = ++errorId;
this.value = x;
this.handled = false;
this.reported = false;
this._report();
}
inherit(Handler, Rejected);
Rejected.prototype._state = -1;
Rejected.prototype.fold = function(f, z, c, to) {
to.become(this);
};
Rejected.prototype.when = function(cont) {
if(typeof cont.rejected === 'function') {
this._unreport();
}
runContinuation1(cont.rejected, this, cont.receiver, cont.resolver);
};
Rejected.prototype._report = function(context) {
tasks.afterQueue(new ReportTask(this, context));
};
Rejected.prototype._unreport = function() {
if(this.handled) {
return;
}
this.handled = true;
tasks.afterQueue(new UnreportTask(this));
};
Rejected.prototype.fail = function(context) {
this.reported = true;
emitRejection('unhandledRejection', this);
Promise.onFatalRejection(this, context === void 0 ? this.context : context);
};
function ReportTask(rejection, context) {
this.rejection = rejection;
this.context = context;
}
ReportTask.prototype.run = function() {
if(!this.rejection.handled && !this.rejection.reported) {
this.rejection.reported = true;
emitRejection('unhandledRejection', this.rejection) ||
Promise.onPotentiallyUnhandledRejection(this.rejection, this.context);
}
};
function UnreportTask(rejection) {
this.rejection = rejection;
}
UnreportTask.prototype.run = function() {
if(this.rejection.reported) {
emitRejection('rejectionHandled', this.rejection) ||
Promise.onPotentiallyUnhandledRejectionHandled(this.rejection);
}
};
// Unhandled rejection hooks
// By default, everything is a noop
Promise.createContext
= Promise.enterContext
= Promise.exitContext
= Promise.onPotentiallyUnhandledRejection
= Promise.onPotentiallyUnhandledRejectionHandled
= Promise.onFatalRejection
= noop;
// Errors and singletons
var foreverPendingHandler = new Handler();
var foreverPendingPromise = new Promise(Handler, foreverPendingHandler);
function cycle() {
return new Rejected(new TypeError('Promise cycle'));
}
// Task runners
/**
* Run a single consumer
* @constructor
*/
function ContinuationTask(continuation, handler) {
this.continuation = continuation;
this.handler = handler;
}
ContinuationTask.prototype.run = function() {
this.handler.join().when(this.continuation);
};
/**
* Run a queue of progress handlers
* @constructor
*/
function ProgressTask(value, handler) {
this.handler = handler;
this.value = value;
}
ProgressTask.prototype.run = function() {
var q = this.handler.consumers;
if(q === void 0) {
return;
}
for (var c, i = 0; i < q.length; ++i) {
c = q[i];
runNotify(c.progress, this.value, this.handler, c.receiver, c.resolver);
}
};
/**
* Assimilate a thenable, sending it's value to resolver
* @param {function} then
* @param {object|function} thenable
* @param {object} resolver
* @constructor
*/
function AssimilateTask(then, thenable, resolver) {
this._then = then;
this.thenable = thenable;
this.resolver = resolver;
}
AssimilateTask.prototype.run = function() {
var h = this.resolver;
tryAssimilate(this._then, this.thenable, _resolve, _reject, _notify);
function _resolve(x) { h.resolve(x); }
function _reject(x) { h.reject(x); }
function _notify(x) { h.notify(x); }
};
function tryAssimilate(then, thenable, resolve, reject, notify) {
try {
then.call(thenable, resolve, reject, notify);
} catch (e) {
reject(e);
}
}
/**
* Fold a handler value with z
* @constructor
*/
function Fold(f, z, c, to) {
this.f = f; this.z = z; this.c = c; this.to = to;
this.resolver = failIfRejected;
this.receiver = this;
}
Fold.prototype.fulfilled = function(x) {
this.f.call(this.c, this.z, x, this.to);
};
Fold.prototype.rejected = function(x) {
this.to.reject(x);
};
Fold.prototype.progress = function(x) {
this.to.notify(x);
};
// Other helpers
/**
* @param {*} x
* @returns {boolean} true iff x is a trusted Promise
*/
function isPromise(x) {
return x instanceof Promise;
}
/**
* Test just enough to rule out primitives, in order to take faster
* paths in some code
* @param {*} x
* @returns {boolean} false iff x is guaranteed *not* to be a thenable
*/
function maybeThenable(x) {
return (typeof x === 'object' || typeof x === 'function') && x !== null;
}
function runContinuation1(f, h, receiver, next) {
if(typeof f !== 'function') {
return next.become(h);
}
Promise.enterContext(h);
tryCatchReject(f, h.value, receiver, next);
Promise.exitContext();
}
function runContinuation3(f, x, h, receiver, next) {
if(typeof f !== 'function') {
return next.become(h);
}
Promise.enterContext(h);
tryCatchReject3(f, x, h.value, receiver, next);
Promise.exitContext();
}
/**
* @deprecated
*/
function runNotify(f, x, h, receiver, next) {
if(typeof f !== 'function') {
return next.notify(x);
}
Promise.enterContext(h);
tryCatchReturn(f, x, receiver, next);
Promise.exitContext();
}
function tryCatch2(f, a, b) {
try {
return f(a, b);
} catch(e) {
return reject(e);
}
}
/**
* Return f.call(thisArg, x), or if it throws return a rejected promise for
* the thrown exception
*/
function tryCatchReject(f, x, thisArg, next) {
try {
next.become(getHandler(f.call(thisArg, x)));
} catch(e) {
next.become(new Rejected(e));
}
}
/**
* Same as above, but includes the extra argument parameter.
*/
function tryCatchReject3(f, x, y, thisArg, next) {
try {
f.call(thisArg, x, y, next);
} catch(e) {
next.become(new Rejected(e));
}
}
/**
* @deprecated
* Return f.call(thisArg, x), or if it throws, *return* the exception
*/
function tryCatchReturn(f, x, thisArg, next) {
try {
next.notify(f.call(thisArg, x));
} catch(e) {
next.notify(e);
}
}
function inherit(Parent, Child) {
Child.prototype = objectCreate(Parent.prototype);
Child.prototype.constructor = Child;
}
function snd(x, y) {
return y;
}
function noop() {}
function initEmitRejection() {
/*global process, self, CustomEvent*/
if(typeof process !== 'undefined' && process !== null
&& typeof process.emit === 'function') {
// Returning falsy here means to call the default
// onPotentiallyUnhandledRejection API. This is safe even in
// browserify since process.emit always returns falsy in browserify:
// https://github.com/defunctzombie/node-process/blob/master/browser.js#L40-L46
return function(type, rejection) {
return type === 'unhandledRejection'
? process.emit(type, rejection.value, rejection)
: process.emit(type, rejection);
};
} else if(typeof self !== 'undefined' && typeof CustomEvent === 'function') {
return (function(noop, self, CustomEvent) {
var hasCustomEvent = false;
try {
var ev = new CustomEvent('unhandledRejection');
hasCustomEvent = ev instanceof CustomEvent;
} catch (e) {}
return !hasCustomEvent ? noop : function(type, rejection) {
var ev = new CustomEvent(type, {
detail: {
reason: rejection.value,
key: rejection
},
bubbles: false,
cancelable: true
});
return !self.dispatchEvent(ev);
};
}(noop, self, CustomEvent));
}
return noop;
}
return Promise;
};
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */
(function(define) { 'use strict';
define(function() {
return {
pending: toPendingState,
fulfilled: toFulfilledState,
rejected: toRejectedState,
inspect: inspect
};
function toPendingState() {
return { state: 'pending' };
}
function toRejectedState(e) {
return { state: 'rejected', reason: e };
}
function toFulfilledState(x) {
return { state: 'fulfilled', value: x };
}
function inspect(handler) {
var state = handler.state();
return state === 0 ? toPendingState()
: state > 0 ? toFulfilledState(handler.value)
: toRejectedState(handler.value);
}
});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
/** @license MIT License (c) copyright 2010-2014 original author or authors */
/**
* Promises/A+ and when() implementation
* when is part of the cujoJS family of libraries (http://cujojs.com/)
* @author Brian Cavalier
* @author John Hann
*/
(function(define) { 'use strict';
define(function (require) {
var timed = require('./lib/decorators/timed');
var array = require('./lib/decorators/array');
var flow = require('./lib/decorators/flow');
var fold = require('./lib/decorators/fold');
var inspect = require('./lib/decorators/inspect');
var generate = require('./lib/decorators/iterate');
var progress = require('./lib/decorators/progress');
var withThis = require('./lib/decorators/with');
var unhandledRejection = require('./lib/decorators/unhandledRejection');
var TimeoutError = require('./lib/TimeoutError');
var Promise = [array, flow, fold, generate, progress,
inspect, withThis, timed, unhandledRejection]
.reduce(function(Promise, feature) {
return feature(Promise);
}, require('./lib/Promise'));
var apply = require('./lib/apply')(Promise);
// Public API
when.promise = promise; // Create a pending promise
when.resolve = Promise.resolve; // Create a resolved promise
when.reject = Promise.reject; // Create a rejected promise
when.lift = lift; // lift a function to return promises
when['try'] = attempt; // call a function and return a promise
when.attempt = attempt; // alias for when.try
when.iterate = Promise.iterate; // DEPRECATED (use cujojs/most streams) Generate a stream of promises
when.unfold = Promise.unfold; // DEPRECATED (use cujojs/most streams) Generate a stream of promises
when.join = join; // Join 2 or more promises
when.all = all; // Resolve a list of promises
when.settle = settle; // Settle a list of promises
when.any = lift(Promise.any); // One-winner race
when.some = lift(Promise.some); // Multi-winner race
when.race = lift(Promise.race); // First-to-settle race
when.map = map; // Array.map() for promises
when.filter = filter; // Array.filter() for promises
when.reduce = lift(Promise.reduce); // Array.reduce() for promises
when.reduceRight = lift(Promise.reduceRight); // Array.reduceRight() for promises
when.isPromiseLike = isPromiseLike; // Is something promise-like, aka thenable
when.Promise = Promise; // Promise constructor
when.defer = defer; // Create a {promise, resolve, reject} tuple
// Error types
when.TimeoutError = TimeoutError;
/**
* Get a trusted promise for x, or by transforming x with onFulfilled
*
* @param {*} x
* @param {function?} onFulfilled callback to be called when x is
* successfully fulfilled. If promiseOrValue is an immediate value, callback
* will be invoked immediately.
* @param {function?} onRejected callback to be called when x is
* rejected.
* @param {function?} onProgress callback to be called when progress updates
* are issued for x. @deprecated
* @returns {Promise} a new promise that will fulfill with the return
* value of callback or errback or the completion value of promiseOrValue if
* callback and/or errback is not supplied.
*/
function when(x, onFulfilled, onRejected, onProgress) {
var p = Promise.resolve(x);
if (arguments.length < 2) {
return p;
}
return p.then(onFulfilled, onRejected, onProgress);
}
/**
* Creates a new promise whose fate is determined by resolver.
* @param {function} resolver function(resolve, reject, notify)
* @returns {Promise} promise whose fate is determine by resolver
*/
function promise(resolver) {
return new Promise(resolver);
}
/**
* Lift the supplied function, creating a version of f that returns
* promises, and accepts promises as arguments.
* @param {function} f
* @returns {Function} version of f that returns promises
*/
function lift(f) {
return function() {
for(var i=0, l=arguments.length, a=new Array(l); i<l; ++i) {
a[i] = arguments[i];
}
return apply(f, this, a);
};
}
/**
* Call f in a future turn, with the supplied args, and return a promise
* for the result.
* @param {function} f
* @returns {Promise}
*/
function attempt(f /*, args... */) {
/*jshint validthis:true */
for(var i=0, l=arguments.length-1, a=new Array(l); i<l; ++i) {
a[i] = arguments[i+1];
}
return apply(f, this, a);
}
/**
* Creates a {promise, resolver} pair, either or both of which
* may be given out safely to consumers.
* @return {{promise: Promise, resolve: function, reject: function, notify: function}}
*/
function defer() {
return new Deferred();
}
function Deferred() {
var p = Promise._defer();
function resolve(x) { p._handler.resolve(x); }
function reject(x) { p._handler.reject(x); }
function notify(x) { p._handler.notify(x); }
this.promise = p;
this.resolve = resolve;
this.reject = reject;
this.notify = notify;
this.resolver = { resolve: resolve, reject: reject, notify: notify };
}
/**
* Determines if x is promise-like, i.e. a thenable object
* NOTE: Will return true for *any thenable object*, and isn't truly
* safe, since it may attempt to access the `then` property of x (i.e.
* clever/malicious getters may do weird things)
* @param {*} x anything
* @returns {boolean} true if x is promise-like
*/
function isPromiseLike(x) {
return x && typeof x.then === 'function';
}
/**
* Return a promise that will resolve only once all the supplied arguments
* have resolved. The resolution value of the returned promise will be an array
* containing the resolution values of each of the arguments.
* @param {...*} arguments may be a mix of promises and values
* @returns {Promise}
*/
function join(/* ...promises */) {
return Promise.all(arguments);
}
/**
* Return a promise that will fulfill once all input promises have
* fulfilled, or reject when any one input promise rejects.
* @param {array|Promise} promises array (or promise for an array) of promises
* @returns {Promise}
*/
function all(promises) {
return when(promises, Promise.all);
}
/**
* Return a promise that will always fulfill with an array containing
* the outcome states of all input promises. The returned promise
* will only reject if `promises` itself is a rejected promise.
* @param {array|Promise} promises array (or promise for an array) of promises
* @returns {Promise} promise for array of settled state descriptors
*/
function settle(promises) {
return when(promises, Promise.settle);
}
/**
* Promise-aware array map function, similar to `Array.prototype.map()`,
* but input array may contain promises or values.
* @param {Array|Promise} promises array of anything, may contain promises and values
* @param {function(x:*, index:Number):*} mapFunc map function which may
* return a promise or value
* @returns {Promise} promise that will fulfill with an array of mapped values
* or reject if any input promise rejects.
*/
function map(promises, mapFunc) {
return when(promises, function(promises) {
return Promise.map(promises, mapFunc);
});
}
/**
* Filter the provided array of promises using the provided predicate. Input may
* contain promises and values
* @param {Array|Promise} promises array of promises and values
* @param {function(x:*, index:Number):boolean} predicate filtering predicate.
* Must return truthy (or promise for truthy) for items to retain.
* @returns {Promise} promise that will fulfill with an array containing all items
* for which predicate returned truthy.
*/
function filter(promises, predicate) {
return when(promises, function(promises) {
return Promise.filter(promises, predicate);
});
}
return when;
});
})(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); });
{
"private": true,
"dependencies": {
"jquery": "^2",
"when": "^3.4",
"poly": "git://github.com/cujojs/poly#0.6.0",
"requirejs": "^2.1",
"todomvc-app-css": "^2",
"todomvc-common": "^1"
},
"devDependencies": {
"napa": "^1.2.0"
},
"scripts": {
"install": "napa"
},
"napa": {
"mu-error": "mu-lib/mu-error#0.0.1",
"mu-emitter": "mu-lib/mu-emitter#0.1.0",
"mu-getargs": "mu-lib/mu-getargs#0.0.3",
"mu-merge": "mu-lib/mu-merge#0.0.2",
"mu-selector-set": "mu-lib/mu-selector-set#1.1.0",
"mu-jquery-destroy": "mu-lib/mu-jquery-destroy#0.0.2",
"mu-jquery-hashchange": "mu-lib/mu-jquery-hashchange#0.0.1",
"troopjs": "troopjs/troopjs#3.0.0-rc.7",
"troopjs-route-hash": "troopjs/troopjs-route-hash#1.0.0-rc.3",
"troopjs-widget": "troopjs/troopjs-widget#1.0.0-rc.5"
}
}
# TroopJS TodoMVC Example # TroopJS • [TodoMVC](http://todomvc.com)
> The simple js framework that does as little as possible, then stays out of the way. > The minimalist anti-framework that does as little as possible, then stays out of the way.
> _[TroopJS - troopjs.com](http://troopjs.com)_ ## Resources
- [Website](http://troopjs.com)
- [Documentation](https://cdn.rawgit.com/troopjs/troopjs/build/3.x/docs/index.html)
- [Blog](http://blog.troopjs.com)
- [GitHub](http:/github.com/troopjs/)
## Learning TroopJS ### Support
The [TroopJS website](http://troopjs.com) is a great resource for getting started. - [Issues](http:/github.com/troopjs/troopjs/issues)
Here are some links you may find helpful: *Let us [know](https://github.com/tastejs/todomvc/issues) if you discover anything worth sharing.*
* [TroopJS on GitHub](https://github.com/troopjs)
_If you have other helpful links to share, or find any of the links above no longer work, please [let us know](https://github.com/tastejs/todomvc/issues)._ ## Credit
Created by [Mikael Karon](http://mikael.karon.se)
...@@ -78,15 +78,17 @@ list.forEach(function (framework) { ...@@ -78,15 +78,17 @@ list.forEach(function (framework) {
assert: function (after, initial) { assert: function (after, initial) {
var nodeIncrease = (after.nodes - initial.nodes); var nodeIncrease = (after.nodes - initial.nodes);
var listenerIncrease = (after.jsEventListeners - initial.jsEventListeners); var listenerIncrease = (after.jsEventListeners - initial.jsEventListeners);
var memoryExceptions = exceptions[framework.name] || {};
console.log(this + ', ' + nodeIncrease + ', ' + console.log(this + ', ' + nodeIncrease + ', ' +
(after.jsHeapSizeUsed - initial.jsHeapSizeUsed) + ', ' + listenerIncrease); (after.jsHeapSizeUsed - initial.jsHeapSizeUsed) + ', ' + listenerIncrease);
//https://code.google.com/p/chromium/issues/detail?id=516153 //https://code.google.com/p/chromium/issues/detail?id=516153
if (nodeIncrease > exceptions[framework.name].nodes || 0) { if (nodeIncrease > memoryExceptions.nodes || 0) {
throw new Error('Node Count leak detected!'); throw new Error('Node Count leak detected!');
} }
if (listenerIncrease > exceptions[framework.name].listeners || 0) { if (listenerIncrease > memoryExceptions.listeners || 0) {
throw new Error('Event Listener leak detected!'); throw new Error('Event Listener leak detected!');
} }
......
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