Commit 701a6600 authored by Arthur Verschaeve's avatar Arthur Verschaeve

Drop the `componentjs` app

The project was deprecated a while ago

Ref https://github.com/componentjs/component/commit/a580e2e0dca99c93be638bfe55a3718489d625fa
parent 689921b2
/* global cs, UUIDjs, _ */
(function () {
'use strict';
// data model: Todo List entity
cs.ns('app.dm').TodoList = cs.clazz({
dynamics: {
items: []
},
cons: function (obj) {
_.assign(this, _.pick(obj, function (val, key) { return _.has(this, key); }, this));
},
protos: {
itemAdd: function (item) { this.items.push(item); },
itemDel: function (item) { this.items = _.without(this.items, item); },
itemById: function (id) { return _.find(this.items, { id: id }); }
}
});
// data model: Todo Item entity
cs.ns('app.dm').TodoItem = cs.clazz({
dynamics: {
id: '0',
title: '',
completed: false
},
cons: function (obj) {
this.id = UUIDjs.create(1).hex;
_.assign(this, _.pick(obj, function (val, key) { return _.has(this, key); }, this));
}
});
}());
/* global cs, app, _ */
(function () {
'use strict';
// service tier
cs.ns('app').sv = new cs.clazz({
dynamics: {
todoList: null,
storageId: 'todos-componentjs'
},
protos: {
todo: function () {
return this.todoList;
},
load: function () {
this.todoList = new app.dm.TodoList();
if (_.has(localStorage, this.storageId)) {
var obj = JSON.parse(localStorage[this.storageId]);
this.todoList.items = _.map(obj.items, function (item) {
return new app.dm.TodoItem(item);
});
}
},
save: function () {
localStorage[this.storageId] = JSON.stringify(this.todoList);
}
}
})();
}());
<!-- view mask of "main" UI composite -->
<markup id="main">
<div class="main">
<section class="main__todo">
</section>
<footer class="main__info">
<p>Double-click to edit a todo</p>
<p>Written by <a href="http://engelschall.com">Ralf S. Engelschall</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
</div>
</markup>
.main {
font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
line-height: 1.4em;
font-smoothing: antialiased;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
}
.main__todo {
color: #4d4d4d;
width: 550px;
margin: 0 auto;
}
.main__info {
margin: 65px auto 0;
color: #a6a6a6;
font-size: 12px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
text-align: center;
}
.main__info a {
color: inherit;
}
/* global cs, app, Router, $ */
(function () {
'use strict';
// component of the 'main' UI composite
cs.ns('app.ui.composite').main = cs.clazz({
mixin: [cs.marker.controller, cs.marker.view],
protos: {
create: function () {
// create todo widget components and auto-increase their state with us
cs(this).create('todo-model/todo-view', app.ui.widget.todo.model, app.ui.widget.todo.view);
cs(this).property('ComponentJS:state-auto-increase', true);
},
prepare: function () {
var self = this;
var todoModel = cs(self, '//todo-model');
// two-way bind URL route to presentation model
var router = new Router({
'/': function () { todoFilterSelect('all'); },
'/active': function () { todoFilterSelect('active'); },
'/completed': function () { todoFilterSelect('completed'); }
});
var todoFilterSelect = function (filter) {
todoModel.value('state:status-filter-selected', filter);
todoModel.value('cmd:item-list-updated', true);
};
todoModel.observe({ name: 'event:status-filter-select', func: function (ev, value) {
var route = '/' + value;
if (route === '/all') {
route = '/';
}
if (router.getRoute() !== route) {
window.location.hash = '#' + route;
router.setRoute(route);
}
}});
router.init();
// transfer business model into presentation model
var bm2pm = function () {
var bmTodoList = app.sv.todo();
var pmItems = bmTodoList.items.map(function (bmTodoItem) {
return {
id: bmTodoItem.id,
title: bmTodoItem.title,
completed: bmTodoItem.completed,
editing: false
};
});
todoModel.value('data:item-list', pmItems);
todoModel.value('cmd:item-list-updated', true);
};
// react on item CRUD model event value changes
todoModel.observe({ name: 'event:new-item-create', func: function (/* ev, value */) {
var text = todoModel.value('data:new-item-text');
todoModel.value('data:new-item-text', '');
var todoList = app.sv.todo();
var todoItem = new app.dm.TodoItem({ title: text });
todoList.itemAdd(todoItem);
app.sv.save();
bm2pm();
}});
todoModel.observe({ name: 'event:item-list-item-modified', func: function (ev, item) {
var todoList = app.sv.todo();
var todoItem = todoList.itemById(item.id);
todoItem.title = item.title;
todoItem.completed = item.completed;
app.sv.save();
bm2pm();
}});
todoModel.observe({ name: 'event:item-list-item-removed', func: function (ev, item) {
var todoList = app.sv.todo();
var todoItem = todoList.itemById(item.id);
todoList.itemDel(todoItem);
app.sv.save();
bm2pm();
}});
// initially load business model and trigger transfer into presentation model
app.sv.load();
bm2pm();
},
render: function () {
// render view mask
var ui = $.markup('main');
cs(this).plug(ui);
cs(this).socket({ ctx: $('.main__todo', ui), spool: 'materialized' });
}
}
});
}());
html,
body {
margin: 0;
padding: 0;
background: #eaeaea url(../bower_components/todomvc-common/bg.png);
}
/* global cs, app, $, _ */
(function () {
'use strict';
// component of the root UI composite
cs.ns('app.ui.composite').root = cs.clazz({
mixin: [cs.marker.controller, cs.marker.view],
protos: {
create: function () {
// create main composite component and auto-increase its state with us
cs(this).create('main', app.ui.composite.main);
cs(this).property('ComponentJS:state-auto-increase', true);
},
prepare: function () {
// await the readiness of the DOM
if (_.isObject(document)) {
var self = this;
cs(self).guard('render', 1);
$(document).ready(function () {
// load all markup code
$.markup.load(function () {
cs(self).guard('render', -1);
});
});
}
},
render: function () {
// place a socket onto the DOM body element
cs(this).socket({ ctx: $('body'), spool: 'materialized' });
},
release: function () {
// destroy socket onto DOM body element
cs(this).unspool('materialized');
},
cleanup: function () {
// destroy main composite component
cs(this, 'main').destroy();
}
}
});
}());
/* global cs */
(function () {
'use strict';
// some UI constants
cs.ns('app.ui').constants = {
// DOM event key code
KEY_ENTER: 13,
KEY_ESCAPE: 27
};
}());
<!-- view mask of "todo" UI widget -->
<markup id="todo">
<div class="todo">
<header class="todo__header">
<h1 class="todo__h1">todos</h1>
<input class="todo__new todo__input" placeholder="What needs to be done?" autofocus="autofocus">
</header>
<section class="todo__main">
<input class="todo__toggle-all todo__input" type="checkbox">
<label class="todo__toggle-label" for="todo__toggle-all">Mark all as complete</label>
<ul class="todo__list">
<markup id="item">
<li class="todo__item {% if completed %}completed{% endif %}" data-id="{{id}}">
<div class="todo__item--view view">
<input class="todo__toggle toggle todo__input" type="checkbox"
{% if completed %}checked="checked"{% endif %}>
<label class="todo__label">{{title}}</label>
<button class="todo__destroy destroy todo__button">
</div>
<input class="todo__item--edit edit todo__input" value="{{title}}">
</li>
</markup>
</ul>
</section>
<footer class="todo__footer">
<span class="todo__count">
<strong data-bind="data:status-items-remaining"></strong>
<span data-bind="data:status-items-remaining-unit"></span> left
</span>
<ul class="todo__filters" data-bind="state:status-filter-selected">
<li><a href="#" data-tag="all">All</a></li>
<li><a href="#" data-tag="active">Active</a></li>
<li><a href="#" data-tag="completed">Completed</a></li>
</ul>
<button class="todo__completed todo__button">
Clear completed
</button>
</footer>
</div>
</markup>
/* global cs, _ */
(function () {
'use strict';
// model component of the 'todo' UI widget
cs.ns('app.ui.widget.todo').model = cs.clazz({
mixin: [cs.marker.model],
protos: {
create: function () {
// define presentation model
cs(this).model({
'data:item-list': { value: [], valid: '[{ id: string, title: string, completed: boolean, editing: boolean }*]' },
'cmd:item-list-updated': { value: false, valid: 'boolean', autoreset: true },
'state:all-item-selected': { value: false, valid: 'boolean' },
'event:all-item-select': { value: false, valid: 'boolean', autoreset: true },
'data:new-item-text': { value: '', valid: 'string', store: true },
'event:new-item-create': { value: false, valid: 'boolean', autoreset: true },
'event:item-list-item-modified': { value: null, valid: 'object', autoreset: true },
'event:item-list-item-removed': { value: null, valid: 'object', autoreset: true },
'data:status-items-remaining': { value: 0, valid: 'number' },
'data:status-items-remaining-unit': { value: '', valid: 'string' },
'state:status-filter-selected': { value: 'all', valid: 'string', store: true },
'event:status-filter-select': { value: '', valid: 'string', autoreset: true },
'data:status-items-completed': { value: 0, valid: 'number' },
'event:status-clear-select': { value: false, valid: 'boolean', autoreset: true }
});
},
prepare: function () {
var self = this;
// presentation logic: determine singular/plural of remaining items unit
cs(self).observe({
name: 'data:status-items-remaining',
touch: true,
func: function (ev, value) {
cs(self).value('data:status-items-remaining-unit',
value !== 1 ? 'items' : 'item');
}
});
// presentation logic: determine number of completed and remaining items
cs(self).observe({
name: 'cmd:item-list-updated',
touch: true,
func: function (/* ev, value */) {
var items = cs(self).value('data:item-list');
var completed = _.filter(items, 'completed').length;
var remaining = items.length - completed;
cs(self).value('data:status-items-completed', completed);
cs(self).value('data:status-items-remaining', remaining);
if (remaining === 0 && completed > 0) {
cs(self).value('state:all-item-selected', true);
} else if (remaining > 0) {
cs(self).value('state:all-item-selected', false);
}
}
});
// presentation logic: implement 'all item selected'
cs(self).observe({
name: 'event:all-item-select',
func: function (ev, value) {
var items = cs(self).value('data:item-list');
var modified = false;
_.forEach(items, function (item) {
if (item.completed !== value) {
item.completed = value;
cs(self).value('event:item-list-item-modified', item, true);
modified = true;
}
});
if (modified) {
cs(self).value('cmd:item-list-updated', true, true);
}
}
});
}
}
});
}());
.todo {
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);
}
.todo:before {
content: "";
border-left: 1px solid #f5d6d6;
border-right: 1px solid #f5d6d6;
width: 2px;
position: absolute;
top: 0;
left: 40px;
height: 100%;
}
.todo__button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
color: inherit;
-webkit-appearance: none;
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
.todo__button,
.todo__input[type="checkbox"] {
outline: none;
}
.todo__input::-webkit-input-placeholder {
font-style: italic;
}
.todo__input::-moz-placeholder {
font-style: italic;
color: #a9a9a9;
}
.todo__h1 {
position: absolute;
top: -120px;
width: 100%;
font-size: 70px;
font-weight: bold;
text-align: center;
color: #b3b3b3;
color: rgba(255, 255, 255, 0.3);
text-shadow: -1px -1px rgba(0, 0, 0, 0.2);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
-ms-text-rendering: optimizeLegibility;
-o-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
.todo__header {
padding-top: 15px;
border-radius: inherit;
}
.todo__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;
}
.todo__footer {
color: #777;
padding: 0 15px;
position: absolute;
right: 0;
bottom: -31px;
left: 0;
height: 20px;
z-index: 1;
text-align: center;
}
.todo__footer:before {
content: '';
position: absolute;
right: 0;
bottom: 31px;
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);
}
.todo__main.hidden {
display: none;
}
.todo__footer.hidden {
display: none;
}
.todo__count {
float: left;
text-align: left;
}
.todo__filters {
margin: 0;
padding: 0;
list-style: none;
position: absolute;
right: 0;
left: 0;
}
.todo__filters li {
display: inline;
}
.todo__filters li a {
color: #83756f;
margin: 2px;
text-decoration: none;
}
.todo__filters li a.selected {
font-weight: bold;
}
.todo__completed {
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);
}
.todo__completed:hover {
background: rgba(0, 0, 0, 0.15);
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3);
}
.todo__new,
.todo__item--edit {
position: relative;
margin: 0;
width: 100%;
font-size: 24px;
font-family: inherit;
line-height: 1.4em;
border: 0;
outline: none;
color: inherit;
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;
}
.todo__new {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.02);
z-index: 2;
box-shadow: none;
}
.todo__main {
position: relative;
z-index: 2;
border-top: 1px dotted #adadad;
}
.todo__toggle-label {
display: none;
}
.todo__toggle-all {
position: absolute;
top: -42px;
left: -4px;
width: 40px;
text-align: center;
/* Mobile Safari */
border: none;
}
.todo__toggle-all:before {
content: "»";
font-size: 28px;
color: #d9d9d9;
padding: 0 25px 7px;
}
.todo__toggle-all:checked:before {
color: #737373;
}
.todo__list {
margin: 0;
padding: 0;
list-style: none;
}
.todo__item {
position: relative;
font-size: 24px;
border-bottom: 1px dotted #ccc;
}
.todo__item:last-child {
border-bottom: none;
}
.todo__item.editing {
border-bottom: none;
padding: 0;
}
.todo__item.editing .todo__item--edit {
display: block;
width: 506px;
padding: 13px 17px 12px 17px;
margin: 0 0 0 43px;
}
.todo__item.editing .todo__item--view {
display: none;
}
.todo__toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
/* Mobile Safari */
border: none;
-webkit-appearance: none;
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
.todo__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__toggle:checked:after {
color: #85ada7;
text-shadow: 0 1px 0 #669991;
bottom: 1px;
position: relative;
}
.todo__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__item.completed .todo__label {
color: #a9a9a9;
text-decoration: line-through;
}
.todo__destroy {
display: none;
position: absolute;
top: 0;
right: 10px;
bottom: 0;
width: 40px;
height: 40px;
margin: auto 0;
font-size: 22px;
color: #a88a8a;
-webkit-transition: all 0.2s;
transition: all 0.2s;
}
.todo__destroy:hover {
text-shadow: 0 0 1px #000,
0 0 10px rgba(199, 107, 107, 0.8);
-webkit-transform: scale(1.3);
-ms-transform: scale(1.3);
transform: scale(1.3);
}
.todo__destroy:after {
content: '✖';
}
.todo__item:hover .todo__destroy {
display: block;
}
.todo__item--edit {
display: none;
}
.todo__item.editing:last-child {
margin-bottom: -1px;
}
/* Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox and Opera */
@media screen and (-webkit-min-device-pixel-ratio:0) {
.todo__toggle-all,
.todo__toggle {
background: none;
}
.todo__toggle {
height: 40px;
}
.todo__toggle-all {
top: -56px;
left: -15px;
width: 65px;
height: 41px;
-webkit-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg);
-webkit-appearance: none;
appearance: none;
}
}
/* global cs, app, $, _ */
(function () {
'use strict';
// view component of the 'todo' UI widget
cs.ns('app.ui.widget.todo').view = cs.clazz({
mixin: [cs.marker.view],
protos: {
render: function () {
// render outer view mask
var self = this;
var ui = $.markup('todo');
cs(self).plug(ui);
// two-way bind the 'all items' selection checkbox
$('.todo__toggle-all', ui).change(function (/* ev */) {
cs(self).value('event:all-item-select', $('.todo__toggle-all', ui).prop('checked'), true);
});
cs(self).observe({
name: 'state:all-item-selected',
spool: 'materialized',
func: function (ev, value) { $('.todo__toggle-all', ui).prop('checked', value); }
});
// two-way bind the 'new item' text input field
$('.todo__new', ui).keyup(function (/* ev */) {
cs(self).value('data:new-item-text', $('.todo__new', ui).val());
}).change(function (/* ev */) {
var value = $('.todo__new', ui).val().trim();
if (value !== '') {
cs(self).value('data:new-item-text', value);
cs(self).value('event:new-item-create', true, true);
}
});
cs(self).observe({
name: 'data:new-item-text',
touch: true,
spool: 'materialized',
func: function (ev, value) { $('.todo__new', ui).val(value); }
});
// two-way bind the list of items
cs(self).observe({
name: 'cmd:item-list-updated',
spool: 'materialized',
touch: true,
func: function (/* ev, value */) {
var items = cs(self).value('data:item-list');
var filter = cs(self).value('state:status-filter-selected');
// render item markup for all non-filtered items
$('.todo__list', ui).html('');
for (var i = 0; i < items.length; i++) {
if (filter === 'all' ||
(filter === 'active' && !items[i].completed) ||
(filter === 'completed' && items[i].completed)) {
var item = $.markup('todo/item', items[i]);
$('.todo__list', ui).append(item);
}
}
// show/hide the footer accordingly
if (items.length === 0) {
$('.todo__main', ui).addClass('hidden');
$('.todo__footer', ui).addClass('hidden');
} else {
$('.todo__main', ui).removeClass('hidden');
$('.todo__footer', ui).removeClass('hidden');
}
// one-way bind double-click interaction onto all items to start editing mode
$('.todo__item--view .todo__label', ui).bind('dblclick', function (ev) {
var title = $(ev.target).text();
var parent = $(ev.target).parent().parent();
parent.addClass('editing');
$('.edit', parent).val(title).focus();
});
// one-way bind key-press and field blur interactions to leave editing mode
var blur = function (el, takeTitle) {
var id = String($(el).parent().data('id'));
$(el).parent().removeClass('editing');
if (takeTitle) {
var items = cs(self).value('data:item-list');
var item = _.find(items, { id: id });
var title = $(el).val().trim();
if (title === '') {
cs(self).value('data:item-list', _.without(items, item));
cs(self).value('event:item-list-item-removed', item, true);
} else {
item.title = title;
cs(self).value('event:item-list-item-modified', item);
}
cs(self).value('cmd:item-list-updated', true, true);
}
};
$('.todo__item--edit', ui).keyup(function (ev) {
if (ev.which === app.ui.constants.KEY_ENTER) {
blur(ev.target, true);
} else if (ev.which === app.ui.constants.KEY_ESCAPE) {
blur(ev.target, false);
}
}).blur(function (ev) {
if ($(ev.target).parent().hasClass('editing')) {
blur(ev.target, true);
}
});
// one-way bind click interaction to toggle item completion
$('.todo__toggle', ui).click(function (ev) {
var id = String($(ev.target).parent().parent().data('id'));
var items = cs(self).value('data:item-list');
var item = _.find(items, { id: id });
item.completed = !item.completed;
cs(self).value('event:item-list-item-modified', item, true);
cs(self).value('cmd:item-list-updated', true, true);
});
// one-way bind click interaction to remove item
$('.todo__destroy', ui).click(function (ev) {
var id = String($(ev.target).parent().parent().data('id'));
var items = cs(self).value('data:item-list');
var item = _.find(items, { id: id });
cs(self).value('data:item-list', _.without(items, item));
cs(self).value('event:item-list-item-removed', item, true);
cs(self).value('cmd:item-list-updated', true, true);
});
}
});
// one-way bind status remaining items label
cs(self).observe({
name: 'data:status-items-remaining',
spool: 'materialized',
touch: true,
func: function (ev, value) {
$('*[data-bind=\'data:status-items-remaining\']', ui).text(value);
$('*[data-bind=\'data:status-items-remaining-unit\']', ui)
.text(parseInt(value) === 1 ? 'item' : 'items');
}
});
// two-way bind status filter buttons
cs(self).observe({
name: 'state:status-filter-selected',
spool: 'materialized',
touch: true,
func: function (ev, value) {
var a = $('*[data-bind=\'state:status-filter-selected\'] > li > a', ui);
a.removeClass('selected');
a.filter('*[data-tag=\'' + value + '\']').addClass('selected');
}
});
$('*[data-bind=\'state:status-filter-selected\'] > li > a', ui).click(function (ev) {
cs(self).value('event:status-filter-select', $(ev.target).data('tag'), true);
cs(self).value('cmd:item-list-updated', true, true);
return false;
});
// two-way bind status clear item button
cs(self).observe({
name: 'data:status-items-completed',
spool: 'materialized',
touch: true,
func: function (ev, value) {
if (value > 0) {
$('.todo__completed', ui).css('display', 'block');
} else {
$('.todo__completed', ui).css('display', 'none');
}
}
});
$('.todo__completed', ui).click(function (/* ev */) {
var items = cs(self).value('data:item-list');
_.forEach(items, function (item) {
if (item.completed) {
cs(self).value('event:item-list-item-removed', item, true);
}
});
cs(self).value('data:item-list', _.filter(items, function (item) {
return !item.completed;
}));
cs(self).value('cmd:item-list-updated', true, true);
});
}
}
});
}());
/* global ComponentJS, cs, app, _ */
(function () {
'use strict';
// application bootstrap class
ComponentJS.symbol('cs');
cs.ns('app').boot = cs.clazz({
statics: {
init: function () {
// bootstrap ComponentJS framework
cs.bootstrap();
cs.debug(0);
},
main: function () {
// fire up the component tree
cs.create('/ui', app.ui.composite.root);
cs('/ui').state(_.isObject(document) ? 'visible' : 'prepared');
}
}
});
}());
{
"name": "todomvc-componentjs",
"dependencies": {
"componentjs": "1.0.1",
"jquery": "2.0.3",
"jquery-markup": "1.0.26",
"nunjucks": "1.0.0",
"director": "1.2.0",
"lodash": "2.3.0",
"uuid-js": "0.7.5",
"todomvc-common": "0.1.9"
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
** ComponentJS -- Component System for JavaScript <http://componentjs.com>
** Copyright (c) 2009-2013 Ralf S. Engelschall <http://engelschall.com>
**
** This Source Code Form is subject to the terms of the Mozilla Public
** License, v. 2.0. If a copy of the MPL was not distributed with this
** file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/*
* This is a graphical ComponentJS debugger which provides both a
* component tree visualization and a ComponentJS debug message
* console. As in a production environment one might not want to carry
* this functionality with the application, this functionality has to
* stay in a separate optional plugin, of course.
*/
/* global ComponentJS:false */
ComponentJS.plugin("debugger", function (_cs, $cs, GLOBAL) {
/*
* minimum emulation of jQuery
*/
_cs.jq = function (sel, el) {
var result = [];
if (arguments.length === 1 && typeof sel !== "string")
result.push(sel);
else {
if (typeof el === "undefined")
el = GLOBAL.document;
try { result = el.querySelectorAll(sel); }
catch (e) { result = GLOBAL.document; }
result = _cs.concat([], result);
}
_cs.extend(result, _cs.jq_methods);
return result;
};
_cs.jq_methods = {
ready: function (callback) {
/* not correct (because too complicated to
emulate portably), but sufficient for now! */
for (var i = 0; i < this.length; i++) {
(function (i) {
var el = this[i];
/* global setTimeout:false */
setTimeout(function () {
callback.call(el);
}, 250);
})(i);
}
},
bind: function (name, callback) {
for (var i = 0; i < this.length; i++) {
if (typeof this[i].addEventListener === "function")
this[i].addEventListener(name, callback, false);
else if (typeof this[i].attachEvent === "function")
this[i].attachEvent("on" + name, callback);
}
return this;
},
width: function (value) {
var result = (typeof value !== "undefined" ? this : undefined);
for (var i = 0; i < this.length; i++) {
if (typeof value === "undefined") {
result = this[i].offsetWidth;
if (typeof result === "undefined")
result = this[i].innerWidth;
if (typeof result === "undefined")
result = this[i].clientWidth;
}
else {
this[i].style.width = value;
}
}
return result;
},
height: function (value) {
var result = (typeof value !== "undefined" ? this : undefined);
for (var i = 0; i < this.length; i++) {
if (typeof value === "undefined") {
result = this[i].offsetHeight;
if (typeof result === "undefined")
result = this[i].innerHeight;
if (typeof result === "undefined")
result = this[i].clientHeight;
}
else {
this[i].style.height = value;
}
}
return result;
},
css: function (name, value) {
var result = (typeof value !== "undefined" ? this : undefined);
var field = name.replace(/-([a-z])/g, function (a0, a1) {
return a1.toUpperCase();
});
for (var i = 0; i < this.length; i++) {
if (typeof value === "undefined")
result = this[i].style[field];
else {
if (_cs.isIE())
this[i].style.cssText = name + ":" + value + ";";
else
this[i].style[field] = value;
}
}
return result;
},
attr: function (name, value) {
var result = (typeof value !== "undefined" ? this : undefined);
for (var i = 0; i < this.length; i++) {
if (typeof value === "undefined")
result = this[i].getAttribute(name);
else
this[i].setAttribute(name, value);
}
return result;
},
html: function (html) {
for (var i = 0; i < this.length; i++) {
try {
/* direct approach (but does not work on all elements,
especially not on html, head and body, etc) */
this[i].innerHTML = html;
}
catch (e) {
/* create an arbitrary element on which we can use innerHTML */
var content = _cs.dbg.document.createElement("div");
/* set innerHTML, but use an outer wrapper element
to ensure we have a single root element */
content.innerHTML = "<div>" + html + "</div>";
/* remove all nodes from target node */
while (this[i].firstChild)
this[i].removeChild(this[i].firstChild);
/* add all nodes in our <div><div>...</div></div> enclosure */
for (var j = 0; j < content.firstChild.childNodes.length; j++)
this[i].appendChild(content.firstChild.childNodes[j]);
}
}
return this;
},
scrollTop: function (value) {
for (var i = 0; i < this.length; i++)
this[i].scrollTop = value;
return this;
},
get: function (pos) {
return this[pos];
}
};
/* create debugger view mask (markup and style) */
_cs.dbg_view_mask = function (title) {
_cs.jq("html head", _cs.dbg.document).html(
"<title>" + title + "</title>"
);
_cs.jq("html body", _cs.dbg.document).html(
"<style type=\"text/css\">" +
"html, body {" +
"margin: 0px;" +
"padding: 0px;" +
"}" +
".dbg {" +
"width: 100%;" +
"height: 100%;" +
"font-family: Helvetica, Arial, sans-serif;" +
"background-color: #e0e0e0;" +
"overflow: hidden;" +
"font-size: 9pt;" +
"}" +
".dbg .header {" +
"width: 100%;" +
"height: 30px;" +
"background: #666666;" +
"background: -moz-linear-gradient(top, #666666 0%, #333333 49%, #222222 51%, #000000 100%);" +
"background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#666666), color-stop(49%,#333333), color-stop(51%,#222222), color-stop(100%,#000000));" +
"background: -webkit-linear-gradient(top, #666666 0%,#333333 49%,#222222 51%,#000000 100%);" +
"background: -o-linear-gradient(top, #666666 0%,#333333 49%,#222222 51%,#000000 100%);" +
"background: -ms-linear-gradient(top, #666666 0%,#333333 49%,#222222 51%,#000000 100%);" +
"background: linear-gradient(to bottom, #666666 0%,#333333 49%,#222222 51%,#000000 100%);" +
"filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#666666', endColorstr='#000000',GradientType=0 );" +
"text-align: center;" +
"position: relative;" +
"}" +
".dbg .header .text {" +
"position: relative;" +
"top: 6px;" +
"color: #ffffff;" +
"font-size: 12pt;" +
"font-weight: bold;" +
"}" +
".dbg .viewer {" +
"position: relative;" +
"width: 100%;" +
"height: 50%;" +
"background: #d0d0d0;" +
"background: -moz-linear-gradient(top, #d0d0d0 0%, #e8e8e8 50%, #d0d0d0 100%);" +
"background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#d0d0d0), color-stop(50%,#e8e8e8), color-stop(100%,#d0d0d0));" +
"background: -webkit-linear-gradient(top, #d0d0d0 0%,#e8e8e8 50%,#d0d0d0 100%);" +
"background: -o-linear-gradient(top, #d0d0d0 0%,#e8e8e8 50%,#d0d0d0 100%);" +
"background: -ms-linear-gradient(top, #d0d0d0 0%,#e8e8e8 50%,#d0d0d0 100%);" +
"background: linear-gradient(to bottom, #d0d0d0 0%,#e8e8e8 50%,#d0d0d0 100%);" +
"filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#d0d0d0', endColorstr='#d0d0d0',GradientType=0 );" +
"overflow: hidden;" +
"}" +
".dbg .viewer canvas {" +
"position: absolute;" +
"top: 0px;" +
"left: 0px;" +
"}" +
".dbg .status {" +
"width: 100%;" +
"height: 20px;" +
"background: #666666;" +
"background: -moz-linear-gradient(top, #666666 0%, #333333 49%, #222222 51%, #000000 100%);" +
"background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#666666), color-stop(49%,#333333), color-stop(51%,#222222), color-stop(100%,#000000));" +
"background: -webkit-linear-gradient(top, #666666 0%,#333333 49%,#222222 51%,#000000 100%);" +
"background: -o-linear-gradient(top, #666666 0%,#333333 49%,#222222 51%,#000000 100%);" +
"background: -ms-linear-gradient(top, #666666 0%,#333333 49%,#222222 51%,#000000 100%);" +
"background: linear-gradient(to bottom, #666666 0%,#333333 49%,#222222 51%,#000000 100%);" +
"filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#666666', endColorstr='#000000',GradientType=0 );" +
"color: #f0f0f0;" +
"text-align: center;" +
"}" +
".dbg .status .text {" +
"position: relative;" +
"top: 3px;" +
"color: #ffffff;" +
"font-size: 9pt;" +
"}" +
".dbg .console {" +
"width: 100%;" +
"height: 50%;" +
"background-color: #ffffff;" +
"color: #000000;" +
"overflow: scroll;" +
"font-size: 9pt;" +
"}" +
".dbg .console .text {" +
"width: 100%;" +
"height: auto;" +
"}" +
".dbg .console .text .line {" +
"border-collapse: collapse;" +
"width: 100%;" +
"border-bottom: 1px solid #e0e0e0;" +
"font-size: 9pt;" +
"}" +
".dbg .console .text .num {" +
"width: 40px;" +
"background-color: #f0f0f0;" +
"text-align: right;" +
"}" +
".dbg .console .text .msg {" +
"padding-left: 10px;" +
"}" +
".dbg .console .text .msg .prefix {" +
"color: #999999;" +
"}" +
".dbg .console .text .msg .context {" +
"font-weight: bold;" +
"}" +
".dbg .console .text .msg .path {" +
"color: #003399;" +
"}" +
".dbg .console .text .msg .state {" +
"font-style: italic;" +
"}" +
".dbg .console .text .msg .arrow {" +
"color: #999999;" +
"}" +
".dbg .console .text .msg .method {" +
"font-family: monospace;" +
"}" +
".dbg .grabber {" +
"position: absolute; " +
"cursor: move; " +
"width: 100%;" +
"height: 20px;" +
"background-color: transparent;" +
"opacity: 0.5;" +
"z-index: 100;" +
"}" +
".dbg .infobox {" +
"position: absolute;" +
"top: 0px;" +
"left: 0px;" +
"width: 100%;" +
"background-color: #ffffff;" +
"color: #000000;" +
"z-index: 200;" +
"display: none;" +
"}" +
".dbg .infobox table {" +
"border-collapse: collapse;" +
"width: 100%;" +
"}" +
".dbg .infobox table tr td {" +
"border-bottom: 1px solid #e0e0e0;" +
"}" +
".dbg .infobox table tr td {" +
"font-size: 11pt;" +
"}" +
".dbg .infobox table tr td.label {" +
"padding-left: 10px;" +
"background-color: #f0f0f0;" +
"color: #909090;" +
"vertical-align: top;" +
"width: 160px;" +
"}" +
".dbg .infobox table tr td.value {" +
"padding-left: 10px;" +
"vertical-align: top;" +
"}" +
".dbg .infobox table tr td.value span.none {" +
"color: #909090;" +
"font-style: italic;" +
"}" +
".dbg .plus, .dbg .reset, .dbg .minus {" +
"position: absolute; " +
"top: 4px; " +
"width: 10px; " +
"text-align: center; " +
"font-weight: bold; " +
"padding: 2px 8px 2px 8px; " +
"border-top: 1px solid #777777;" +
"border-left: 1px solid #777777;" +
"border-right: 1px solid #555555;" +
"border-bottom: 1px solid #555555;" +
"background: #666666;" +
"background: -moz-linear-gradient(top, #666666 0%, #333333 49%, #222222 51%, #000000 100%);" +
"background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#666666), color-stop(49%,#333333), color-stop(51%,#222222), color-stop(100%,#000000));" +
"background: -webkit-linear-gradient(top, #666666 0%,#333333 49%,#222222 51%,#000000 100%);" +
"background: -o-linear-gradient(top, #666666 0%,#333333 49%,#222222 51%,#000000 100%);" +
"background: -ms-linear-gradient(top, #666666 0%,#333333 49%,#222222 51%,#000000 100%);" +
"background: linear-gradient(to bottom, #666666 0%,#333333 49%,#222222 51%,#000000 100%);" +
"filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#666666', endColorstr='#000000',GradientType=0 );" +
"color: #c0c0c0;" +
"z-index: 100;" +
"}" +
".dbg .plus {" +
"right: 80px; " +
"}" +
".dbg .reset {" +
"right: 110px; " +
"}" +
".dbg .minus {" +
"right: 140px; " +
"}" +
".dbg .exporter {" +
"position: absolute; " +
"top: 4px; " +
"right: 20px; " +
"padding: 2px 8px 2px 8px; " +
"border-top: 1px solid #777777;" +
"border-left: 1px solid #777777;" +
"border-right: 1px solid #555555;" +
"border-bottom: 1px solid #555555;" +
"background: #666666;" +
"background: -moz-linear-gradient(top, #666666 0%, #333333 49%, #222222 51%, #000000 100%);" +
"background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#666666), color-stop(49%,#333333), color-stop(51%,#222222), color-stop(100%,#000000));" +
"background: -webkit-linear-gradient(top, #666666 0%,#333333 49%,#222222 51%,#000000 100%);" +
"background: -o-linear-gradient(top, #666666 0%,#333333 49%,#222222 51%,#000000 100%);" +
"background: -ms-linear-gradient(top, #666666 0%,#333333 49%,#222222 51%,#000000 100%);" +
"background: linear-gradient(to bottom, #666666 0%,#333333 49%,#222222 51%,#000000 100%);" +
"filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#666666', endColorstr='#000000',GradientType=0 );" +
"color: #c0c0c0;" +
"z-index: 100;" +
"}" +
"</style>" +
"<div class=\"dbg\">" +
"<div class=\"header\"><div class=\"text\">" + title + "</div></div>" +
"<div class=\"viewer\"><canvas></canvas></div>" +
"<div class=\"grabber\"></div>" +
"<div class=\"plus\">+</div>" +
"<div class=\"reset\">0</div>" +
"<div class=\"minus\">-</div>" +
"<div class=\"exporter\">Export</div>" +
"<div class=\"status\"><div class=\"text\"></div></div>" +
"<div class=\"console\"><div class=\"text\"></div></div>" +
"<div class=\"infobox\"></div>" +
"</div>"
);
};
/* debugger console log */
_cs.dbg_logline = 0;
_cs.dbg_logbook = "";
/* log message to debugger console */
_cs.dbg_log = function (msg) {
if (_cs.dbg === null)
return;
msg = msg
.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace("\n", "<br/>");
_cs.dbg_logline++;
msg = msg.replace(/(DEBUG\[\d+\]: )([^:]+)/,
"<span class=\"prefix\">$1</span>" +
"<span class=\"context\">$2</span>"
);
msg = msg.replace(/(\s+)(\/[^:\s]*)/g, "$1<span class=\"path\">$2</span>");
msg = msg.replace(/(\s+)(@[a-z]+)/g, "$1<span class=\"state\">$2</span>");
msg = msg.replace(/((?:&lt;)?--\()([a-z]+)(\)--(?:&gt;)?)/g,
"<span class=\"arrow\">$1</span>" +
"<span class=\"method\">$2</span>" +
"<span class=\"arrow\">$3</span>"
);
_cs.dbg_logbook =
"<table class=\"line\">" +
"<tr>" +
"<td class=\"num\">" + _cs.dbg_logline + ".</td>" +
"<td class=\"msg\">" + msg + "</td>" +
"</tr>" +
"</table>" + _cs.dbg_logbook;
_cs.dbg_state_invalidate("console");
_cs.dbg_update();
};
/* determine component information for infobox */
_cs.dbg_infobox_content = function (comp) {
var name, method, id;
var html = "";
/* name and path */
name = comp.name().replace(/</, "&lt;").replace(/>/, "&gt;");
html += "<tr>" +
"<td class=\"label\">Name:</td>" +
"<td class=\"value\"><b>" + name + "</b></td>" +
"</tr>";
html += "<tr>" +
"<td class=\"label\">Path:</td>" +
"<td class=\"value\"><code>" + comp.path("/") + "</code></td>" +
"</tr>";
/* role markers */
var markers = "";
if ($cs.marked(comp.obj(), "view")) markers += "view, ";
if ($cs.marked(comp.obj(), "model")) markers += "model, ";
if ($cs.marked(comp.obj(), "controller")) markers += "controller, ";
if ($cs.marked(comp.obj(), "service")) markers += "service, ";
markers = markers.replace(/, $/, "");
if (markers === "")
markers = "<span class=\"none\">none</span>";
html += "<tr>" +
"<td class=\"label\">Markers:</td>" +
"<td class=\"value\">" + markers + "</td>" +
"</tr>";
/* state and guards */
html += "<tr>" +
"<td class=\"label\">State:</td>" +
"<td class=\"value\"><code>" + comp.state() + "</code></td>" +
"</tr>";
var guards = "";
for (method in comp.__state_guards)
if (_cs.isown(comp.__state_guards, method))
if (typeof comp.__state_guards[method] === "number" &&
comp.__state_guards[method] !== 0 )
guards += "<code>" + method + "</code> (" + comp.__state_guards[method] + "), ";
guards = guards.replace(/, $/, "");
if (guards === "")
guards = "<span class=\"none\">none</span>";
html += "<tr>" +
"<td class=\"label\">Guards:</td>" +
"<td class=\"value\">" + guards + "</td>" +
"</tr>";
/* spools */
var spools = "";
for (name in comp.__spool)
if (_cs.isown(comp.__spool, name))
if (typeof comp.__spool[name] !== "undefined" &&
comp.__spool[name].length > 0 )
spools += "<code>" + name + "</code> (" + comp.__spool[name].length + "), ";
spools = spools.replace(/, $/, "");
if (spools === "")
spools = "<span class=\"none\">none</span>";
html += "<tr>" +
"<td class=\"label\">Spools:</td>" +
"<td class=\"value\">" + spools + "</td>" +
"</tr>";
/* model values */
var values = "";
for (id in comp.__config)
if (_cs.isown(comp.__config, id))
if (id.match(/^ComponentJS:property:ComponentJS:model/))
if (typeof comp.__config[id] === "object")
for (name in comp.__config[id])
if (_cs.isown(comp.__config[id], name))
values += "<code>" + name + "</code>, ";
values = values.replace(/, $/, "");
if (values === "")
values = "<span class=\"none\">none</span>";
html += "<tr>" +
"<td class=\"label\">Model Values:</td>" +
"<td class=\"value\">" + values + "</td>" +
"</tr>";
/* sockets */
var sockets = "";
for (id in comp.__config)
if (_cs.isown(comp.__config, id))
if (id.match(/^ComponentJS:property:ComponentJS:socket:/))
if (typeof comp.__config[id] === "object")
sockets += "<code>" + id
.replace(/^ComponentJS:property:ComponentJS:socket:/, "") + "</code>, ";
sockets = sockets.replace(/, $/, "");
if (sockets === "")
sockets = "<span class=\"none\">none</span>";
html += "<tr>" +
"<td class=\"label\">Sockets:</td>" +
"<td class=\"value\">" + sockets + "</td>" +
"</tr>";
/* event subscriptions */
var subscriptions = "";
for (id in comp.__subscription)
if (_cs.isown(comp.__subscription, id))
if (typeof comp.__subscription[id] === "object")
if (!comp.__subscription[id].name.match(/^ComponentJS:/))
subscriptions += "<code>" + comp.__subscription[id].name + "</code>, ";
subscriptions = subscriptions.replace(/, $/, "");
if (subscriptions === "")
subscriptions = "<span class=\"none\">none</span>";
html += "<tr>" +
"<td class=\"label\">Event Subscriptions:</td>" +
"<td class=\"value\">" + subscriptions + "</td>" +
"</tr>";
/* service registrations */
var registrations = "";
for (id in comp.__subscription)
if (_cs.isown(comp.__subscription, id))
if (typeof comp.__subscription[id] === "object")
if (comp.__subscription[id].name.match(/^ComponentJS:service:/))
registrations += "<code>" + comp.__subscription[id].name
.replace(/^ComponentJS:service:/, "") + "</code>, ";
registrations = registrations.replace(/, $/, "");
if (registrations === "")
registrations = "<span class=\"none\">none</span>";
html += "<tr>" +
"<td class=\"label\">Service Registrations:</td>" +
"<td class=\"value\">" + registrations + "</td>" +
"</tr>";
/* hooks */
var hooks = "";
for (id in comp.__subscription)
if (_cs.isown(comp.__subscription, id))
if (typeof comp.__subscription[id] === "object")
if (comp.__subscription[id].name.match(/^ComponentJS:hook:/))
hooks += "<code>" + comp.__subscription[id].name
.replace(/^ComponentJS:hook:/, "") + "</code>, ";
hooks = hooks.replace(/, $/, "");
if (hooks === "")
hooks = "<span class=\"none\">none</span>";
html += "<tr>" +
"<td class=\"label\">Hook Points:</td>" +
"<td class=\"value\">" + hooks + "</td>" +
"</tr>";
/* finish and return table */
html = "<table>" + html + "</table>";
return html;
};
/*
* ComponentJS debugger window
*/
/* debugger window */
_cs.dbg = null;
/* debugger update state */
_cs.dbg_state_invalid = {
components: false,
states: false,
requests: false,
console: false
};
_cs.dbg_state_invalidate = function (name) {
_cs.dbg_state_invalid[name] = true;
};
/* debugger canvas: natural tree direction flag */
_cs.dbg_natural = false;
/* try to determine whether we are running instrumented,
i.e., whether the native Browser debugger is active/open */
$cs.debug_instrumented = function () {
return (
typeof GLOBAL !== "undefined" &&
GLOBAL.console &&
(GLOBAL.console.firebug || /* precision: Firefox Firebug */
(GLOBAL.outerHeight - GLOBAL.innerHeight) > 120) /* guessing: Chrome/Safari Inspector, IE Debugger */
);
};
/* try to determine whether Internet Explorer is used */
_cs.isIE = function () {
/* global navigator:false */
return (
typeof navigator !== "undefined" &&
navigator.appName === "Microsoft Internet Explorer" &&
navigator.userAgent.match(new RegExp("MSIE ([0-9]+[.0-9]*)"))
);
};
/* debugger window API entry point */
$cs.debug_window = function () {
/* determine parameters */
var params = $cs.params("debugger", arguments, {
enable: { pos: 0, def: null },
autoclose: { pos: 1, def: false },
name: { pos: 2, def: null },
width: { def: 800 },
height: { def: 600 },
natural: { def: false }
});
/* dispatch according to requested operation */
if (params.enable === null)
/* determine debugger state */
return (_cs.dbg !== null);
else if (params.enable) {
/* remember natural rendering flag */
_cs.dbg_natural = params.natural;
/* enable debugger */
if (_cs.dbg === null) {
/* determine (potentially application specific) title */
var title = "ComponentJS Debugger";
if (typeof params.name !== null)
title += " (" + params.name + ")";
/* create external debugger window */
var wname = title;
var wopts = "location=no,scrollbars=no,toolbars=no,menubar=no,status=no";
wopts += ",width=" + params.width + ",height=" + params.height;
if (_cs.isIE())
wname = wname.replace(/ /g, "_").replace(/[()]/g, "");
else
wopts += ",replace=yes";
_cs.dbg = GLOBAL.open("about:blank", wname, wopts);
if (_cs.isIE()) {
/* IE does not support reuse flag, so close old instance and open a fresh one */
_cs.dbg.close();
_cs.dbg = GLOBAL.open("about:blank", wname, wopts);
}
/* initialize the window content (deferred to avoid problems) */
/* global setTimeout:false */
setTimeout(_cs.hook("ComponentJS:plugin:debugger:settimeout:func", "pass", function () {
_cs.jq(_cs.dbg.document).ready(function () {
/* optionally automatically close debugger window with application window */
if (params.autoclose) {
_cs.jq(GLOBAL).bind("beforeunload", function () {
if (_cs.dbg !== null)
_cs.dbg.close();
});
}
/* generate view mask */
_cs.dbg_view_mask(title);
/* window-based resize support */
_cs.dbg_refresh();
_cs.jq(_cs.dbg).bind("resize", function () {
_cs.dbg_refresh();
});
/* avoid text selections (which confuse the grabbing) [non cross-browser event!] */
_cs.jq(".dbg", _cs.dbg.document).bind("selectstart", function (ev) {
ev.preventDefault();
return false;
});
/* grabbing-based resize support */
var grabbing = false;
var positioning = false;
var positioning_x = -1;
var positioning_y = -1;
_cs.jq(".dbg .grabber", _cs.dbg.document).bind("mousedown", function (ev) {
grabbing = true;
_cs.jq(".dbg .grabber", _cs.dbg.document).css("background-color", "red");
ev.preventDefault();
});
_cs.jq(".dbg", _cs.dbg.document).bind("mousemove", function (ev) {
if (grabbing) {
var offset = ev.pageY;
if (offset < 300)
offset = 300;
var vh = _cs.jq(_cs.dbg).height();
if (offset > vh - 100)
offset = vh - 100;
_cs.jq(".dbg .grabber", _cs.dbg.document).css("top", offset);
_cs.dbg_grabber_offset = offset;
ev.preventDefault();
}
else if (positioning) {
if (positioning_x === -1)
positioning_x = ev.pageX;
if (positioning_y === -1)
positioning_y = ev.pageY;
var offsetX = positioning_x - ev.pageX;
var offsetY = positioning_y - ev.pageY;
positioning_x = ev.pageX;
positioning_y = ev.pageY;
_cs.dbg_canvas_info.x += offsetX;
_cs.dbg_canvas_info.y += offsetY;
_cs.dbg_reposition();
}
});
_cs.jq(".dbg", _cs.dbg.document).bind("mouseup", function (ev) {
if (grabbing) {
_cs.jq(".dbg .grabber", _cs.dbg.document).css("background-color", "transparent");
_cs.dbg_refresh();
grabbing = false;
ev.preventDefault();
}
});
/* canvas export functionality */
_cs.jq(".dbg .exporter", _cs.dbg.document).bind("click", function (ev) {
var ctx = _cs.jq(".dbg .viewer canvas", _cs.dbg.document).get(0);
if (typeof ctx !== "undefined") {
var dataurl = ctx.toDataURL("image/png");
GLOBAL.open(dataurl);
}
ev.preventDefault();
return false;
});
/* canvas scroll and zoom functionality */
var zoom_step = 100;
var scroll_step = 10;
_cs.jq(".dbg .plus", _cs.dbg.document).bind("click", function (/* ev */) {
_cs.dbg_canvas_info.w += zoom_step;
_cs.dbg_canvas_info.h += zoom_step;
_cs.dbg_refresh();
});
_cs.jq(".dbg .minus", _cs.dbg.document).bind("click", function (/* ev */) {
_cs.dbg_canvas_info.w -= zoom_step;
_cs.dbg_canvas_info.h -= zoom_step;
_cs.dbg_refresh();
});
_cs.jq(".dbg .reset", _cs.dbg.document).bind("click", function (/* ev */) {
_cs.dbg_canvas_info.w = _cs.dbg_canvas_info.wmin;
_cs.dbg_canvas_info.h = _cs.dbg_canvas_info.hmin;
_cs.dbg_refresh();
});
_cs.jq(".dbg .viewer canvas", _cs.dbg.document).bind("mousedown", function (/* ev */) {
positioning = true;
positioning_x = -1;
positioning_y = -1;
});
_cs.jq(".dbg .viewer canvas", _cs.dbg.document).bind("mouseup", function (/* ev */) {
positioning = false;
});
_cs.jq(_cs.dbg.document).bind("keydown", function (ev) {
if (ev.keyCode === 43 || ev.keyCode === 107 || ev.keyCode === 187) {
/* key "+" pressed */
_cs.dbg_canvas_info.w += zoom_step;
_cs.dbg_canvas_info.h += zoom_step;
_cs.dbg_refresh();
}
else if (ev.keyCode === 45 || ev.keyCode === 109 || ev.keyCode === 189) {
/* key "-" pressed */
_cs.dbg_canvas_info.w -= zoom_step;
_cs.dbg_canvas_info.h -= zoom_step;
_cs.dbg_refresh();
}
else if (ev.keyCode === 48) {
/* key "0" pressed */
_cs.dbg_canvas_info.w = _cs.dbg_canvas_info.wmin;
_cs.dbg_canvas_info.h = _cs.dbg_canvas_info.hmin;
_cs.dbg_refresh();
}
else if (ev.keyCode === 37) {
/* key LEFT pressed */
_cs.dbg_canvas_info.x += scroll_step;
_cs.dbg_reposition();
}
else if (ev.keyCode === 38) {
/* key UP pressed */
_cs.dbg_canvas_info.y += scroll_step;
_cs.dbg_reposition();
}
else if (ev.keyCode === 39) {
/* key RIGHT pressed */
_cs.dbg_canvas_info.x -= scroll_step;
_cs.dbg_reposition();
}
else if (ev.keyCode === 40) {
/* key DOWN pressed */
_cs.dbg_canvas_info.y -= scroll_step;
_cs.dbg_reposition();
}
});
});
}), 500);
}
$cs.debug(3, "debugger enabled");
}
else {
/* disable debugger */
if (_cs.dbg !== null) {
$cs.debug(3, "debugger disabled");
_cs.dbg.close();
_cs.dbg = null;
}
}
};
/*
* ComponentJS debugger content rendering
*/
/* the grabber offset */
_cs.dbg_grabber_offset = -1;
/* the canvas size and position */
_cs.dbg_canvas_info = { x: 0, y: 0, w: -1, h: -1, wmin: -1, hmin: -1 };
/* refresh the browser rendering */
_cs.dbg_refresh = function () {
/* expand to viewport width/height */
var vw = _cs.jq(_cs.dbg).width();
var vh = _cs.jq(_cs.dbg).height();
_cs.jq(".dbg", _cs.dbg.document).width(vw).height(vh);
/* initially determine reasonable grabber offset */
_cs.jq(".dbg .grabber", _cs.dbg.document).height(
_cs.jq(".dbg .status", _cs.dbg.document).height());
if (_cs.dbg_grabber_offset === -1) {
var h = vh - _cs.jq(".dbg .header", _cs.dbg.document).height();
_cs.dbg_grabber_offset = Math.floor(h / 2) + _cs.jq(".dbg .header", _cs.dbg.document).height();
}
/* calculate viewer and console sizes based on grabber offset */
var h1 = _cs.dbg_grabber_offset - _cs.jq(".dbg .header", _cs.dbg.document).height();
var h2 = vh - _cs.dbg_grabber_offset + _cs.jq(".dbg .status", _cs.dbg.document).height();
_cs.jq(".dbg .viewer", _cs.dbg.document).height(h1);
_cs.jq(".dbg .console", _cs.dbg.document).height(h2);
_cs.jq(".dbg .infobox", _cs.dbg.document).height(h2);
_cs.jq(".dbg .infobox", _cs.dbg.document).css("top",
_cs.dbg_grabber_offset + _cs.jq(".dbg .status", _cs.dbg.document).height());
_cs.jq(".dbg .grabber", _cs.dbg.document).css("top", _cs.dbg_grabber_offset);
/* explicitly set the canvas size of the viewer */
_cs.dbg_canvas_info.wmin = vw;
_cs.dbg_canvas_info.hmin = h1;
if (_cs.dbg_canvas_info.w < _cs.dbg_canvas_info.wmin)
_cs.dbg_canvas_info.w = _cs.dbg_canvas_info.wmin;
if (_cs.dbg_canvas_info.h < _cs.dbg_canvas_info.hmin)
_cs.dbg_canvas_info.h = _cs.dbg_canvas_info.hmin;
_cs.jq(".dbg .viewer canvas", _cs.dbg.document)
.height(_cs.dbg_canvas_info.h).attr("height", _cs.dbg_canvas_info.h)
.width (_cs.dbg_canvas_info.w).attr("width", _cs.dbg_canvas_info.w);
_cs.dbg_reposition();
/* trigger an initial update */
_cs.dbg_update();
};
/* refresh the canvas positioning */
_cs.dbg_reposition = function () {
if (_cs.dbg_canvas_info.x < 0)
_cs.dbg_canvas_info.x = 0;
if (_cs.dbg_canvas_info.x > _cs.dbg_canvas_info.w - _cs.dbg_canvas_info.wmin)
_cs.dbg_canvas_info.x = _cs.dbg_canvas_info.w - _cs.dbg_canvas_info.wmin;
if (_cs.dbg_canvas_info.y < 0)
_cs.dbg_canvas_info.y = 0;
if (_cs.dbg_canvas_info.y > _cs.dbg_canvas_info.h - _cs.dbg_canvas_info.hmin)
_cs.dbg_canvas_info.y = _cs.dbg_canvas_info.h - _cs.dbg_canvas_info.hmin;
_cs.jq(".dbg .viewer canvas", _cs.dbg.document)
.css("top", -_cs.dbg_canvas_info.y)
.css("left", -_cs.dbg_canvas_info.x);
};
/* update the debugger rendering */
_cs.dbg_timer = null;
_cs.dbg_update = function () {
if (_cs.dbg === null)
return;
if (_cs.dbg_timer === null) {
/* global setTimeout:false */
_cs.dbg_timer = setTimeout(_cs.hook("ComponentJS:plugin:debugger:settimeout:func", "pass", function () {
_cs.dbg_update_once();
_cs.dbg_timer = null;
}), 250);
}
};
/* update the debugger rendering */
_cs.dbg_update_once = function () {
/* update console information */
if (_cs.dbg_state_invalid.console) {
_cs.jq(".dbg .console .text", _cs.dbg.document).html(_cs.dbg_logbook);
_cs.jq(".dbg .console", _cs.dbg.document).scrollTop(0);
_cs.dbg_state_invalid.console = true;
}
/* update component information */
if (_cs.dbg_state_invalid.components ||
_cs.dbg_state_invalid.requests ||
_cs.dbg_state_invalid.states ) {
/* walk the component tree to determine information about components */
var D, W, T;
if (_cs.dbg_state_invalid.components || _cs.dbg_state_invalid.states) {
D = _cs.root.walk_down(function (level, comp, D, depth_first) {
if (!depth_first) {
/* on downward walking, annotate component with its depth level
and calculcate the maximum depth level at all */
_cs.annotation(comp, "debugger_depth", level);
D = (level > D ? level : D);
}
else {
/* on upward walking, aggregate the width and total counts */
var width = 0;
var total = 0;
var children = comp.children();
for (var i = 0; i < children.length; i++) {
width += _cs.annotation(children[i], "debugger_width");
total += _cs.annotation(children[i], "debugger_total");
}
if (total === 0)
width++;
total++;
_cs.annotation(comp, "debugger_width", width);
_cs.annotation(comp, "debugger_total", total);
}
return D;
}, 1);
W = _cs.annotation(_cs.root, "debugger_width");
T = _cs.annotation(_cs.root, "debugger_total");
}
/* status update */
if (_cs.dbg_state_invalid.components || _cs.dbg_state_invalid.requests) {
/* determine pending state transition requests */
var reqs = 0;
for (var cid in _cs.state_requests) {
if (!_cs.isown(_cs.state_requests, cid))
continue;
reqs++;
}
/* update status line */
_cs.jq(".dbg .status .text", _cs.dbg.document).html(
"Created Components: <b>" + T + "</b>, " +
"Pending Transition Requests: <b>" + reqs + "</b>"
);
_cs.dbg_state_invalid.requests = true;
}
/* viewer update */
if (_cs.dbg_state_invalid.components || _cs.dbg_state_invalid.states) {
/* ensure the canvas (already) exists */
var ctx = _cs.jq(".dbg .viewer canvas", _cs.dbg.document).get(0);
if (typeof ctx === "undefined")
return;
ctx = ctx.getContext("2d");
/* determine canvas width/height and calculate grid width/height and offset width/height */
var ch = _cs.jq(".dbg .viewer canvas", _cs.dbg.document).height() - 20;
var cw = _cs.jq(".dbg .viewer canvas", _cs.dbg.document).width() - 20;
var gw = Math.floor(cw / W);
var gh = Math.floor(ch / (D + 1));
var ow = Math.floor(gw / 8);
var oh = Math.floor(gh / 4);
/* clear the canvas as we redraw everything */
ctx.clearRect(0, 0, cw, ch);
/* walk the component tree to draw each component (on upward steps only) */
var natural = _cs.dbg_natural;
_cs.root.walk_down(function (level, comp, X, depth_first) {
if (depth_first) {
/* grab previously calculated information */
var d = _cs.annotation(comp, "debugger_depth");
/* var w = _cs.annotation(comp, "debugger_width"); */
var t = _cs.annotation(comp, "debugger_total");
var my_x, my_y, my_w, my_h;
if (t === 1) {
/* CASE 1: leaf node */
my_x = 10 + gw * X++;
my_y = natural ? (ch - gh * d - gh + 10) : (gh * d - 10);
my_w = gw - ow;
my_h = gh - oh;
}
else {
/* CASE 2: intermediate node */
var children = comp.children();
/* determine boundaries for x position */
var minx = _cs.annotation(children[0], "debugger_x");
var miny = _cs.annotation(children[0], "debugger_y");
var maxx = minx;
var maxy = miny;
if (children.length > 1) {
maxx = _cs.annotation(children[children.length - 1], "debugger_x");
maxy = _cs.annotation(children[children.length - 1], "debugger_y");
}
/* calculate our information */
my_x = minx + Math.ceil((maxx - minx) / 2);
my_y = natural ? (ch - gh * d - gh + 10) : (gh * d - 10);
my_w = gw - ow;
my_h = gh - oh;
/* draw line from component to each child component */
for (var i = 0; i < children.length; i++) {
var child_x = _cs.annotation(children[i], "debugger_x");
var child_y = _cs.annotation(children[i], "debugger_y");
var child_w = _cs.annotation(children[i], "debugger_w");
/* var child_h = _cs.annotation(children[i], "debugger_h"); */
ctx.strokeStyle = "#888888";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(my_x + Math.ceil(my_w / 2),
my_y + (natural ? 0 : my_h));
ctx.lineTo(my_x + Math.ceil(my_w / 2),
my_y + (natural ? -Math.ceil(oh / 2) : my_h + Math.ceil(oh / 2)));
ctx.lineTo(child_x + Math.ceil(child_w / 2),
my_y + (natural ? -Math.ceil(oh / 2) : my_h + Math.ceil(oh / 2)));
ctx.lineTo(child_x + Math.ceil(child_w / 2),
child_y + (natural ? my_h : 0));
ctx.stroke();
}
}
/* determine type of component */
var type = "";
if ($cs.marked(comp.obj(), "view")) type += "V";
if ($cs.marked(comp.obj(), "model")) type += "M";
if ($cs.marked(comp.obj(), "controller")) type += "C";
if ($cs.marked(comp.obj(), "service")) type += "S";
/* draw component background */
var bg1, fg1, bg2, fg2;
if (type === "V") { bg1 = "#14426f"; fg1 = "#ffffff"; bg2 = "#2068b0"; fg2 = "#adcef0"; }
else if (type === "M") { bg1 = "#6f5014"; fg1 = "#ffffff"; bg2 = "#9a6f1c"; fg2 = "#e8c581"; }
else if (type === "S") { bg1 = "#e8e8e8"; fg1 = "#000000"; bg2 = "#ffffff"; fg2 = "#666666"; }
else { bg1 = "#444444"; fg1 = "#ffffff"; bg2 = "#777777"; fg2 = "#cccccc"; }
ctx.save();
ctx.fillStyle = bg1;
ctx.shadowColor = "#888888";
ctx.shadowBlur = 6;
ctx.shadowOffsetX = 1;
ctx.shadowOffsetY = 1;
ctx.fillRect(my_x, my_y, my_w, my_h);
ctx.restore();
ctx.fillStyle = bg2;
ctx.fillRect(my_x, my_y + my_h / 2, my_w, my_h / 2);
/* draw component state indicator bulp */
ctx.save();
ctx.fillStyle = _cs.states[comp.__state].color;
ctx.shadowColor = "#000000";
ctx.shadowBlur = 2;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.beginPath();
ctx.arc(
my_x + my_w - (my_h / 4) - 1,
my_y + 3 * (my_h / 4),
(my_h / 4) - 3,
0, 2 * Math.PI, true
);
ctx.closePath();
ctx.fill();
ctx.restore();
/* draw optional state guard indicator bulp */
var guarded = false;
for (var method in comp.__state_guards) {
if (typeof comp.__state_guards[method] === "number" &&
comp.__state_guards[method] !== 0 ) {
guarded = true;
break;
}
}
if (guarded) {
ctx.save();
ctx.fillStyle = "#ff0000";
ctx.shadowColor = "#000000";
ctx.shadowBlur = 2;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.beginPath();
ctx.arc(
my_x + my_w - 2 * (my_h / 4) - 1,
my_y + 3 * (my_h / 4),
(my_h / 4) - 3,
0, 2 * Math.PI, true
);
ctx.closePath();
ctx.fill();
ctx.restore();
}
/* common text rendering */
var renderText = function (text, color, x, y, width) {
ctx.fillStyle = color;
var metric = ctx.measureText(text);
if (metric.width > width) {
while (text !== "") {
metric = ctx.measureText(text + "...");
if (metric.width <= width) {
text += "...";
break;
}
text = text.substr(0, text.length - 1);
}
}
ctx.fillText(text, x, y, width);
};
/* draw component type indicators */
var width = 0;
if (type !== "") {
ctx.font = "bold " + ((my_h / 2) * 0.7) + "px Helvetica, Arial, sans-serif";
ctx.textBaseline = "top";
var metric = ctx.measureText(type);
renderText(type, fg2, my_x + my_w - metric.width - 4, my_y + 2, metric.width);
width = metric.width;
}
/* draw component information (name and state) */
ctx.font = ((my_h / 2) * 0.7) + "px Helvetica, Arial, sans-serif";
ctx.textBaseline = "top";
renderText(comp.name(), fg1, my_x + 4, my_y + 2, my_w - width - 8);
renderText(comp.state(), fg2, my_x + 4, my_y + (my_h / 2) + 2, my_w - (my_h / 2) - 8);
/* provide our information to the parent component */
_cs.annotation(comp, "debugger_x", my_x);
_cs.annotation(comp, "debugger_y", my_y);
_cs.annotation(comp, "debugger_w", my_w);
_cs.annotation(comp, "debugger_h", my_h);
}
/* pass-through the global X position */
return X;
}, 0);
/* component information on mouse click */
var infoboxed = false;
_cs.jq(".dbg .viewer canvas", _cs.dbg.document).bind("mousedown", function (ev) {
if (ev.target !== _cs.jq(".dbg .viewer canvas", _cs.dbg.document).get(0))
return;
infobox_event(ev);
infoboxed = true;
});
_cs.jq(".dbg .viewer canvas", _cs.dbg.document).bind("mousemove", function (ev) {
if (ev.target !== _cs.jq(".dbg .viewer canvas", _cs.dbg.document).get(0))
return;
if (infoboxed)
infobox_event(ev);
});
_cs.jq(".dbg .viewer canvas", _cs.dbg.document).bind("mouseup", function (ev) {
if (ev.target !== _cs.jq(".dbg .viewer canvas", _cs.dbg.document).get(0))
return;
_cs.jq(".dbg .infobox", _cs.dbg.document).css("display", "none");
infoboxed = false;
});
/* determine component on infobox event */
var infobox_event = function (ev) {
var mx = ev.offsetX;
var my = ev.offsetY;
var comp = null;
_cs.root.walk_down(function (level, comp_this, X, depth_first) {
if (depth_first) {
var x = _cs.annotation(comp_this, "debugger_x");
var y = _cs.annotation(comp_this, "debugger_y");
var w = _cs.annotation(comp_this, "debugger_w");
var h = _cs.annotation(comp_this, "debugger_h");
if (x <= mx && mx <= x + w &&
y <= my && my <= y + h)
comp = comp_this;
}
}, 0);
if (comp !== null) {
var html = _cs.dbg_infobox_content(comp);
_cs.jq(".dbg .infobox", _cs.dbg.document).html(html);
_cs.jq(".dbg .infobox", _cs.dbg.document).css("display", "block");
}
};
}
_cs.dbg_state_invalid.components = true;
_cs.dbg_state_invalid.states = true;
}
};
/*
* ComponentJS debugger hooking
*/
/* hook into internal logging */
_cs.latch("ComponentJS:log", function (msg) {
var logged = false;
if (_cs.dbg !== null) {
_cs.dbg_log(msg);
logged = true;
}
return logged;
});
/* hook into state changes */
_cs.latch("ComponentJS:state-change", function () {
_cs.dbg_update();
});
/* hook into state invalidation */
_cs.latch("ComponentJS:state-invalidate", function (name) {
_cs.dbg_state_invalidate(name);
});
});
/*
** ComponentJS -- Component System for JavaScript <http://componentjs.com>
** Copyright (c) 2009-2013 Ralf S. Engelschall <http://engelschall.com>
**
** This Source Code Form is subject to the terms of the Mozilla Public
** License, v. 2.0. If a copy of the MPL was not distributed with this
** file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/*
* This is a small ComponentJS plugin which adds some ExtJS-specific
* functionality to the otherwise fully UI toolkit independent ComponentJS
* core framework.
*/
/* global ComponentJS:false */
/* jshint unused:false */
ComponentJS.plugin("extjs", function (_cs, $cs, GLOBAL) {
/*
* SPECIALIZED EXTJS SOCKET SUPPORT
*/
/* define the extra trait for components */
var trait = $cs.trait({
protos: {
socket: function () {
/* determine parameters */
var params = $cs.params("socket", arguments, {
name: { def: "default" },
scope: { def: null },
ctx: { pos: 0, req: true },
plug: { pos: 1, def: null }, /* removed "req: true" */
unplug: { pos: 2, def: null }, /* removed "req: true" */
spool: { def: null },
type: { def: "default" } /* added */
});
/* create pass-through information */
var arg = _cs.extend({}, params);
delete arg.type;
/* optionally change behaviour */
if (params.type === "extjs") {
/* provide specialized ExtJS socket functionality */
arg.plug = function (el, comp) { this.add(el); };
arg.unplug = function (el, comp) { this.remove(el); };
}
/* pass-through execution to original/base method */
return this.base(arg);
}
}
});
/* mixin this trait to all components */
_cs.latch("ComponentJS:bootstrap:comp:mixin", function (mixins) {
mixins.push(trait);
});
});
/*
** ComponentJS -- Component System for JavaScript <http://componentjs.com>
** Copyright (c) 2009-2013 Ralf S. Engelschall <http://engelschall.com>
**
** This Source Code Form is subject to the terms of the Mozilla Public
** License, v. 2.0. If a copy of the MPL was not distributed with this
** file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/*
* This is a small ComponentJS plugin which adds some jQuery-specific
* functionality to the otherwise fully UI toolkit independent ComponentJS
* core framework.
*/
/* global ComponentJS:false */
/* global jQuery:false */
/* jshint unused:false */
ComponentJS.plugin("jquery", function (_cs, $cs, GLOBAL) {
/*
* SPECIALIZED JQUERY SOCKET SUPPORT
*/
/* define the extra trait for components */
var trait = $cs.trait({
protos: {
socket: function () {
/* determine parameters */
var params = $cs.params("socket", arguments, {
name: { def: "default" },
scope: { def: null },
ctx: { pos: 0, req: true },
plug: { pos: 1, def: null }, /* removed "req: true" */
unplug: { pos: 2, def: null }, /* removed "req: true" */
spool: { def: null },
type: { def: "default" } /* added */
});
/* create pass-through information */
var arg = _cs.extend({}, params);
delete arg.type;
/* optionally change behaviour */
if ( /* explicitly requested */
params.type === "jquery" ||
/* implicitly detected */
( params.type === "default" &&
typeof params.ctx.jquery === "string" &&
params.ctx.jquery.match(/^[0-9]+(?:\.[0-9]+)+$/) ) ) {
/* provide specialized jQuery socket functionality */
arg.plug = function (el, comp) { jQuery(this).append(el); };
arg.unplug = function (el, comp) { jQuery(el).remove(); };
}
/* pass-through execution to original/base method */
return this.base(arg);
}
}
});
/* mixin this trait to all components */
_cs.latch("ComponentJS:bootstrap:comp:mixin", function (mixins) {
mixins.push(trait);
});
});
//
// Generated on Sun Dec 16 2012 22:47:05 GMT-0500 (EST) by Nodejitsu, Inc (Using Codesurgeon).
// Version 1.1.9
//
(function (exports) {
/*
* browser.js: Browser specific functionality for director.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
if (!Array.prototype.filter) {
Array.prototype.filter = function(filter, that) {
var other = [], v;
for (var i = 0, n = this.length; i < n; i++) {
if (i in this && filter.call(that, v = this[i], i, this)) {
other.push(v);
}
}
return other;
};
}
if (!Array.isArray){
Array.isArray = function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
};
}
var dloc = document.location;
function dlocHashEmpty() {
// Non-IE browsers return '' when the address bar shows '#'; Director's logic
// assumes both mean empty.
return dloc.hash === '' || dloc.hash === '#';
}
var listener = {
mode: 'modern',
hash: dloc.hash,
history: false,
check: function () {
var h = dloc.hash;
if (h != this.hash) {
this.hash = h;
this.onHashChanged();
}
},
fire: function () {
if (this.mode === 'modern') {
this.history === true ? window.onpopstate() : window.onhashchange();
}
else {
this.onHashChanged();
}
},
init: function (fn, history) {
var self = this;
this.history = history;
if (!Router.listeners) {
Router.listeners = [];
}
function onchange(onChangeEvent) {
for (var i = 0, l = Router.listeners.length; i < l; i++) {
Router.listeners[i](onChangeEvent);
}
}
//note IE8 is being counted as 'modern' because it has the hashchange event
if ('onhashchange' in window && (document.documentMode === undefined
|| document.documentMode > 7)) {
// At least for now HTML5 history is available for 'modern' browsers only
if (this.history === true) {
// There is an old bug in Chrome that causes onpopstate to fire even
// upon initial page load. Since the handler is run manually in init(),
// this would cause Chrome to run it twise. Currently the only
// workaround seems to be to set the handler after the initial page load
// http://code.google.com/p/chromium/issues/detail?id=63040
setTimeout(function() {
window.onpopstate = onchange;
}, 500);
}
else {
window.onhashchange = onchange;
}
this.mode = 'modern';
}
else {
//
// IE support, based on a concept by Erik Arvidson ...
//
var frame = document.createElement('iframe');
frame.id = 'state-frame';
frame.style.display = 'none';
document.body.appendChild(frame);
this.writeFrame('');
if ('onpropertychange' in document && 'attachEvent' in document) {
document.attachEvent('onpropertychange', function () {
if (event.propertyName === 'location') {
self.check();
}
});
}
window.setInterval(function () { self.check(); }, 50);
this.onHashChanged = onchange;
this.mode = 'legacy';
}
Router.listeners.push(fn);
return this.mode;
},
destroy: function (fn) {
if (!Router || !Router.listeners) {
return;
}
var listeners = Router.listeners;
for (var i = listeners.length - 1; i >= 0; i--) {
if (listeners[i] === fn) {
listeners.splice(i, 1);
}
}
},
setHash: function (s) {
// Mozilla always adds an entry to the history
if (this.mode === 'legacy') {
this.writeFrame(s);
}
if (this.history === true) {
window.history.pushState({}, document.title, s);
// Fire an onpopstate event manually since pushing does not obviously
// trigger the pop event.
this.fire();
} else {
dloc.hash = (s[0] === '/') ? s : '/' + s;
}
return this;
},
writeFrame: function (s) {
// IE support...
var f = document.getElementById('state-frame');
var d = f.contentDocument || f.contentWindow.document;
d.open();
d.write("<script>_hash = '" + s + "'; onload = parent.listener.syncHash;<script>");
d.close();
},
syncHash: function () {
// IE support...
var s = this._hash;
if (s != dloc.hash) {
dloc.hash = s;
}
return this;
},
onHashChanged: function () {}
};
var Router = exports.Router = function (routes) {
if (!(this instanceof Router)) return new Router(routes);
this.params = {};
this.routes = {};
this.methods = ['on', 'once', 'after', 'before'];
this.scope = [];
this._methods = {};
this._insert = this.insert;
this.insert = this.insertEx;
this.historySupport = (window.history != null ? window.history.pushState : null) != null
this.configure();
this.mount(routes || {});
};
Router.prototype.init = function (r) {
var self = this;
this.handler = function(onChangeEvent) {
var newURL = onChangeEvent && onChangeEvent.newURL || window.location.hash;
var url = self.history === true ? self.getPath() : newURL.replace(/.*#/, '');
self.dispatch('on', url);
};
listener.init(this.handler, this.history);
if (this.history === false) {
if (dlocHashEmpty() && r) {
dloc.hash = r;
} else if (!dlocHashEmpty()) {
self.dispatch('on', dloc.hash.replace(/^#/, ''));
}
}
else {
var routeTo = dlocHashEmpty() && r ? r : !dlocHashEmpty() ? dloc.hash.replace(/^#/, '') : null;
if (routeTo) {
window.history.replaceState({}, document.title, routeTo);
}
// Router has been initialized, but due to the chrome bug it will not
// yet actually route HTML5 history state changes. Thus, decide if should route.
if (routeTo || this.run_in_init === true) {
this.handler();
}
}
return this;
};
Router.prototype.explode = function () {
var v = this.history === true ? this.getPath() : dloc.hash;
if (v.charAt(1) === '/') { v=v.slice(1) }
return v.slice(1, v.length).split("/");
};
Router.prototype.setRoute = function (i, v, val) {
var url = this.explode();
if (typeof i === 'number' && typeof v === 'string') {
url[i] = v;
}
else if (typeof val === 'string') {
url.splice(i, v, s);
}
else {
url = [i];
}
listener.setHash(url.join('/'));
return url;
};
//
// ### function insertEx(method, path, route, parent)
// #### @method {string} Method to insert the specific `route`.
// #### @path {Array} Parsed path to insert the `route` at.
// #### @route {Array|function} Route handlers to insert.
// #### @parent {Object} **Optional** Parent "routes" to insert into.
// insert a callback that will only occur once per the matched route.
//
Router.prototype.insertEx = function(method, path, route, parent) {
if (method === "once") {
method = "on";
route = function(route) {
var once = false;
return function() {
if (once) return;
once = true;
return route.apply(this, arguments);
};
}(route);
}
return this._insert(method, path, route, parent);
};
Router.prototype.getRoute = function (v) {
var ret = v;
if (typeof v === "number") {
ret = this.explode()[v];
}
else if (typeof v === "string"){
var h = this.explode();
ret = h.indexOf(v);
}
else {
ret = this.explode();
}
return ret;
};
Router.prototype.destroy = function () {
listener.destroy(this.handler);
return this;
};
Router.prototype.getPath = function () {
var path = window.location.pathname;
if (path.substr(0, 1) !== '/') {
path = '/' + path;
}
return path;
};
function _every(arr, iterator) {
for (var i = 0; i < arr.length; i += 1) {
if (iterator(arr[i], i, arr) === false) {
return;
}
}
}
function _flatten(arr) {
var flat = [];
for (var i = 0, n = arr.length; i < n; i++) {
flat = flat.concat(arr[i]);
}
return flat;
}
function _asyncEverySeries(arr, iterator, callback) {
if (!arr.length) {
return callback();
}
var completed = 0;
(function iterate() {
iterator(arr[completed], function(err) {
if (err || err === false) {
callback(err);
callback = function() {};
} else {
completed += 1;
if (completed === arr.length) {
callback();
} else {
iterate();
}
}
});
})();
}
function paramifyString(str, params, mod) {
mod = str;
for (var param in params) {
if (params.hasOwnProperty(param)) {
mod = params[param](str);
if (mod !== str) {
break;
}
}
}
return mod === str ? "([._a-zA-Z0-9-]+)" : mod;
}
function regifyString(str, params) {
var matches, last = 0, out = "";
while (matches = str.substr(last).match(/[^\w\d\- %@&]*\*[^\w\d\- %@&]*/)) {
last = matches.index + matches[0].length;
matches[0] = matches[0].replace(/^\*/, "([_.()!\\ %@&a-zA-Z0-9-]+)");
out += str.substr(0, matches.index) + matches[0];
}
str = out += str.substr(last);
var captures = str.match(/:([^\/]+)/ig), length;
if (captures) {
length = captures.length;
for (var i = 0; i < length; i++) {
str = str.replace(captures[i], paramifyString(captures[i], params));
}
}
return str;
}
function terminator(routes, delimiter, start, stop) {
var last = 0, left = 0, right = 0, start = (start || "(").toString(), stop = (stop || ")").toString(), i;
for (i = 0; i < routes.length; i++) {
var chunk = routes[i];
if (chunk.indexOf(start, last) > chunk.indexOf(stop, last) || ~chunk.indexOf(start, last) && !~chunk.indexOf(stop, last) || !~chunk.indexOf(start, last) && ~chunk.indexOf(stop, last)) {
left = chunk.indexOf(start, last);
right = chunk.indexOf(stop, last);
if (~left && !~right || !~left && ~right) {
var tmp = routes.slice(0, (i || 1) + 1).join(delimiter);
routes = [ tmp ].concat(routes.slice((i || 1) + 1));
}
last = (right > left ? right : left) + 1;
i = 0;
} else {
last = 0;
}
}
return routes;
}
Router.prototype.configure = function(options) {
options = options || {};
for (var i = 0; i < this.methods.length; i++) {
this._methods[this.methods[i]] = true;
}
this.recurse = options.recurse || this.recurse || false;
this.async = options.async || false;
this.delimiter = options.delimiter || "/";
this.strict = typeof options.strict === "undefined" ? true : options.strict;
this.notfound = options.notfound;
this.resource = options.resource;
this.history = options.html5history && this.historySupport || false;
this.run_in_init = this.history === true && options.run_handler_in_init !== false;
this.every = {
after: options.after || null,
before: options.before || null,
on: options.on || null
};
return this;
};
Router.prototype.param = function(token, matcher) {
if (token[0] !== ":") {
token = ":" + token;
}
var compiled = new RegExp(token, "g");
this.params[token] = function(str) {
return str.replace(compiled, matcher.source || matcher);
};
};
Router.prototype.on = Router.prototype.route = function(method, path, route) {
var self = this;
if (!route && typeof path == "function") {
route = path;
path = method;
method = "on";
}
if (Array.isArray(path)) {
return path.forEach(function(p) {
self.on(method, p, route);
});
}
if (path.source) {
path = path.source.replace(/\\\//ig, "/");
}
if (Array.isArray(method)) {
return method.forEach(function(m) {
self.on(m.toLowerCase(), path, route);
});
}
path = path.split(new RegExp(this.delimiter));
path = terminator(path, this.delimiter);
this.insert(method, this.scope.concat(path), route);
};
Router.prototype.dispatch = function(method, path, callback) {
var self = this, fns = this.traverse(method, path, this.routes, ""), invoked = this._invoked, after;
this._invoked = true;
if (!fns || fns.length === 0) {
this.last = [];
if (typeof this.notfound === "function") {
this.invoke([ this.notfound ], {
method: method,
path: path
}, callback);
}
return false;
}
if (this.recurse === "forward") {
fns = fns.reverse();
}
function updateAndInvoke() {
self.last = fns.after;
self.invoke(self.runlist(fns), self, callback);
}
after = this.every && this.every.after ? [ this.every.after ].concat(this.last) : [ this.last ];
if (after && after.length > 0 && invoked) {
if (this.async) {
this.invoke(after, this, updateAndInvoke);
} else {
this.invoke(after, this);
updateAndInvoke();
}
return true;
}
updateAndInvoke();
return true;
};
Router.prototype.invoke = function(fns, thisArg, callback) {
var self = this;
if (this.async) {
_asyncEverySeries(fns, function apply(fn, next) {
if (Array.isArray(fn)) {
return _asyncEverySeries(fn, apply, next);
} else if (typeof fn == "function") {
fn.apply(thisArg, fns.captures.concat(next));
}
}, function() {
if (callback) {
callback.apply(thisArg, arguments);
}
});
} else {
_every(fns, function apply(fn) {
if (Array.isArray(fn)) {
return _every(fn, apply);
} else if (typeof fn === "function") {
return fn.apply(thisArg, fns.captures || []);
} else if (typeof fn === "string" && self.resource) {
self.resource[fn].apply(thisArg, fns.captures || []);
}
});
}
};
Router.prototype.traverse = function(method, path, routes, regexp, filter) {
var fns = [], current, exact, match, next, that;
function filterRoutes(routes) {
if (!filter) {
return routes;
}
function deepCopy(source) {
var result = [];
for (var i = 0; i < source.length; i++) {
result[i] = Array.isArray(source[i]) ? deepCopy(source[i]) : source[i];
}
return result;
}
function applyFilter(fns) {
for (var i = fns.length - 1; i >= 0; i--) {
if (Array.isArray(fns[i])) {
applyFilter(fns[i]);
if (fns[i].length === 0) {
fns.splice(i, 1);
}
} else {
if (!filter(fns[i])) {
fns.splice(i, 1);
}
}
}
}
var newRoutes = deepCopy(routes);
newRoutes.matched = routes.matched;
newRoutes.captures = routes.captures;
newRoutes.after = routes.after.filter(filter);
applyFilter(newRoutes);
return newRoutes;
}
if (path === this.delimiter && routes[method]) {
next = [ [ routes.before, routes[method] ].filter(Boolean) ];
next.after = [ routes.after ].filter(Boolean);
next.matched = true;
next.captures = [];
return filterRoutes(next);
}
for (var r in routes) {
if (routes.hasOwnProperty(r) && (!this._methods[r] || this._methods[r] && typeof routes[r] === "object" && !Array.isArray(routes[r]))) {
current = exact = regexp + this.delimiter + r;
if (!this.strict) {
exact += "[" + this.delimiter + "]?";
}
match = path.match(new RegExp("^" + exact));
if (!match) {
continue;
}
if (match[0] && match[0] == path && routes[r][method]) {
next = [ [ routes[r].before, routes[r][method] ].filter(Boolean) ];
next.after = [ routes[r].after ].filter(Boolean);
next.matched = true;
next.captures = match.slice(1);
if (this.recurse && routes === this.routes) {
next.push([ routes.before, routes.on ].filter(Boolean));
next.after = next.after.concat([ routes.after ].filter(Boolean));
}
return filterRoutes(next);
}
next = this.traverse(method, path, routes[r], current);
if (next.matched) {
if (next.length > 0) {
fns = fns.concat(next);
}
if (this.recurse) {
fns.push([ routes[r].before, routes[r].on ].filter(Boolean));
next.after = next.after.concat([ routes[r].after ].filter(Boolean));
if (routes === this.routes) {
fns.push([ routes["before"], routes["on"] ].filter(Boolean));
next.after = next.after.concat([ routes["after"] ].filter(Boolean));
}
}
fns.matched = true;
fns.captures = next.captures;
fns.after = next.after;
return filterRoutes(fns);
}
}
}
return false;
};
Router.prototype.insert = function(method, path, route, parent) {
var methodType, parentType, isArray, nested, part;
path = path.filter(function(p) {
return p && p.length > 0;
});
parent = parent || this.routes;
part = path.shift();
if (/\:|\*/.test(part) && !/\\d|\\w/.test(part)) {
part = regifyString(part, this.params);
}
if (path.length > 0) {
parent[part] = parent[part] || {};
return this.insert(method, path, route, parent[part]);
}
if (!part && !path.length && parent === this.routes) {
methodType = typeof parent[method];
switch (methodType) {
case "function":
parent[method] = [ parent[method], route ];
return;
case "object":
parent[method].push(route);
return;
case "undefined":
parent[method] = route;
return;
}
return;
}
parentType = typeof parent[part];
isArray = Array.isArray(parent[part]);
if (parent[part] && !isArray && parentType == "object") {
methodType = typeof parent[part][method];
switch (methodType) {
case "function":
parent[part][method] = [ parent[part][method], route ];
return;
case "object":
parent[part][method].push(route);
return;
case "undefined":
parent[part][method] = route;
return;
}
} else if (parentType == "undefined") {
nested = {};
nested[method] = route;
parent[part] = nested;
return;
}
throw new Error("Invalid route context: " + parentType);
};
Router.prototype.extend = function(methods) {
var self = this, len = methods.length, i;
function extend(method) {
self._methods[method] = true;
self[method] = function() {
var extra = arguments.length === 1 ? [ method, "" ] : [ method ];
self.on.apply(self, extra.concat(Array.prototype.slice.call(arguments)));
};
}
for (i = 0; i < len; i++) {
extend(methods[i]);
}
};
Router.prototype.runlist = function(fns) {
var runlist = this.every && this.every.before ? [ this.every.before ].concat(_flatten(fns)) : _flatten(fns);
if (this.every && this.every.on) {
runlist.push(this.every.on);
}
runlist.captures = fns.captures;
runlist.source = fns.source;
return runlist;
};
Router.prototype.mount = function(routes, path) {
if (!routes || typeof routes !== "object" || Array.isArray(routes)) {
return;
}
var self = this;
path = path || [];
if (!Array.isArray(path)) {
path = path.split(self.delimiter);
}
function insertOrMount(route, local) {
var rename = route, parts = route.split(self.delimiter), routeType = typeof routes[route], isRoute = parts[0] === "" || !self._methods[parts[0]], event = isRoute ? "on" : rename;
if (isRoute) {
rename = rename.slice((rename.match(new RegExp(self.delimiter)) || [ "" ])[0].length);
parts.shift();
}
if (isRoute && routeType === "object" && !Array.isArray(routes[route])) {
local = local.concat(parts);
self.mount(routes[route], local);
return;
}
if (isRoute) {
local = local.concat(rename.split(self.delimiter));
local = terminator(local, self.delimiter);
}
self.insert(event, local, routes[route]);
}
for (var route in routes) {
if (routes.hasOwnProperty(route)) {
insertOrMount(route, path.slice(0));
}
}
};
}(typeof exports === "object" ? exports : window));
\ No newline at end of file
/*!
** jQuery Markup -- jQuery Template Based Markup Generation
** Copyright (c) 2013 Ralf S. Engelschall <rse@engelschall.com>
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* global jQuery: true */
(function ($) {
/* jshint -W014 */
/* the storage of all markup expansion functions */
var markup = {};
/* local context-sensitive markup-expansion API function */
$.fn.extend({
markup: function (id, data) {
var result = this;
this.each(function () {
var el = $.markup(id, data);
$(this).append(el);
result = el;
});
return result;
}
});
/* global context-free markup-expansion API function */
$.markup = function (id, data) {
if (typeof markup[id] === "undefined")
throw new Error("jquery: markup: ERROR: no such markup with id '" + id + "' found");
return $($.parseHTML($.markup.render(id, data)));
};
/* plugin version number */
$.markup.version = "1.0.26";
/* debug level */
$.markup.debug = 0;
var debug = function (level, msg) {
/* global console: true */
if ( $.markup.debug >= level
&& typeof console !== "undefined"
&& typeof console.log === "function")
console.log("jquery: markup: DEBUG[" + level + "]: " + msg);
};
var esctxt = function (txt) {
return "\"" +
txt.replace(/\r/g, "\\r")
.replace(/\n/g, "\\n")
.replace(/"/g, "\\\"") +
"\"";
};
/* template language registry */
var registry = {};
$.markup.register = function (lang) {
registry[lang.id] = lang;
};
/* compile a markup into an expansion function */
$.markup.compile = function (type, id, txt) {
debug(1, "compile: type=" + type + " id=" + id);
debug(4, "compile: txt=" + esctxt(txt));
/* sanity check usage */
if (typeof registry[type] === "undefined")
throw new Error("jquery: markup: ERROR: no template language registered under id '" + type + "'");
if (!registry[type].available())
throw new Error("jquery: markup: ERROR: template language '" +
type + "' (" + registry[type].name + ", " + registry[type].url + ") " +
"known but not found under run-time");
/* remove all leading and trailing whitespaces
(as usually one wants a single DOM element back without any surrounding text elements!) */
txt = txt.replace(/^\s+/, "").replace(/\s+$/, "");
/* compile with registered template engine */
markup[id] = registry[type].compile(txt);
};
/* parse a markup definition document */
$.markup.parse = function (txt, type) {
if (typeof type === "undefined")
type = "plain";
debug(3, "parse: type=" + type + " txt=" + esctxt(txt));
var section = [];
section.push({ txt: "", attr: { id: "", type: type, include: "false" }});
var m;
while (txt !== "") {
/* section opening tag */
if ((m = txt.match(/^<markup((?:\s+(?:id|type|include|wrap|trim)="[^"]*")+)\s*>/)) !== null) {
/* parse key="value" attributes */
debug(4, "parse: section opening tag: " + esctxt(m[0]));
var attr = {};
var txt2 = m[1], m2;
while ((m2 = txt2.match(/^\s+([a-z]+)="([^"]*)"/)) !== null) {
debug(4, "parse: section attribute: name=" + esctxt(m2[1]) + " value=" + esctxt(m2[2]));
attr[m2[1]] = m2[2].match(/^true|false$/) ? (m2[2] === "true") : m2[2];
txt2 = txt2.substr(m2[0].length);
}
if (typeof attr.id === "undefined")
throw new Error("jquery: markup: ERROR: missing required 'id' attribute");
if (attr.id.match(/^[^\/]+$/) === null)
throw new Error("jquery: markup: ERROR: invalid 'id' attribute");
if (section[section.length - 1].attr.id !== "")
attr.id = section[section.length - 1].attr.id + "/" + attr.id;
if (typeof attr.type === "undefined")
attr.type = section[section.length - 1].attr.type;
if (typeof attr.include === "undefined")
attr.include = false;
if (typeof attr.wrap === "undefined")
attr.wrap = false;
if (typeof attr.trim === "undefined")
attr.trim = false;
/* open new section */
section.push({ txt: "", attr: attr });
txt = txt.substr(m[0].length);
}
/* section closing tag */
else if ((m = txt.match(/^<\/markup\s*>/)) !== null) {
/* close current section */
debug(4, "parse: section closing tag: " + esctxt(m[0]));
var s = section.pop();
if (s.attr.wrap) {
/* optionally wrap markup with a root element and
a CSS class which is derived from the section id */
var clazz = s.attr.id.replace(/\//g, "-").replace(/[^a-zA-Z0-9_-]/g, "_");
s.txt = "<div class=\"" + clazz + "\">" + s.txt + "</div>";
debug(4, "post-process: wrapped markup: " + esctxt(s.txt));
}
if (s.attr.trim) {
/* optionally trim all whitespaces around tags */
var src = s.txt, dst = "", m3, i, matched;
var spec = [
/^\s*(<!--.+?-->)\s*/,
/^\s*(<[a-zA-Z_][a-zA-Z0-9_:-]*(?:\s+[a-z]+="[^"]*")*\s*\/?\s*>)\s*/,
/^\s*(<\/[a-zA-Z_][a-zA-Z0-9_:-]*\s*>)\s*/,
/^([^\s<]+)/,
/^(\s+)/
];
while (src !== "") {
matched = false;
for (i = 0; i < spec.length; i++) {
if ((m3 = src.match(spec[i])) !== null) {
dst += m3[1];
src = src.substr(m3[0].length);
matched = true;
break;
}
}
if (!matched)
throw new Error("jquery: markup: ERROR: during markup trimming: " +
"unrecognized next token at " + esctxt(src));
}
s.txt = dst;
debug(4, "post-process: trimmed markup: " + esctxt(s.txt));
}
$.markup.compile(s.attr.type, s.attr.id, s.txt);
if (s.attr.include)
section[section.length - 1].txt += s.txt;
txt = txt.substr(m[0].length);
}
/* plain-text between tags (arbitray long) */
else if ((m = txt.match(/^((?:.|\r?\n)+?)(?=<\/?markup|$)/)) !== null) {
/* append plain-text to current section */
debug(4, "parse: plain-text (arbitrary long): " + esctxt(m[0]));
section[section.length - 1].txt += m[1];
txt = txt.substr(m[0].length);
}
/* plain-text between tags (single character fallback) */
else {
/* append plain-text to current section */
debug(4, "parse: plain-text (single character fallback): " + esctxt(txt.substr(0, 1)));
section[section.length - 1].txt += txt.substr(0, 1);
txt = txt.substr(1);
}
}
};
/* queue markup loading requests */
var queue = [];
$.markup.queue = function (url, type) {
debug(2, "queue: url=" + url + " type=" + type);
queue.push({ url: url, type: type });
};
/* asynchronously load all queued markup */
$.markup.load = function (onDone) {
debug(1, "load: loading all queued markup requests");
var todo = queue; queue = [];
var done = 0;
for (var i = 0; i < todo.length; i++) {
var type = todo[i].type;
debug(2, "load: url=" + todo[i].url + " type=" + type);
$.get(todo[i].url, function (txt) {
$.markup.parse(txt, type);
done += 1;
if (done >= todo.length)
onDone();
});
}
};
/* render a compiled markup and return the textual result */
$.markup.render = function (id, data) {
if (typeof markup[id] === "undefined")
throw new Error("jquery: markup: ERROR: no such markup with id '" + id + "' found");
return markup[id](data);
};
/* automatically process all <link> tags */
/* global document: true */
$(document).ready(function () {
debug(1, "ready: processing all <link> tags");
$("head > link").each(function () {
var h = $(this).attr("href");
var r = $(this).attr("rel");
var t = $(this).attr("type");
if (typeof h !== "undefined" && h !== "" &&
typeof r !== "undefined" && r !== "" &&
typeof t !== "undefined" && t !== "" ) {
var mr = r.match(/^markup(?:\/([a-z]+))?$/);
var mt = t.match(/^text\/(?:html|x-markup-([a-z]+))$/);
if (mr !== null && mt !== null) {
var type = (typeof mr[1] === "string" && mr[1] !== "") ? mr[1] :
(typeof mt[1] === "string" && mt[1] !== "") ? mt[1] : "plain";
$.markup.queue(h, type);
}
}
});
});
/* helper function for checking that a function is available */
var isfn = function (path) {
/* global window: true */
var p = path.split(/\./);
var o = window;
for (var i = 0; i < p.length; i++) {
o = o[p[i]];
if ( (i < p.length - 1 && typeof o === "undefined")
|| (i === p.length - 1 && typeof o !== "function" ))
return false;
}
return true;
};
/* helper function for simple templating (structured value derefering only) */
var simple = function (txt) {
var func = "";
var outside = true;
var expr, m;
while (txt !== "") {
if (outside && txt.substr(0, 1) === "\"") {
func += "\\\"";
txt = txt.substr(1);
}
else if (outside && (m = txt.match(/^[\r\n]/)) !== null) {
func += "\\" + (m[0] === "\r" ? "r" : "n");
txt = txt.substr(m[0].length);
}
else if (outside && txt.substr(0, 2) === "{{") {
outside = false;
expr = "data";
txt = txt.substr(2);
}
else if (!outside && (m = txt.match(/^\s+/)) !== null) {
txt = txt.substr(m[0].length);
}
else if (!outside && (m = txt.match(/^(?:\.)?([a-zA-Z_][a-zA-Z0-9_]*)/)) !== null) {
expr = "deref(" + expr + ", \"" + m[1] + "\")";
txt = txt.substr(m[0].length);
}
else if (!outside && (m = txt.match(/^\[(\d+)\]/)) !== null) {
expr = "deref(" + expr + ", " + m[1] + ")";
txt = txt.substr(m[0].length);
}
else if (!outside && (m = txt.match(/^\["((?:\\"|.)*)"\]/)) !== null) {
expr = "deref(" + expr + ", \"" + m[1] + "\")";
txt = txt.substr(m[0].length);
}
else if (!outside && (m = txt.match(/^\['((?:\\'|.)*)'\]/)) !== null) {
expr = "deref(" + expr + ", '" + m[1] + "')";
txt = txt.substr(m[0].length);
}
else if (!outside && txt.substr(0, 2) === "}}") {
func += "\" + " + expr + " + \"";
outside = true;
txt = txt.substr(2);
}
else if (outside && (m = txt.match(/^(.+?)(?=\r|\n|"|\{\{|$)/)) !== null) {
func += m[1];
txt = txt.substr(m[0].length);
}
else {
func += txt.substr(0, 1);
txt = txt.substr(1);
}
}
/* jshint -W054 */
return new Function("data",
"var deref = function (obj, sel) {" +
"return (typeof obj === \"object\" ? obj[sel] : undefined);" +
"};" +
"return \"" + func + "\";"
);
};
/* helper function for registering a template engine */
var reg = function (id, name, url, func, comp) {
$.markup.register({
id: id,
name: name,
url: url,
available: (typeof func === "function" ? func : function () { return isfn(func); }),
compile: comp
});
};
/* Plain HTML (efficient: pass-through only, incomplete: no data) */
reg("plain", "Plain HTML", "-", function () { return true; },
function (txt) { return function (/* data */) { return txt; }; });
/* Simple HTML (efficient: pre-compilation, complete: data support) */
reg("simple", "Simple HTML", "-", function () { return true; },
function (txt) { return simple(txt); });
/* Handlebars (efficient: pre-compilation, complete: data support) */
reg("handlebars", "Handlebars", "http://handlebarsjs.com/", "Handlebars.compile",
function (txt) { /* global Handlebars: true */ return Handlebars.compile(txt); });
/* Emblem (indented Handlebars) (efficient: pre-compilation, complete: data support) */
reg("emblem", "Emblem", "http://emblemjs.com/", function () { return isfn("Handlebars") && isfn("Emblem.compile"); },
function (txt) { /* global Emblem: true */ return Emblem.compile(Handlebars, txt); });
/* DUST (efficient: pre-compilation, complete: data support) */
reg("dust", "DUST", "http://linkedin.github.io/dustjs/", "dust.compile",
function (txt) { /* global dust: true */ return dust.compile(txt); });
/* Jade (efficient: pre-compilation, complete: data support) */
reg("jade", "Jade", "http://jade-lang.com/", "jade.compile",
function (txt) { /* global jade: true */ return jade.compile(txt); });
/* Mustache (efficient: pre-compilation, complete: data support) */
reg("mustache", "Mustache", "http://mustache.github.io/", "Mustache.compile",
function (txt) { /* global Mustache: true */ return Mustache.compile(txt); });
/* Walrus (efficient: pre-compilation, complete: data support) */
reg("walrus", "Walrus", "http://documentup.com/jeremyruppel/walrus/", "Walrus.Parser.parse",
function (txt) { /* global Walrus: true */ var tmpl = Walrus.Parser.parse(txt);
return function (data) { return tmpl.compile(data); }; });
/* HAML-JS (efficient: pre-compilation, complete: data support) */
reg("haml", "HAML-JS", "https://github.com/creationix/haml-js", "Haml",
function (txt) { /* global Haml: true */ return Haml(txt); });
/* doT (efficient: pre-compilation, complete: data support) */
reg("dot", "doT", "http://olado.github.io/doT/", "doT.template",
function (txt) { /* global doT: true */ return doT.template(txt); });
/* rssi (efficient: pre-compilation, complete: data support) */
reg("rssi", "rssi", "https://github.com/mvasilkov/rssi", "fmt",
function (txt) { /* global fmt: true */ return fmt(txt); });
/* Hogan (efficient: pre-compilation, complete: data support) */
reg("hogan", "Twitter Hogan", "http://twitter.github.io/hogan.js/", "hogan.compile",
function (txt) { /* global hogan: true */ var tmpl = hogan.compile(txt);
return function (data) { return tmpl.render(data); }; });
/* Underscore Template (efficient: pre-compilation, complete: data support) */
reg("underscore", "Underscore Template", "http://underscorejs.org/", "_.template",
function (txt) { /* global _: true */ return _.template(txt); });
/* Jiko (efficient: on-the-fly compilation, complete: data support) */
reg("jiko", "Jiko", "http://jiko.neoname.eu/", "jiko.loadTemplate",
function (txt) { /* global jiko: true */ return jiko.loadTemplate(txt); });
/* Qatrix Template (efficient: cached on-the-fly compilation, complete: data support) */
reg("qatrix", "Qatrix Template", "http://qatrix.com/", "Qatrix.$template",
function (txt) { return function (data) { /* global Qatrix: true */ return Qatrix.template(txt, data); }; });
/* Teddy (efficient: pre-compilation, complete: data support) */
reg("teddy", "Teddy", "https://github.com/kethinov/teddy", "teddy.compile",
function (txt) { /* global teddy: true */ var name = txt /* hack */; teddy.compile(txt, name);
return function (data) { return teddy.render(teddy.compiledTemplates[name], data); }; });
/* EJS (efficient: pre-compilation, complete: data support) */
reg("ejs", "EJS", "http://embeddedjs.com/", "EJS",
function (txt) { /* global EJS: true */ var tmpl = EJS({ text: txt });
return function (data) { return tmpl.render(data); }; });
/* JST (efficient: pre-compilation, complete: data support) */
reg("jst", "JST", "http://code.google.com/p/trimpath/wiki/JavaScriptTemplates", "TrimPath.parseTemplate",
function (txt) { /* global TrimPath: true */ var tmpl = TrimPath.parseTemplate(txt);
return function (data) { return tmpl.process(data); }; });
/* Nunjucks (efficient: on-the-fly compilation, complete: data support) */
reg("nunjucks", "Nunjucks", "http://nunjucks.jlongster.com/", "nunjucks.Template",
function (txt) { /* global nunjucks: true */ var tmpl = new nunjucks.Template(txt);
return function (data) { return tmpl.render(data); }; });
/* Markup.js (inefficient: on-the-fly compilation, complete: data support) */
reg("markup", "Markup.js", "https://github.com/adammark/Markup.js/", "Mark.up",
function (txt) { return function (data) { /* global Mark: true */ return Mark.up(txt, data); }; });
/* Plates (inefficient: on-the-fly compilation, complete: data support) */
reg("plates", "Plates", "https://github.com/flatiron/plates", "Plates.bind",
function (txt) { return function (data) { /* global Plates: true */ return Plates.bind(txt, data); }; });
/* Emmet markup (inefficient: on-the-fly compilation, incomplete: no data support) */
reg("markup", "Markup.js", "http://emmet.io/", "emmet.expandAbbreviation",
function (txt) { return function (/* data */) { /* global emmet: true */ return emmet.expandAbbreviation(txt); }; });
})(jQuery);
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
html,
body {
margin: 0;
padding: 0;
}
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
color: inherit;
-webkit-appearance: none;
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
background: #eaeaea url('bg.png');
color: #4d4d4d;
width: 550px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased;
}
button,
input[type="checkbox"] {
outline: 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);
}
#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;
}
#todoapp input::-moz-placeholder {
font-style: italic;
color: #a9a9a9;
}
#todoapp h1 {
position: absolute;
top: -120px;
width: 100%;
font-size: 70px;
font-weight: bold;
text-align: center;
color: #b3b3b3;
color: rgba(255, 255, 255, 0.3);
text-shadow: -1px -1px rgba(0, 0, 0, 0.2);
-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,
.edit {
position: relative;
margin: 0;
width: 100%;
font-size: 24px;
font-family: inherit;
line-height: 1.4em;
border: 0;
outline: none;
color: inherit;
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 {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.02);
z-index: 2;
box-shadow: none;
}
#main {
position: relative;
z-index: 2;
border-top: 1px dotted #adadad;
}
label[for='toggle-all'] {
display: none;
}
#toggle-all {
position: absolute;
top: -42px;
left: -4px;
width: 40px;
text-align: center;
/* Mobile Safari */
border: none;
}
#toggle-all:before {
content: '»';
font-size: 28px;
color: #d9d9d9;
padding: 0 25px 7px;
}
#toggle-all:checked:before {
color: #737373;
}
#todo-list {
margin: 0;
padding: 0;
list-style: none;
}
#todo-list li {
position: relative;
font-size: 24px;
border-bottom: 1px dotted #ccc;
}
#todo-list li:last-child {
border-bottom: none;
}
#todo-list li.editing {
border-bottom: none;
padding: 0;
}
#todo-list li.editing .edit {
display: block;
width: 506px;
padding: 13px 17px 12px 17px;
margin: 0 0 0 43px;
}
#todo-list li.editing .view {
display: none;
}
#todo-list li .toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
/* Mobile Safari */
border: none;
-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:checked:after {
color: #85ada7;
text-shadow: 0 1px 0 #669991;
bottom: 1px;
position: relative;
}
#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;
text-decoration: line-through;
}
#todo-list li .destroy {
display: none;
position: absolute;
top: 0;
right: 10px;
bottom: 0;
width: 40px;
height: 40px;
margin: auto 0;
font-size: 22px;
color: #a88a8a;
-webkit-transition: all 0.2s;
transition: all 0.2s;
}
#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:after {
content: '✖';
}
#todo-list li:hover .destroy {
display: block;
}
#todo-list li .edit {
display: none;
}
#todo-list li.editing:last-child {
margin-bottom: -1px;
}
#footer {
color: #777;
padding: 0 15px;
position: absolute;
right: 0;
bottom: -31px;
left: 0;
height: 20px;
z-index: 1;
text-align: center;
}
#footer:before {
content: '';
position: absolute;
right: 0;
bottom: 31px;
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);
}
#todo-count {
float: left;
text-align: left;
}
#filters {
margin: 0;
padding: 0;
list-style: none;
position: absolute;
right: 0;
left: 0;
}
#filters li {
display: inline;
}
#filters li a {
color: #83756f;
margin: 2px;
text-decoration: none;
}
#filters li a.selected {
font-weight: bold;
}
#clear-completed {
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);
}
#clear-completed:hover {
background: rgba(0, 0, 0, 0.15);
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3);
}
#info {
margin: 65px auto 0;
color: #a6a6a6;
font-size: 12px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
text-align: center;
}
#info a {
color: inherit;
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox and Opera
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
#toggle-all,
#todo-list li .toggle {
background: none;
}
#todo-list li .toggle {
height: 40px;
}
#toggle-all {
top: -56px;
left: -15px;
width: 65px;
height: 41px;
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
-webkit-appearance: none;
appearance: none;
}
}
.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;
}
.learn-bar #todoapp {
width: 550px;
margin: 130px auto 40px auto;
}
}
(function () {
'use strict';
// Underscore's Template Module
// Courtesy of underscorejs.org
var _ = (function (_) {
_.defaults = function (object) {
if (!object) {
return object;
}
for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {
var iterable = arguments[argsIndex];
if (iterable) {
for (var key in iterable) {
if (object[key] == null) {
object[key] = iterable[key];
}
}
}
}
return object;
}
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g
};
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
var noMatch = /(.)^/;
// Certain characters need to be escaped so that they can be put into a
// string literal.
var escapes = {
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function(text, data, settings) {
var render;
settings = _.defaults({}, settings, _.templateSettings);
// Combine delimiters into one regular expression via alternation.
var matcher = new RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source
].join('|') + '|$', 'g');
// Compile the template source, escaping string literals appropriately.
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset)
.replace(escaper, function(match) { return '\\' + escapes[match]; });
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
}
if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
}
if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
index = offset + match.length;
return match;
});
source += "';\n";
// If a variable is not specified, place data values in local scope.
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + "return __p;\n";
try {
render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
}
if (data) return render(data, _);
var template = function(data) {
return render.call(this, data, _);
};
// Provide the compiled function source as a convenience for precompilation.
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
return template;
};
return _;
})({});
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 redirect() {
if (location.hostname === 'tastejs.github.io') {
location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com');
}
}
function findRoot() {
var base = location.href.indexOf('examples/');
return location.href.substr(0, base);
}
function getFile(file, callback) {
if (!location.host) {
return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.');
}
var xhr = new XMLHttpRequest();
xhr.open('GET', findRoot() + file, true);
xhr.send();
xhr.onload = function () {
if (xhr.status === 200 && callback) {
callback(xhr.responseText);
}
};
}
function Learn(learnJSON, config) {
if (!(this instanceof Learn)) {
return new Learn(learnJSON, config);
}
var template, framework;
if (typeof learnJSON !== 'object') {
try {
learnJSON = JSON.parse(learnJSON);
} catch (e) {
return;
}
}
if (config) {
template = config.template;
framework = config.framework;
}
if (!template && learnJSON.templates) {
template = learnJSON.templates.todomvc;
}
if (!framework && document.querySelector('[data-framework]')) {
framework = document.querySelector('[data-framework]').dataset.framework;
}
this.template = template;
if (learnJSON.backend) {
this.frameworkJSON = learnJSON.backend;
this.append({
backend: true
});
} else if (learnJSON[framework]) {
this.frameworkJSON = learnJSON[framework];
this.append();
}
}
Learn.prototype.append = function (opts) {
var aside = document.createElement('aside');
aside.innerHTML = _.template(this.template, this.frameworkJSON);
aside.className = 'learn';
if (opts && opts.backend) {
// Remove demo link
var sourceLinks = aside.querySelector('.source-links');
var heading = sourceLinks.firstElementChild;
var sourceLink = sourceLinks.lastElementChild;
// Correct link path
var href = sourceLink.getAttribute('href');
sourceLink.setAttribute('href', href.substr(href.lastIndexOf('http')));
sourceLinks.innerHTML = heading.outerHTML + sourceLink.outerHTML;
} else {
// Localize demo links
var demoLinks = aside.querySelectorAll('.demo-link');
Array.prototype.forEach.call(demoLinks, function (demoLink) {
if (demoLink.getAttribute('href').substr(0, 4) !== 'http') {
demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href'));
}
});
}
document.body.className = (document.body.className + ' learn-bar').trim();
document.body.insertAdjacentHTML('afterBegin', aside.outerHTML);
};
redirect();
getFile('learn.json', Learn);
})();
/*
* UUID-js: A js library to generate and parse UUIDs, TimeUUIDs and generate
* TimeUUID based on dates for range selections.
* @see http://www.ietf.org/rfc/rfc4122.txt
**/
function UUIDjs() {
}
UUIDjs.maxFromBits = function(bits) {
return Math.pow(2, bits);
};
UUIDjs.limitUI04 = UUIDjs.maxFromBits(4);
UUIDjs.limitUI06 = UUIDjs.maxFromBits(6);
UUIDjs.limitUI08 = UUIDjs.maxFromBits(8);
UUIDjs.limitUI12 = UUIDjs.maxFromBits(12);
UUIDjs.limitUI14 = UUIDjs.maxFromBits(14);
UUIDjs.limitUI16 = UUIDjs.maxFromBits(16);
UUIDjs.limitUI32 = UUIDjs.maxFromBits(32);
UUIDjs.limitUI40 = UUIDjs.maxFromBits(40);
UUIDjs.limitUI48 = UUIDjs.maxFromBits(48);
UUIDjs.randomUI04 = function() {
return Math.round(Math.random() * UUIDjs.limitUI04);
};
UUIDjs.randomUI06 = function() {
return Math.round(Math.random() * UUIDjs.limitUI06);
};
UUIDjs.randomUI08 = function() {
return Math.round(Math.random() * UUIDjs.limitUI08);
};
UUIDjs.randomUI12 = function() {
return Math.round(Math.random() * UUIDjs.limitUI12);
};
UUIDjs.randomUI14 = function() {
return Math.round(Math.random() * UUIDjs.limitUI14);
};
UUIDjs.randomUI16 = function() {
return Math.round(Math.random() * UUIDjs.limitUI16);
};
UUIDjs.randomUI32 = function() {
return Math.round(Math.random() * UUIDjs.limitUI32);
};
UUIDjs.randomUI40 = function() {
return (0 | Math.random() * (1 << 30)) + (0 | Math.random() * (1 << 40 - 30)) * (1 << 30);
};
UUIDjs.randomUI48 = function() {
return (0 | Math.random() * (1 << 30)) + (0 | Math.random() * (1 << 48 - 30)) * (1 << 30);
};
UUIDjs.paddedString = function(string, length, z) {
string = String(string);
z = (!z) ? '0' : z;
var i = length - string.length;
for (i; i > 0; i >>>= 1, z += z) {
if (i & 1) {
string = z + string;
}
}
return string;
};
UUIDjs.prototype.fromParts = function(timeLow, timeMid, timeHiAndVersion, clockSeqHiAndReserved, clockSeqLow, node) {
this.version = (timeHiAndVersion >> 12) & 0xF;
this.hex = UUIDjs.paddedString(timeLow.toString(16), 8)
+ '-'
+ UUIDjs.paddedString(timeMid.toString(16), 4)
+ '-'
+ UUIDjs.paddedString(timeHiAndVersion.toString(16), 4)
+ '-'
+ UUIDjs.paddedString(clockSeqHiAndReserved.toString(16), 2)
+ UUIDjs.paddedString(clockSeqLow.toString(16), 2)
+ '-'
+ UUIDjs.paddedString(node.toString(16), 12);
return this;
};
UUIDjs.prototype.toString = function() {
return this.hex;
};
UUIDjs.prototype.toURN = function() {
return 'urn:uuid:' + this.hex;
};
UUIDjs.prototype.toBytes = function() {
var parts = this.hex.split('-');
var ints = [];
var intPos = 0;
var i = 0;
for (i; i < parts.length; i++) {
var j = 0;
for (j; j < parts[i].length; j+=2) {
ints[intPos++] = parseInt(parts[i].substr(j, 2), 16);
}
}
return ints;
};
UUIDjs.prototype.equals = function(uuid) {
if (!(uuid instanceof UUID)) {
return false;
}
if (this.hex !== uuid.hex) {
return false;
}
return true;
};
UUIDjs.getTimeFieldValues = function(time) {
var ts = time - Date.UTC(1582, 9, 15);
var hm = ((ts / 0x100000000) * 10000) & 0xFFFFFFF;
return { low: ((ts & 0xFFFFFFF) * 10000) % 0x100000000,
mid: hm & 0xFFFF, hi: hm >>> 16, timestamp: ts };
};
UUIDjs._create4 = function() {
return new UUIDjs().fromParts(
UUIDjs.randomUI32(),
UUIDjs.randomUI16(),
0x4000 | UUIDjs.randomUI12(),
0x80 | UUIDjs.randomUI06(),
UUIDjs.randomUI08(),
UUIDjs.randomUI48()
);
};
UUIDjs._create1 = function() {
var now = new Date().getTime();
var sequence = UUIDjs.randomUI14();
var node = (UUIDjs.randomUI08() | 1) * 0x10000000000 + UUIDjs.randomUI40();
var tick = UUIDjs.randomUI04();
var timestamp = 0;
var timestampRatio = 1/4;
if (now !== timestamp) {
if (now < timestamp) {
sequence++;
}
timestamp = now;
tick = UUIDjs.randomUI04();
} else if (Math.random() < timestampRatio && tick < 9984) {
tick += 1 + UUIDjs.randomUI04();
} else {
sequence++;
}
var tf = UUIDjs.getTimeFieldValues(timestamp);
var tl = tf.low + tick;
var thav = (tf.hi & 0xFFF) | 0x1000;
sequence &= 0x3FFF;
var cshar = (sequence >>> 8) | 0x80;
var csl = sequence & 0xFF;
return new UUIDjs().fromParts(tl, tf.mid, thav, cshar, csl, node);
};
UUIDjs.create = function(version) {
version = version || 4;
return this['_create' + version]();
};
UUIDjs.fromTime = function(time, last) {
last = (!last) ? false : last;
var tf = UUIDjs.getTimeFieldValues(time);
var tl = tf.low;
var thav = (tf.hi & 0xFFF) | 0x1000; // set version '0001'
if (last === false) {
return new UUIDjs().fromParts(tl, tf.mid, thav, 0, 0, 0);
}
return new UUIDjs().fromParts(tl, tf.mid, thav, 0x80 | UUIDjs.limitUI06, UUIDjs.limitUI08 - 1, UUIDjs.limitUI48 - 1);
};
UUIDjs.firstFromTime = function(time) {
return UUIDjs.fromTime(time, false);
};
UUIDjs.lastFromTime = function(time) {
return UUIDjs.fromTime(time, true);
};
UUIDjs.fromURN = function(strId) {
var r, p = /^(?:urn:uuid:|\{)?([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{2})([0-9a-f]{2})-([0-9a-f]{12})(?:\})?$/i;
if ((r === p.exec(strId))) {
return new UUIDjs().fromParts(parseInt(r[1], 16), parseInt(r[2], 16),
parseInt(r[3], 16), parseInt(r[4], 16),
parseInt(r[5], 16), parseInt(r[6], 16));
}
return null;
};
UUIDjs.fromBytes = function(ints) {
var str = '';
var pos = 0;
var parts = [4, 2, 2, 2, 6];
var i = 0;
if (ints.length < 5) {
return null;
}
for (i; i < parts.length; i++) {
var j = 0;
for (j; j < parts[i]; j++) {
var octet = ints[pos++].toString(16);
if (octet.length === 1) {
octet = '0' + octet;
}
str += octet;
}
if (parts[i] !== 6) {
str += '-';
}
}
return UUIDjs.fromURN(str);
};
UUIDjs.fromBinary = function(binary) {
var ints = [];
var i = 0;
for (i; i < binary.length; i++) {
ints[i] = binary.charCodeAt(i);
if (ints[i] > 255 || ints[i] < 0) {
throw new Error('Unexpected byte in binary data.');
}
}
return UUIDjs.fromBytes(ints);
};
<!doctype html>
<html lang="en" data-framework="componentjs">
<head>
<meta charset="utf-8">
<title>ComponentJS • TodoMVC</title>
<!-- load third-party libraries -->
<script src="bower_components/lodash/dist/lodash.js"></script>
<script src="bower_components/uuid-js/lib/uuid.js"></script>
<script src="bower_components/nunjucks/browser/nunjucks.js"></script>
<script src="bower_components/jquery/jquery.js"></script>
<script src="bower_components/jquery-markup/jquery.markup.js"></script>
<script src="bower_components/director/build/director.js"></script>
<script src="bower_components/componentjs/component.js"></script>
<script src="bower_components/componentjs/component.plugin.jquery.js"></script>
<!-- load and call application initialization code -->
<script src="app/app.js"></script>
<script>app.boot.init()</script>
<!-- load the service and datamodel parts -->
<script src="app/app-dm.js"></script>
<script src="app/app-sv.js"></script>
<!-- load the root UI component -->
<script src="app/app-ui-constants.js"></script>
<script src="app/app-ui-composite-root.js"></script>
<link href="app/app-ui-composite-root-style.css" rel="stylesheet">
<!-- load the main UI component -->
<script src="app/app-ui-composite-main.js"></script>
<link href="app/app-ui-composite-main-mask.html" rel="markup" type="text/x-markup-nunjucks">
<link href="app/app-ui-composite-main-style.css" rel="stylesheet">
<!-- load the todo UI component -->
<script src="app/app-ui-widget-todo-view.js"></script>
<script src="app/app-ui-widget-todo-model.js"></script>
<link href="app/app-ui-widget-todo-mask.html" rel="markup" type="text/x-markup-nunjucks">
<link href="app/app-ui-widget-todo-style.css" rel="stylesheet">
<!-- load required todomvc.com integration functions -->
<link href="bower_components/todomvc-common/base.css" rel="stylesheet">
<script src="bower_components/todomvc-common/base.js"></script>
<!-- call application main code -->
<script>app.boot.main()</script>
</head>
<body>
</body>
</html>
# ComponentJS TodoMVC Example
## About ComponentJS
> ComponentJS is a stand-alone MPL-licensed Open Source library for
JavaScript, providing a powerful Component System for hierarchically
structuring the User-Interface (UI) dialogs of complex HTML5-based Rich
Clients (aka Single-Page-Apps) — under maximum applied Separation
of Concerns (SoC) architecture principle, through optional Model,
View and Controller component roles, with sophisticated hierarchical
Event, Service, Hook, Model, Socket and Property mechanisms, and fully
independent and agnostic of the particular UI widget toolkit.
> _[ComponentJS &mdash; componentjs.com](http://componentjs.com)_
## Third-Party Libraries
This ComponentJS TodoMVC Example uses the following libraries and frameworks
(which are all installed through Bower):
- [ComponentJS](http://componentjs.com/) 1.0.1<br/>
The MVC framework.
- [jQuery](http://jquery.com/) 2.0.3<br/>
The DOM manipulation and eventing library.
- [jQuery-Markup](http://plugins.jquery.com/markup/) 1.0.26<br/>
The view mask template integration library.
- [Nunjucks](http://jlongster.github.io/nunjucks/) 1.0.0<br/>
The view mask template engine library.
- [Flatiron Director](https://github.com/flatiron/director) 1.2.0<br/>
The URL routing library.
- [UUID.js](https://github.com/aurigadl/uuid-js) 0.7.5<br/>
The UUID generation library.
- [Lo-Dash](http://lodash.com/) 2.3.0<br/>
The collection utility library.
- [todomvc-common](https://github.com/tastejs/todomvc-common) 0.1.9<br/>
The background image and TodoMVC.com integration code.
## Hints about the ComponentJS TodoMVC Example
This ComponentJS TodoMVC Example tries to
closely follow the official [TodoMVC App Specification](https://github.com/tastejs/todomvc/blob/master/app-spec.md)
as long as there are no conflicting ComponentJS best practices.
The known resolved conflicts were:
- **Component Reusability and CSS Usage**:
TodoMVC `todo-common` provides a `base.css` which was not
directly used within this ComponentJS TodoMVC Example. There
are two reasons for this:
- **Single vs. Multiple Files**:
The `base.css` provides all styles of the TodoMVC application
in one single file, while in ComponentJS-based applications
the styles are local to the components which create the
corresponding DOM elements. In ComponentJS TodoMVC Example
we have three such components (`root`, `main` and `todo`)
and hence the `base.css` was split into three parts accordingly, too.
- **Unique Ids vs. Class Selectors**:
The styling in `base.css` is mainly based on unique identifiers (`#foo`)
instead of classes (`.foo`). This is a big "no-go" for UI
approaches like ComponentJS where UI widgets (here the `todo`
UI component) are fully reusable and are potentially rendered
multiple times into the same DOM tree. For the particular
TodoMVC use case this does not happen, but the ComponentJS
TodoMVC Example should have been strictly the way things
are done in ComponentJS applications. As a result, all CSS
selectors of `base.css` were converted from unique identifiers to
[BEM](http://bem.info/method/definitions/)-like classes.
- **Source File Grouping**:
TodoMVC recommends to group all sources files according to
technical classifications. ComponentJS-based applications usually
use a domain-specific classification to group files, i.e., the UI is
split into domain-specific components and each component is fully
self-contained. This means that each component consists of its own
JavaScript code, its own style, its own mask, etc. As the TodoMVC use
case is a trivial one, in the ComponentJS TodoMVC example you see this
through the common filename prefixes only. In a real-world ComponentJS
application one would see this also through the directory tree.
- **URL Routing**:
The TodoMVC application speciification just requires that an URL
based routing exists. In order to avoid extra code, one could have
implemented this by using FlatIron Director directly within the `todo`
component and especially just use direct hyperlinks in the view mask.
While sufficient and perhaps acceptable for a trivial use case like
the TodoMVC, it is not for a larger application. There the URL routing
should be done only by a component which has the whole UI as the scope
(the `root` and `main` components but not the `todo` widget) and there
should be no direct hyperlinks within a single component (as it is not
allowd to control the URL of the whole apllication). We use Flatiron
Director in the `main` component and perform a two-way binding into
the `todo` component.
- **LocalStorage**:
The TodoMVC application speciification just requires that the
todo list is persistend in the the HTML5 `localStorage`. In
order to avoid extra code, one could have implemented this by
allowing ComponentJS to implicitly persist the Todo list items into
`localStorage` from within the `todo` widget with the help of the
`component.plugin.localstorage.js` plugin. While sufficient and
perhaps acceptable for a trivial use case like the TodoMVC, it is not
for a larger application. There the Todo items would come from an
underlying service tier and its UI-independent Business Model (while
the UI widget uses a so-called Presentation Model). We decided to
already use this strict separation between Presentation and Business
model for the trivial TodoMVC use case, even if it increases the total
amount of required code, of course.
## Learning ComponentJS
The [ComponentJS website](http://componentjs.com) is a great resource for getting started.
Here are some links you may find helpful:
* [Features](http://componentjs.com/features.html)
* [Demo](http://componentjs.com/demo.html)
* [API Reference](http://componentjs.com/api/api.screen.html)
* [Tutorial](http://componentjs.com/tutorial.html)
For more details about the TodoMVC initiative and the idea behind the TodoMVC applications see:
* [TodoMVC Initiative](https://todomvc.com/)
* [App Specification](https://github.com/tastejs/todomvc/blob/master/app-spec.md)
......@@ -266,9 +266,6 @@
<li>
<a href="examples/ractive/" data-source="http://ractivejs.org" data-content="Ractive.js is a next-generation DOM manipulation library, optimised for developer sanity.">Ractive.js</a>
</li>
<li>
<a href="examples/componentjs/" data-source="http://componentjs.com" data-content="ComponentJS is a stand-alone MPL-licensed Open Source library for JavaScript, providing a powerful run-time Component System for hierarchically structuring the User-Interface (UI) dialogs of complex HTML5-based Rich Clients (aka Single-Page-Apps) — under maximum applied Separation of Concerns (SoC) architecture principle, through optional Model, View and Controller component roles, with sophisticated hierarchical Event, Service, Hook, Model, Socket and Property mechanisms, and fully independent and agnostic of the particular UI widget toolkit.">ComponentJS</a>
</li>
<li class="routing">
<a href="examples/react-alt/" data-source="http://alt.js.org" data-content="This React example integrates Alt for an example of an application following the Flux architecture.">React + Alt</a>
</li>
......
......@@ -504,40 +504,6 @@
}]
}]
},
"componentjs": {
"name": "ComponentJS",
"description": "Provides a powerful run-time Component System for hierarchically structuring the UI dialogs of complex HTML5-based Clients. Fully isolates each UI segment, with sophisticated hierarchical Event, Service, Hook, Model, Socket and Property mechanisms.",
"homepage": "componentjs.com/",
"source_path": [{
"name": "Example",
"url": "examples/componentjs"
}],
"link_groups": [{
"heading": "Official Resources",
"links": [{
"name": "Overview Video",
"url": "http://www.youtube.com/watch?v=gtz7PCMxzVA"
}, {
"name": "Demo",
"url": "http://componentjs.com/demo.html"
}, {
"name": "Tutorial",
"url": "http://componentjs.com/tutorial.html"
}, {
"name": "API Reference",
"url": "http://componentjs.com/api.html"
}]
}, {
"heading": "Community",
"links": [{
"name": "ComponentJS on GitHub",
"url": "https://github.com/rse/componentjs"
}, {
"name": "ComponentJS on Twitter",
"url": "http://twitter.com/componentjs"
}]
}]
},
"cujo": {
"name": "cujoJS",
"description": "cujo is an architectural toolkit for next generation JavaScript applications. It encourages highly modular development, declarative application assembly, and embraces the asynchronous nature of JavaScript and its fusion of object-oriented and functional programming styles.",
......
......@@ -62,7 +62,6 @@ module.exports = [
// https://github.com/tastejs/todomvc/issues/856
'TodoMVC - knockoutjs, Routing, should respect the back button',
'TodoMVC - spine, Routing, should respect the back button',
'TodoMVC - componentjs, Routing, should respect the back button',
'TodoMVC - serenadejs, Routing, should respect the back button',
'TodoMVC - flight, Routing, should respect the back button',
'TodoMVC - lavaca_require, Routing, should respect the back button',
......
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