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 */
#footer, #main {
display: none;
}
.footer,
.main,
.clear-completed,
.filter-active .completed,
.filter-completed .active {
display: none;
......
......@@ -2,76 +2,56 @@
<html lang="en" data-framework="troopjs">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<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">
</head>
<body>
<section id="todoapp">
<header id="header">
<h1>todos</h1>
<input id="new-todo" placeholder="What needs to be done?" autofocus data-weave="troopjs-todos/widget/create">
</header>
<section id="main" data-weave="troopjs-todos/widget/display">
<input id="toggle-all" type="checkbox" data-weave="troopjs-todos/widget/mark">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list" data-weave="troopjs-todos/widget/list"></ul>
<section class="todoapp">
<header class="header">
<h1>todos</h1>
<input class="new-todo" placeholder="What needs to be done?" autofocus data-weave="todos-troopjs/create">
</header>
<section class="main" data-weave="todos-troopjs/hide">
<input class="toggle-all" type="checkbox" data-weave="todos-troopjs/toggle">
<label for="toggle-all">Mark all as complete</label>
<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>
<footer class="footer" data-weave="todos-troopjs/hide">
<span class="todo-count" data-weave="todos-troopjs/count"><strong>0</strong> items left</span>
<ul class="filters" data-weave="todos-troopjs/filter">
<li>
<a class="selected" href="#/">All</a>
</li>
<li>
<a href="#/active">Active</a>
</li>
<li>
<a href="#/completed">Completed</a>
</li>
</ul>
<button class="clear-completed" data-weave="todos-troopjs/clear">Clear completed</button>
</footer>
</section>
<footer id="footer" data-weave="troopjs-todos/widget/display">
<span id="todo-count" data-weave="troopjs-todos/widget/count"><strong>1</strong> item left</span>
<ul id="filters" data-weave="troopjs-todos/widget/filters">
<li>
<a class="selected" href="#/">All</a>
</li>
<li>
<a href="#/active">Active</a>
</li>
<li>
<a href="#/completed">Completed</a>
</li>
</ul>
<button id="clear-completed" data-weave="troopjs-todos/widget/clear">Clear completed</button>
<footer class="info">
<p>Double-click to edit a todo</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>
</footer>
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Created by <a href="https://github.com/mikaelkaron">Mikael Karon</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<script src="bower_components/todomvc-common/base.js"></script>
<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>
<script src="node_modules/todomvc-common/base.js"></script>
<script src="node_modules/requirejs/require.js" data-main="index.js"></script>
</body>
</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
* @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.
* see: http://github.com/jrburke/requirejs for details
*/
......@@ -12,7 +12,7 @@ var requirejs, require, define;
(function (global) {
var req, s, head, baseElement, dataMain, src,
interactiveScript, currentlyAddingScript, mainScript, subPath,
version = '2.1.6',
version = '2.1.20',
commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
jsSuffixRegExp = /\.js$/,
......@@ -21,8 +21,7 @@ var requirejs, require, define;
ostring = op.toString,
hasOwn = op.hasOwnProperty,
ap = Array.prototype,
apsp = ap.splice,
isBrowser = !!(typeof window !== 'undefined' && navigator && window.document),
isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document),
isWebWorker = !isBrowser && typeof importScripts !== 'undefined',
//PS3 indicates loaded and complete, but need to wait for complete
//specifically. Sequence is 'loading', 'loaded', execution,
......@@ -108,7 +107,10 @@ var requirejs, require, define;
if (source) {
eachProp(source, function (value, 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]) {
target[prop] = {};
}
......@@ -138,7 +140,7 @@ var requirejs, require, define;
throw err;
}
//Allow getting a global that expressed in
//Allow getting a global that is expressed in
//dot notation, like 'a.b.c'.
function getGlobal(value) {
if (!value) {
......@@ -177,7 +179,7 @@ var requirejs, require, define;
if (typeof requirejs !== 'undefined') {
if (isFunction(requirejs)) {
//Do not overwrite and existing requirejs instance.
//Do not overwrite an existing requirejs instance.
return;
}
cfg = requirejs;
......@@ -201,6 +203,7 @@ var requirejs, require, define;
waitSeconds: 7,
baseUrl: './',
paths: {},
bundles: {},
pkgs: {},
shim: {},
config: {}
......@@ -214,6 +217,7 @@ var requirejs, require, define;
defQueue = [],
defined = {},
urlFetched = {},
bundlesMap = {},
requireCounter = 1,
unnormalizedCounter = 1;
......@@ -228,20 +232,19 @@ var requirejs, require, define;
*/
function trimDots(ary) {
var i, part;
for (i = 0; ary[i]; i += 1) {
for (i = 0; i < ary.length; i++) {
part = ary[i];
if (part === '.') {
ary.splice(i, 1);
i -= 1;
} else if (part === '..') {
if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
//End of the line. Keep at least one non-dot
//path segment at the front so it can be mapped
//correctly to disk. Otherwise, there is likely
//no path mapping for a path starting with '..'.
//This can still fail, but catches the most reasonable
//uses of ..
break;
// If at the start, or previous value is still ..,
// keep them so that when converted to a path it may
// still work when converted to a path, even though
// as an ID it is less than ideal. In larger point
// releases, may be better to just kick out an error.
if (i === 0 || (i === 1 && ary[2] === '..') || ary[i - 1] === '..') {
continue;
} else if (i > 0) {
ary.splice(i - 1, 2);
i -= 2;
......@@ -261,54 +264,45 @@ var requirejs, require, define;
* @returns {String} normalized name
*/
function normalize(name, baseName, applyMap) {
var pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment,
foundMap, foundI, foundStarMap, starI,
baseParts = baseName && baseName.split('/'),
normalizedBaseParts = baseParts,
var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex,
foundMap, foundI, foundStarMap, starI, normalizedBaseParts,
baseParts = (baseName && baseName.split('/')),
map = config.map,
starMap = map && map['*'];
//Adjust any relative paths.
if (name && name.charAt(0) === '.') {
//If have a base name, try to normalize against it,
//otherwise, assume it is a top-level require that will
//be relative to baseUrl in the end.
if (baseName) {
if (getOwn(config.pkgs, baseName)) {
//If the baseName is a package name, then just treat it as one
//name to concat the name with.
normalizedBaseParts = baseParts = [baseName];
} else {
//Convert baseName to array, and lop off the last part,
//so that . matches that 'directory' and not name of the baseName's
//module. For instance, baseName of 'one/two/three', maps to
//'one/two/three.js', but we want the directory, 'one/two' for
//this normalization.
normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
}
name = normalizedBaseParts.concat(name.split('/'));
trimDots(name);
if (name) {
name = name.split('/');
lastIndex = name.length - 1;
// If wanting node ID compatibility, strip .js from end
// of IDs. Have to do this here, and not in nameToUrl
// because node allows either .js or non .js to map
// to same file.
if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
}
//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('/');
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);
// Starts with a '.' so need the baseName
if (name[0].charAt(0) === '.' && baseParts) {
//Convert baseName to array, and lop off the last part,
//so that . matches that 'directory' and not name of the baseName's
//module. For instance, baseName of 'one/two/three', maps to
//'one/two/three.js', but we want the directory, 'one/two' for
//this normalization.
normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
name = normalizedBaseParts.concat(name);
}
trimDots(name);
name = name.join('/');
}
//Apply map config if available.
if (applyMap && map && (baseParts || starMap)) {
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('/');
if (baseParts) {
......@@ -325,16 +319,12 @@ var requirejs, require, define;
//Match, update name to the new value.
foundMap = mapValue;
foundI = i;
break;
break outerLoop;
}
}
}
}
if (foundMap) {
break;
}
//Check for a star map match, but just hold on to it,
//if there is a shorter segment match later in a matching
//config, then favor over this star map.
......@@ -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) {
......@@ -373,12 +367,17 @@ var requirejs, require, define;
function hasPathFallback(id) {
var pathConfig = getOwn(config.paths, id);
if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) {
removeScript(id);
//Pop off the first array value, since it failed, and
//retry
pathConfig.shift();
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;
}
}
......@@ -444,7 +443,16 @@ var requirejs, require, define;
return normalize(name, parentName, applyMap);
});
} 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 {
//A regular module.
......@@ -545,11 +553,13 @@ var requirejs, require, define;
function takeGlobalQueue() {
//Push all the globalDefQueue items into the context's defQueue
if (globalDefQueue.length) {
//Array splice in the values since the context code has a
//local var ref to defQueue, so cannot just reassign the one
//on context.
apsp.apply(defQueue,
[defQueue.length - 1, 0].concat(globalDefQueue));
each(globalDefQueue, function(queueItem) {
var id = queueItem[0];
if (typeof id === 'string') {
context.defQueueMap[id] = true;
}
defQueue.push(queueItem);
});
globalDefQueue = [];
}
}
......@@ -566,7 +576,7 @@ var requirejs, require, define;
mod.usingExports = true;
if (mod.map.isDefine) {
if (mod.exports) {
return mod.exports;
return (defined[mod.map.id] = mod.exports);
} else {
return (mod.exports = defined[mod.map.id] = {});
}
......@@ -580,15 +590,9 @@ var requirejs, require, define;
id: mod.map.id,
uri: mod.map.url,
config: function () {
var c,
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 || {};
return getOwn(config.config, mod.map.id) || {};
},
exports: defined[mod.map.id]
exports: mod.exports || (mod.exports = {})
});
}
}
......@@ -629,7 +633,7 @@ var requirejs, require, define;
}
function checkLoaded() {
var map, modId, err, usingPathFallback,
var err, usingPathFallback,
waitInterval = config.waitSeconds * 1000,
//It is possible to disable the wait interval by using waitSeconds of 0.
expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(),
......@@ -647,8 +651,8 @@ var requirejs, require, define;
//Figure out the state of all the modules.
eachProp(enabledRegistry, function (mod) {
map = mod.map;
modId = map.id;
var map = mod.map,
modId = map.id;
//Skip things that are not enabled or in error state.
if (!mod.enabled) {
......@@ -842,7 +846,10 @@ var requirejs, require, define;
factory = this.factory;
if (!this.inited) {
this.fetch();
// Only fetch if not already in the defQueue.
if (!hasProp(context.defQueueMap, id)) {
this.fetch();
}
} else if (this.error) {
this.emit('error', this.error);
} else if (!this.defining) {
......@@ -871,17 +878,14 @@ var requirejs, require, define;
exports = context.execCb(id, factory, depExports, exports);
}
if (this.map.isDefine) {
//If setting exports via 'module' is in play,
//favor that over return value and exports. After that,
//favor a non-undefined return value over exports use.
// Favor return value over exports. If node/cjs in play,
// then will not have a return value anyway. Favor
// module.exports assignment over exports object.
if (this.map.isDefine && exports === undefined) {
cjsModule = this.module;
if (cjsModule &&
cjsModule.exports !== undefined &&
//Make sure it is not already the exports value
cjsModule.exports !== this.exports) {
if (cjsModule) {
exports = cjsModule.exports;
} else if (exports === undefined && this.usingExports) {
} else if (this.usingExports) {
//exports already set the defined value.
exports = this.exports;
}
......@@ -941,6 +945,7 @@ var requirejs, require, define;
on(pluginMap, 'defined', bind(this, function (plugin) {
var load, normalizedMap, normalizedMod,
bundleId = getOwn(bundlesMap, this.map.id),
name = this.map.name,
parentName = this.map.parentMap ? this.map.parentMap.name : null,
localRequire = context.makeRequire(map.parentMap, {
......@@ -986,6 +991,14 @@ var requirejs, require, define;
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) {
this.init([], function () { return value; }, null, {
enabled: true
......@@ -1108,12 +1121,22 @@ var requirejs, require, define;
this.depCount += 1;
on(depMap, 'defined', bind(this, function (depExports) {
if (this.undefed) {
return;
}
this.defineDep(i, depExports);
this.check();
}));
if (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;
while (defQueue.length) {
args = defQueue.shift();
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 {
//args are id, deps, factory. Should be normalized by the
//define() function.
callGetModule(args);
}
}
context.defQueueMap = {};
}
context = {
......@@ -1233,6 +1258,7 @@ var requirejs, require, define;
defined: defined,
urlFetched: urlFetched,
defQueue: defQueue,
defQueueMap: {},
Module: Module,
makeModuleMap: makeModuleMap,
nextTick: req.nextTick,
......@@ -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.
var pkgs = config.pkgs,
shim = config.shim,
var shim = config.shim,
objs = {
paths: true,
bundles: true,
config: true,
map: true
};
eachProp(cfg, function (value, prop) {
if (objs[prop]) {
if (prop === 'map') {
if (!config.map) {
config.map = {};
}
mixin(config[prop], value, true, true);
} else {
mixin(config[prop], value, true);
if (!config[prop]) {
config[prop] = {};
}
mixin(config[prop], value, true, true);
} else {
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
if (cfg.shim) {
eachProp(cfg.shim, function (value, id) {
......@@ -1295,29 +1328,25 @@ var requirejs, require, define;
//Adjust packages if necessary.
if (cfg.packages) {
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;
if (location) {
config.paths[name] = pkgObj.location;
}
//Create a brand new object on pkgs, since currentPackages can
//be passed in again, and config.pkgs is the internal transformed
//state for all package configs.
pkgs[pkgObj.name] = {
name: pkgObj.name,
location: location || pkgObj.name,
//Remove leading dot in main, so main paths are normalized,
//and remove any trailing .js, since different package
//envs have different conventions: some use a module name,
//some use a file name.
main: (pkgObj.main || 'main')
.replace(currDirRegExp, '')
.replace(jsSuffixRegExp, '')
};
//Save pointer to main module ID for pkg name.
//Remove leading dot in main, so main paths are normalized,
//and remove any trailing .js, since different package
//envs have different conventions: some use a module name,
//some use a file name.
config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main')
.replace(currDirRegExp, '')
.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,
......@@ -1328,7 +1357,7 @@ var requirejs, require, define;
//late to modify them, and ignore unnormalized ones
//since they are transient.
if (!mod.inited && !mod.map.unnormalized) {
mod.map = makeModuleMap(id);
mod.map = makeModuleMap(id, null, true);
}
});
......@@ -1464,10 +1493,23 @@ var requirejs, require, define;
var map = makeModuleMap(id, relMap, true),
mod = getOwn(registry, id);
mod.undefed = true;
removeScript(id);
delete defined[id];
delete urlFetched[map.url];
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) {
//Hold on to listeners in case the
//module will be attempted to be reloaded
......@@ -1487,7 +1529,7 @@ var requirejs, require, define;
/**
* Called to enable a module if it is still in the registry
* 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.
*/
enable: function (depMap) {
......@@ -1528,6 +1570,7 @@ var requirejs, require, define;
callGetModule(args);
}
context.defQueueMap = {};
//Do this after the cycle of callGetModule in case the result
//of those calls/init calls changes the registry.
......@@ -1561,8 +1604,19 @@ var requirejs, require, define;
* internal API, not a public one. Use toUrl for the public API.
*/
nameToUrl: function (moduleName, ext, skipExt) {
var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url,
parentPath;
var paths, syms, i, parentModule, url,
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
//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;
} else {
//A module that needs to be converted to a path.
paths = config.paths;
pkgs = config.pkgs;
syms = moduleName.split('/');
//For each module name segment, see if there is a path
......@@ -1584,7 +1637,7 @@ var requirejs, require, define;
//and work up from it.
for (i = syms.length; i > 0; i -= 1) {
parentModule = syms.slice(0, i).join('/');
pkg = getOwn(pkgs, parentModule);
parentPath = getOwn(paths, parentModule);
if (parentPath) {
//If an array, it means there are a few choices,
......@@ -1594,22 +1647,12 @@ var requirejs, require, define;
}
syms.splice(0, i, parentPath);
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.
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;
}
......@@ -1794,6 +1837,19 @@ var requirejs, require, define;
*/
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.
* Make this a separate function to allow other environments
......@@ -1808,12 +1864,10 @@ var requirejs, require, define;
node;
if (isBrowser) {
//In the browser so use a script tag
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;
node = req.createNode(config, moduleName, url);
if (config.onNodeCreated) {
config.onNodeCreated(node, config, moduleName, url);
}
node.setAttribute('data-requirecontext', context.contextName);
node.setAttribute('data-requiremodule', moduleName);
......@@ -1910,7 +1964,7 @@ var requirejs, require, define;
}
//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.
eachReverse(scripts(), function (script) {
//Set the 'head' where we can append children by
......@@ -1942,7 +1996,7 @@ var requirejs, require, define;
//like a module name.
mainScript = mainScript.replace(jsSuffixRegExp, '');
//If mainScript is still a path, fall back to dataMain
//If mainScript is still a path, fall back to dataMain
if (req.jsExtRegExp.test(mainScript)) {
mainScript = dataMain;
}
......@@ -2021,14 +2075,18 @@ var requirejs, require, define;
//where the module name is not known until the script onload event
//occurs. If no context, use the global queue, and get it processed
//in the onscript load callback.
(context ? context.defQueue : globalDefQueue).push([name, deps, callback]);
if (context) {
context.defQueue.push([name, deps, callback]);
context.defQueueMap[name] = true;
} else {
globalDefQueue.push([name, deps, callback]);
}
};
define.amd = {
jQuery: true
};
/**
* Executes the text. Normally just uses eval, but can be modified
* to use a better, environment-specific call. Only used for transpiling
......
......@@ -12,111 +12,85 @@ button {
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
font-weight: inherit;
color: inherit;
-webkit-appearance: none;
-ms-appearance: none;
-o-appearance: none;
appearance: none;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
font-smoothing: antialiased;
}
body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
background: #eaeaea url('bg.png');
background: #f5f5f5;
color: #4d4d4d;
width: 550px;
min-width: 230px;
max-width: 550px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased;
font-weight: 300;
}
button,
input[type="checkbox"] {
outline: none;
outline: none;
}
#todoapp {
.hidden {
display: none;
}
.todoapp {
background: #fff;
background: rgba(255, 255, 255, 0.9);
margin: 130px 0 40px 0;
border: 1px solid #ccc;
position: relative;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.15);
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
#todoapp:before {
content: '';
border-left: 1px solid #f5d6d6;
border-right: 1px solid #f5d6d6;
width: 2px;
position: absolute;
top: 0;
left: 40px;
height: 100%;
.todoapp input::-webkit-input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
#todoapp input::-webkit-input-placeholder {
.todoapp input::-moz-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
#todoapp input::-moz-placeholder {
.todoapp input::input-placeholder {
font-style: italic;
color: #a9a9a9;
font-weight: 300;
color: #e6e6e6;
}
#todoapp h1 {
.todoapp h1 {
position: absolute;
top: -120px;
top: -155px;
width: 100%;
font-size: 70px;
font-weight: bold;
font-size: 100px;
font-weight: 100;
text-align: center;
color: #b3b3b3;
color: rgba(255, 255, 255, 0.3);
text-shadow: -1px -1px rgba(0, 0, 0, 0.2);
color: rgba(175, 47, 47, 0.15);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
-ms-text-rendering: optimizeLegibility;
-o-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
#header {
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,
.new-todo,
.edit {
position: relative;
margin: 0;
width: 100%;
font-size: 24px;
font-family: inherit;
font-weight: inherit;
line-height: 1.4em;
border: 0;
outline: none;
......@@ -124,89 +98,83 @@ input[type="checkbox"] {
padding: 6px;
border: 1px solid #999;
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;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased;
}
#new-todo {
.new-todo {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.02);
z-index: 2;
box-shadow: none;
background: rgba(0, 0, 0, 0.003);
box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
}
#main {
.main {
position: relative;
z-index: 2;
border-top: 1px dotted #adadad;
border-top: 1px solid #e6e6e6;
}
label[for='toggle-all'] {
display: none;
}
#toggle-all {
.toggle-all {
position: absolute;
top: -42px;
left: -4px;
width: 40px;
top: -55px;
left: -12px;
width: 60px;
height: 34px;
text-align: center;
/* Mobile Safari */
border: none;
border: none; /* Mobile Safari */
}
#toggle-all:before {
content: '»';
font-size: 28px;
color: #d9d9d9;
padding: 0 25px 7px;
.toggle-all:before {
content: '';
font-size: 22px;
color: #e6e6e6;
padding: 10px 27px 10px 27px;
}
#toggle-all:checked:before {
.toggle-all:checked:before {
color: #737373;
}
#todo-list {
.todo-list {
margin: 0;
padding: 0;
list-style: none;
}
#todo-list li {
.todo-list li {
position: relative;
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;
}
#todo-list li.editing {
.todo-list li.editing {
border-bottom: none;
padding: 0;
}
#todo-list li.editing .edit {
.todo-list li.editing .edit {
display: block;
width: 506px;
padding: 13px 17px 12px 17px;
margin: 0 0 0 43px;
}
#todo-list li.editing .view {
.todo-list li.editing .view {
display: none;
}
#todo-list li .toggle {
.todo-list li .toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
......@@ -215,47 +183,35 @@ label[for='toggle-all'] {
top: 0;
bottom: 0;
margin: auto 0;
/* Mobile Safari */
border: none;
border: none; /* Mobile Safari */
-webkit-appearance: none;
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
#todo-list li .toggle:after {
content: '✔';
/* 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:after {
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>');
}
#todo-list li .toggle:checked:after {
color: #85ada7;
text-shadow: 0 1px 0 #669991;
bottom: 1px;
position: relative;
.todo-list li .toggle:checked:after {
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>');
}
#todo-list li label {
.todo-list li label {
white-space: pre;
word-break: break-word;
padding: 15px 60px 15px 15px;
margin-left: 45px;
display: block;
line-height: 1.2;
-webkit-transition: color 0.4s;
transition: color 0.4s;
}
#todo-list li.completed label {
color: #a9a9a9;
.todo-list li.completed label {
color: #d9d9d9;
text-decoration: line-through;
}
#todo-list li .destroy {
.todo-list li .destroy {
display: none;
position: absolute;
top: 0;
......@@ -264,68 +220,65 @@ label[for='toggle-all'] {
width: 40px;
height: 40px;
margin: auto 0;
font-size: 22px;
color: #a88a8a;
-webkit-transition: all 0.2s;
transition: all 0.2s;
font-size: 30px;
color: #cc9a9a;
margin-bottom: 11px;
transition: color 0.2s ease-out;
}
#todo-list li .destroy:hover {
text-shadow: 0 0 1px #000,
0 0 10px rgba(199, 107, 107, 0.8);
-webkit-transform: scale(1.3);
transform: scale(1.3);
.todo-list li .destroy:hover {
color: #af5b5e;
}
#todo-list li .destroy:after {
content: '';
.todo-list li .destroy:after {
content: '×';
}
#todo-list li:hover .destroy {
.todo-list li:hover .destroy {
display: block;
}
#todo-list li .edit {
.todo-list li .edit {
display: none;
}
#todo-list li.editing:last-child {
.todo-list li.editing:last-child {
margin-bottom: -1px;
}
#footer {
.footer {
color: #777;
padding: 0 15px;
position: absolute;
right: 0;
bottom: -31px;
left: 0;
padding: 10px 15px;
height: 20px;
z-index: 1;
text-align: center;
border-top: 1px solid #e6e6e6;
}
#footer:before {
.footer:before {
content: '';
position: absolute;
right: 0;
bottom: 31px;
bottom: 0;
left: 0;
height: 50px;
z-index: -1;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3),
0 6px 0 -3px rgba(255, 255, 255, 0.8),
0 7px 1px -3px rgba(0, 0, 0, 0.3),
0 43px 0 -6px rgba(255, 255, 255, 0.8),
0 44px 2px -6px rgba(0, 0, 0, 0.2);
overflow: hidden;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
0 8px 0 -3px #f6f6f6,
0 9px 1px -3px rgba(0, 0, 0, 0.2),
0 16px 0 -6px #f6f6f6,
0 17px 2px -6px rgba(0, 0, 0, 0.2);
}
#todo-count {
.todo-count {
float: left;
text-align: left;
}
#filters {
.todo-count strong {
font-weight: 300;
}
.filters {
margin: 0;
padding: 0;
list-style: none;
......@@ -334,69 +287,79 @@ label[for='toggle-all'] {
left: 0;
}
#filters li {
.filters li {
display: inline;
}
#filters li a {
color: #83756f;
margin: 2px;
.filters li a {
color: inherit;
margin: 3px;
padding: 3px 7px;
text-decoration: none;
border: 1px solid transparent;
border-radius: 3px;
}
#filters li a.selected {
font-weight: bold;
.filters li a.selected,
.filters li a:hover {
border-color: rgba(175, 47, 47, 0.1);
}
#clear-completed {
.filters li a.selected {
border-color: rgba(175, 47, 47, 0.2);
}
.clear-completed,
html .clear-completed:active {
float: right;
position: relative;
line-height: 20px;
text-decoration: none;
background: rgba(0, 0, 0, 0.1);
font-size: 11px;
padding: 0 10px;
border-radius: 3px;
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2);
cursor: pointer;
position: relative;
}
#clear-completed:hover {
background: rgba(0, 0, 0, 0.15);
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3);
.clear-completed:hover {
text-decoration: underline;
}
#info {
.info {
margin: 65px auto 0;
color: #a6a6a6;
font-size: 12px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
color: #bfbfbf;
font-size: 10px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
text-align: center;
}
#info a {
.info p {
line-height: 1;
}
.info a {
color: inherit;
text-decoration: none;
font-weight: 400;
}
.info a:hover {
text-decoration: underline;
}
/*
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) {
#toggle-all,
#todo-list li .toggle {
.toggle-all,
.todo-list li .toggle {
background: none;
}
#todo-list li .toggle {
.todo-list li .toggle {
height: 40px;
}
#toggle-all {
top: -56px;
left: -15px;
width: 65px;
height: 41px;
.toggle-all {
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
-webkit-appearance: none;
......@@ -404,151 +367,12 @@ label[for='toggle-all'] {
}
}
.hidden {
display: none;
}
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;
@media (max-width: 430px) {
.footer {
height: 50px;
}
.learn-bar #todoapp {
width: 550px;
margin: 130px auto 40px auto;
.filters {
bottom: 10px;
}
}
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 () {
'use strict';
/* jshint ignore:start */
// Underscore's Template Module
// Courtesy of underscorejs.org
var _ = (function (_) {
......@@ -112,8 +114,14 @@
})({});
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() {
if (location.hostname === 'tastejs.github.io') {
......@@ -175,13 +183,17 @@
if (learnJSON.backend) {
this.frameworkJSON = learnJSON.backend;
this.frameworkJSON.issueLabel = framework;
this.append({
backend: true
});
} else if (learnJSON[framework]) {
this.frameworkJSON = learnJSON[framework];
this.frameworkJSON.issueLabel = framework;
this.append();
}
this.fetchIssueCount();
}
Learn.prototype.append = function (opts) {
......@@ -212,6 +224,26 @@
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();
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) {
assert: function (after, initial) {
var nodeIncrease = (after.nodes - initial.nodes);
var listenerIncrease = (after.jsEventListeners - initial.jsEventListeners);
var memoryExceptions = exceptions[framework.name] || {};
console.log(this + ', ' + nodeIncrease + ', ' +
(after.jsHeapSizeUsed - initial.jsHeapSizeUsed) + ', ' + listenerIncrease);
//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!');
}
if (listenerIncrease > exceptions[framework.name].listeners || 0) {
if (listenerIncrease > memoryExceptions.listeners || 0) {
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