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"
}
}
/* 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); }
));
This diff is collapsed.
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
This diff is collapsed.
This diff is collapsed.
/** @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(); }));
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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