From 0400b32fe0f53490ba1105ca0f974ecf55c4d05b Mon Sep 17 00:00:00 2001 From: Pascal Hartig Date: Mon, 3 Oct 2016 10:28:50 +0100 Subject: [PATCH 01/26] Remove broken VueJS link from README Fix #1699 --- examples/vue/readme.md | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/vue/readme.md b/examples/vue/readme.md index 5dd8f85d63..4c3f5d7238 100644 --- a/examples/vue/readme.md +++ b/examples/vue/readme.md @@ -14,7 +14,6 @@ Here are some links you may find helpful: * [Official Guide](http://vuejs.org/guide/) * [API Reference](http://vuejs.org/api/) * [Examples](http://vuejs.org/examples/) -* [Building Larger Apps with Vue.js](http://vuejs.org/guide/application.html) Get help from other Vue.js users: From 1b76bc9a14dc1278bfb7e8492a3551774c3be8f1 Mon Sep 17 00:00:00 2001 From: alanbares Date: Mon, 3 Oct 2016 02:21:11 -0700 Subject: [PATCH 02/26] Fix broken link in vue readme --- examples/vue/readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/vue/readme.md b/examples/vue/readme.md index 4c3f5d7238..de50031938 100644 --- a/examples/vue/readme.md +++ b/examples/vue/readme.md @@ -1,6 +1,6 @@ # Vue.js TodoMVC Example -> Vue.js is a library for building interactive web interfaces. +> Vue.js is a library for building interactive web interfaces. It provides data-driven, nestable view components with a simple and flexible API. > _[Vue.js - vuejs.org](http://vuejs.org)_ @@ -14,6 +14,7 @@ Here are some links you may find helpful: * [Official Guide](http://vuejs.org/guide/) * [API Reference](http://vuejs.org/api/) * [Examples](http://vuejs.org/examples/) +* [Building Larger Apps with Vue.js](http://v1.vuejs.org/guide/application.html) Get help from other Vue.js users: From 450a463b17d1d888876f8685094f93bd0e0d3b5f Mon Sep 17 00:00:00 2001 From: Evan Czaplicki Date: Thu, 27 Oct 2016 08:08:13 -0700 Subject: [PATCH 03/26] Update Elm links to point to latest URLs (#1710) The old links are currently redirected to the new links. I figured I'd skip the redirect while I was looking at the file! It may also be worth linking to [The Official Guide](http://guide.elm-lang.org/) in addition to these links. Not sure! --- learn.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/learn.json b/learn.json index 1920981326..072fa5b283 100644 --- a/learn.json +++ b/learn.json @@ -341,13 +341,13 @@ "url": "http://elm-lang.org/" }, { "name": "Guides", - "url": "http://elm-lang.org/Learn.elm" + "url": "http://elm-lang.org/docs" }, { "name": "Syntax Reference", - "url": "http://elm-lang.org/learn/Syntax.elm" + "url": "http://elm-lang.org/docs/syntax" }, { "name": "Libraries", - "url": "http://elm-lang.org/Libraries.elm" + "url": "http://package.elm-lang.org/" }, { "name": "#elm IRC Channel on Freenode", "url": "http://webchat.freenode.net/?channels=elm" From 9f2214d25b4089ef50889626a17656276fa70d3e Mon Sep 17 00:00:00 2001 From: Pascal Hartig Date: Thu, 27 Oct 2016 16:10:16 +0100 Subject: [PATCH 04/26] Link to official Elm guide Suggested by @evancz. --- learn.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/learn.json b/learn.json index 072fa5b283..5edc1a9353 100644 --- a/learn.json +++ b/learn.json @@ -340,8 +340,11 @@ "name": "Project Site", "url": "http://elm-lang.org/" }, { - "name": "Guides", + "name": "Documentation", "url": "http://elm-lang.org/docs" + }, { + "name": "An Introduction to Elm", + "url": "https://guide.elm-lang.org/" }, { "name": "Syntax Reference", "url": "http://elm-lang.org/docs/syntax" From e01b44a481a82d50ebaaf12b4a89293d3276f978 Mon Sep 17 00:00:00 2001 From: Arthur Verschaeve Date: Sun, 6 Nov 2016 16:50:39 +0100 Subject: [PATCH 05/26] Remove `batman` from `knownIssues.js` The batman app was deleted a while ago in 052a840e80c00a04fb2684ecec085c90dc072451 ref #1579 --- tests/knownIssues.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/knownIssues.js b/tests/knownIssues.js index 178b663f5e..5887a0a04c 100644 --- a/tests/knownIssues.js +++ b/tests/knownIssues.js @@ -28,7 +28,6 @@ module.exports = [ // https://github.com/tastejs/todomvc/issues/828 // routing should default to all - 'TodoMVC - batman, Routing, should highlight the currently applied filter', 'TodoMVC - sammyjs, Routing, should highlight the currently applied filter', // https://github.com/tastejs/todomvc/issues/824 @@ -68,7 +67,6 @@ module.exports = [ 'TodoMVC - flight, Editing, should cancel edits on escape', 'TodoMVC - thorax_lumbar, Editing, should cancel edits on escape', 'TodoMVC - backbone_require, Editing, should cancel edits on escape', - 'TodoMVC - batman, Editing, should cancel edits on escape', 'TodoMVC - dijon, Editing, should cancel edits on escape', 'TodoMVC - knockoutjs_require, Editing, should cancel edits on escape', From 689921b2c7ead545aacbe6db34492c2216cb1d83 Mon Sep 17 00:00:00 2001 From: Arthur Verschaeve Date: Sun, 6 Nov 2016 17:14:34 +0100 Subject: [PATCH 06/26] Remove known issues regarding `chaplin-brunch` These should be fixed since #832. Ref #1579 --- tests/knownIssues.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/tests/knownIssues.js b/tests/knownIssues.js index 5887a0a04c..a5926dc28f 100644 --- a/tests/knownIssues.js +++ b/tests/knownIssues.js @@ -1,17 +1,5 @@ module.exports = [ - // see: https://github.com/tastejs/todomvc/issues/832 - // chapling brunch does not mark items as completed via the completed CSS class - 'TodoMVC - chaplin-brunch, No Todos, should hide #main and #footer', - 'TodoMVC - chaplin-brunch, Mark all as completed, should allow me to mark all items as completed', - // jscs:disable - 'TodoMVC - chaplin-brunch, Mark all as completed, complete all checkbox should update state when items are completed / cleared', - // jscs:enable - 'TodoMVC - chaplin-brunch, Item, should allow me to mark items as complete', - 'TodoMVC - chaplin-brunch, Item, should allow me to un-mark items as complete', - 'TodoMVC - chaplin-brunch, Item, should allow me to edit an item', - - // durandal routing is very very slow. // see: https://github.com/tastejs/todomvc/issues/831 'TodoMVC - durandal, Routing, should allow me to display active items', From 701a66000404cc9ab5cbcb59e03a1239fecab41f Mon Sep 17 00:00:00 2001 From: Arthur Verschaeve Date: Sun, 6 Nov 2016 17:23:55 +0100 Subject: [PATCH 07/26] Drop the `componentjs` app The project was deprecated a while ago Ref https://github.com/componentjs/component/commit/a580e2e0dca99c93be638bfe55a3718489d625fa --- examples/componentjs/app/app-dm.js | 32 - examples/componentjs/app/app-sv.js | 29 - .../app/app-ui-composite-main-mask.html | 12 - .../app/app-ui-composite-main-style.css | 27 - .../componentjs/app/app-ui-composite-main.js | 93 - .../app/app-ui-composite-root-style.css | 6 - .../componentjs/app/app-ui-composite-root.js | 41 - examples/componentjs/app/app-ui-constants.js | 11 - .../app/app-ui-widget-todo-mask.html | 40 - .../app/app-ui-widget-todo-model.js | 80 - .../app/app-ui-widget-todo-style.css | 378 - .../app/app-ui-widget-todo-view.js | 188 - examples/componentjs/app/app.js | 21 - examples/componentjs/bower.json | 13 - .../bower_components/componentjs/component.js | 4396 -------- .../componentjs/component.plugin.debugger.js | 1234 --- .../componentjs/component.plugin.extjs.js | 60 - .../componentjs/component.plugin.jquery.js | 66 - .../director/build/director.js | 712 -- .../jquery-markup/jquery.markup.js | 435 - .../bower_components/jquery/jquery.js | 8829 ----------------- .../bower_components/lodash/dist/lodash.js | 6591 ------------ .../nunjucks/browser/nunjucks.js | 4959 --------- .../bower_components/todomvc-common/base.css | 554 -- .../bower_components/todomvc-common/base.js | 217 - .../bower_components/todomvc-common/bg.png | Bin 2126 -> 0 bytes .../bower_components/uuid-js/lib/uuid.js | 228 - examples/componentjs/index.html | 50 - examples/componentjs/readme.md | 124 - index.html | 3 - learn.json | 34 - tests/knownIssues.js | 1 - 32 files changed, 29464 deletions(-) delete mode 100644 examples/componentjs/app/app-dm.js delete mode 100644 examples/componentjs/app/app-sv.js delete mode 100644 examples/componentjs/app/app-ui-composite-main-mask.html delete mode 100644 examples/componentjs/app/app-ui-composite-main-style.css delete mode 100644 examples/componentjs/app/app-ui-composite-main.js delete mode 100644 examples/componentjs/app/app-ui-composite-root-style.css delete mode 100644 examples/componentjs/app/app-ui-composite-root.js delete mode 100644 examples/componentjs/app/app-ui-constants.js delete mode 100644 examples/componentjs/app/app-ui-widget-todo-mask.html delete mode 100644 examples/componentjs/app/app-ui-widget-todo-model.js delete mode 100644 examples/componentjs/app/app-ui-widget-todo-style.css delete mode 100644 examples/componentjs/app/app-ui-widget-todo-view.js delete mode 100644 examples/componentjs/app/app.js delete mode 100644 examples/componentjs/bower.json delete mode 100644 examples/componentjs/bower_components/componentjs/component.js delete mode 100644 examples/componentjs/bower_components/componentjs/component.plugin.debugger.js delete mode 100644 examples/componentjs/bower_components/componentjs/component.plugin.extjs.js delete mode 100644 examples/componentjs/bower_components/componentjs/component.plugin.jquery.js delete mode 100644 examples/componentjs/bower_components/director/build/director.js delete mode 100644 examples/componentjs/bower_components/jquery-markup/jquery.markup.js delete mode 100644 examples/componentjs/bower_components/jquery/jquery.js delete mode 100644 examples/componentjs/bower_components/lodash/dist/lodash.js delete mode 100644 examples/componentjs/bower_components/nunjucks/browser/nunjucks.js delete mode 100644 examples/componentjs/bower_components/todomvc-common/base.css delete mode 100644 examples/componentjs/bower_components/todomvc-common/base.js delete mode 100644 examples/componentjs/bower_components/todomvc-common/bg.png delete mode 100644 examples/componentjs/bower_components/uuid-js/lib/uuid.js delete mode 100644 examples/componentjs/index.html delete mode 100644 examples/componentjs/readme.md diff --git a/examples/componentjs/app/app-dm.js b/examples/componentjs/app/app-dm.js deleted file mode 100644 index 3f15613dd9..0000000000 --- a/examples/componentjs/app/app-dm.js +++ /dev/null @@ -1,32 +0,0 @@ -/* 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)); - } - }); -}()); diff --git a/examples/componentjs/app/app-sv.js b/examples/componentjs/app/app-sv.js deleted file mode 100644 index 5efa774a60..0000000000 --- a/examples/componentjs/app/app-sv.js +++ /dev/null @@ -1,29 +0,0 @@ -/* 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); - } - } - })(); -}()); diff --git a/examples/componentjs/app/app-ui-composite-main-mask.html b/examples/componentjs/app/app-ui-composite-main-mask.html deleted file mode 100644 index 32c498d1b8..0000000000 --- a/examples/componentjs/app/app-ui-composite-main-mask.html +++ /dev/null @@ -1,12 +0,0 @@ - - -
-
-
- -
-
diff --git a/examples/componentjs/app/app-ui-composite-main-style.css b/examples/componentjs/app/app-ui-composite-main-style.css deleted file mode 100644 index 891d769a82..0000000000 --- a/examples/componentjs/app/app-ui-composite-main-style.css +++ /dev/null @@ -1,27 +0,0 @@ -.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; -} diff --git a/examples/componentjs/app/app-ui-composite-main.js b/examples/componentjs/app/app-ui-composite-main.js deleted file mode 100644 index 07e9561a6c..0000000000 --- a/examples/componentjs/app/app-ui-composite-main.js +++ /dev/null @@ -1,93 +0,0 @@ -/* 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' }); - } - } - }); -}()); diff --git a/examples/componentjs/app/app-ui-composite-root-style.css b/examples/componentjs/app/app-ui-composite-root-style.css deleted file mode 100644 index bd8ffc8dcf..0000000000 --- a/examples/componentjs/app/app-ui-composite-root-style.css +++ /dev/null @@ -1,6 +0,0 @@ -html, -body { - margin: 0; - padding: 0; - background: #eaeaea url(../bower_components/todomvc-common/bg.png); -} diff --git a/examples/componentjs/app/app-ui-composite-root.js b/examples/componentjs/app/app-ui-composite-root.js deleted file mode 100644 index 7ec6255619..0000000000 --- a/examples/componentjs/app/app-ui-composite-root.js +++ /dev/null @@ -1,41 +0,0 @@ -/* 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(); - } - } - }); -}()); diff --git a/examples/componentjs/app/app-ui-constants.js b/examples/componentjs/app/app-ui-constants.js deleted file mode 100644 index 0faa48a348..0000000000 --- a/examples/componentjs/app/app-ui-constants.js +++ /dev/null @@ -1,11 +0,0 @@ -/* global cs */ -(function () { - 'use strict'; - - // some UI constants - cs.ns('app.ui').constants = { - // DOM event key code - KEY_ENTER: 13, - KEY_ESCAPE: 27 - }; -}()); diff --git a/examples/componentjs/app/app-ui-widget-todo-mask.html b/examples/componentjs/app/app-ui-widget-todo-mask.html deleted file mode 100644 index 3002da7fc7..0000000000 --- a/examples/componentjs/app/app-ui-widget-todo-mask.html +++ /dev/null @@ -1,40 +0,0 @@ - - -
-
-

todos

- -
-
- - -
    - -
  • -
    - - -
    - -
  • -
    -
-
- -
-
diff --git a/examples/componentjs/app/app-ui-widget-todo-model.js b/examples/componentjs/app/app-ui-widget-todo-model.js deleted file mode 100644 index 5cebb525c2..0000000000 --- a/examples/componentjs/app/app-ui-widget-todo-model.js +++ /dev/null @@ -1,80 +0,0 @@ -/* 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); - } - } - }); - } - } - }); -}()); diff --git a/examples/componentjs/app/app-ui-widget-todo-style.css b/examples/componentjs/app/app-ui-widget-todo-style.css deleted file mode 100644 index 2b3f29ee62..0000000000 --- a/examples/componentjs/app/app-ui-widget-todo-style.css +++ /dev/null @@ -1,378 +0,0 @@ -.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; - } -} diff --git a/examples/componentjs/app/app-ui-widget-todo-view.js b/examples/componentjs/app/app-ui-widget-todo-view.js deleted file mode 100644 index e51e24e68c..0000000000 --- a/examples/componentjs/app/app-ui-widget-todo-view.js +++ /dev/null @@ -1,188 +0,0 @@ -/* 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); - }); - } - } - }); -}()); diff --git a/examples/componentjs/app/app.js b/examples/componentjs/app/app.js deleted file mode 100644 index 7dddf28d2a..0000000000 --- a/examples/componentjs/app/app.js +++ /dev/null @@ -1,21 +0,0 @@ -/* 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'); - } - } - }); -}()); diff --git a/examples/componentjs/bower.json b/examples/componentjs/bower.json deleted file mode 100644 index 79db360319..0000000000 --- a/examples/componentjs/bower.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "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" - } -} diff --git a/examples/componentjs/bower_components/componentjs/component.js b/examples/componentjs/bower_components/componentjs/component.js deleted file mode 100644 index 06398446bf..0000000000 --- a/examples/componentjs/bower_components/componentjs/component.js +++ /dev/null @@ -1,4396 +0,0 @@ -/* -** ComponentJS -- Component System for JavaScript -** Copyright (c) 2009-2013 Ralf S. Engelschall -** -** 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/. -*/ - -(function (GLOBAL, EXPORTS, DEFINE) { - - /* - ** GLOBAL LIBRARY NAMESPACING - */ - - /* internal API */ - var _cs = function () {}; - - /* external API */ - var $cs = function () { - /* under run-time just pass through to lookup functionality */ - return _cs.hook("ComponentJS:lookup", "pass", _cs.lookup.apply(GLOBAL, arguments)); - }; - - /* pattern sub-namespace */ - $cs.pattern = {}; - - /* top-level API method: change symbol of external API */ - $cs.symbol = (function () { - /* internal state */ - var value_original; value_original = undefined; - var symbol_current = null; - - /* top-level API method */ - return function (symbol) { - /* release old occupation */ - if (symbol_current !== null) - GLOBAL[symbol_current] = value_original; - - /* perform new occupation */ - if (typeof symbol === "undefined" || symbol === "") - /* occupy no global slot at all */ - symbol_current = null; - else { - /* occupy new global slot */ - symbol_current = symbol; - value_original = GLOBAL[symbol_current]; - GLOBAL[symbol_current] = $cs; - } - - /* return the global API */ - return $cs; - }; - })(); - - /* top-level API method: create a global namespace - and optionally assign a value to the leaf object */ - $cs.ns = function (name, value) { - /* sanity check name argument */ - if (typeof name !== "string" || name === "") - throw "invalid namespace path"; - - /* determine path */ - var path = name.split("."); - var len = path.length; - if (typeof value !== "undefined") - len--; - - /* iterate over the path and create missing objects */ - var i = 0; - var ctx = GLOBAL; - while (i < len) { - if (typeof ctx[path[i]] === "undefined") - ctx[path[i]] = {}; - ctx = ctx[path[i++]]; - } - - /* optionally assign a value to the leaf object */ - if (typeof value !== "undefined") { - ctx[path[i]] = value; - ctx = value; - } - - /* return the leaf object */ - return ctx; - }; - - /* API version */ - $cs.version = { - major: 1, - minor: 0, - micro: 1, - date: 20131009 - }; - - - /* - ** COMMON UTILITY FUNCTIONALITIES - */ - - /* utility function: create an exception string for throwing */ - _cs.exception = function (method, error) { - var trace; - - /* optionally log stack trace to console */ - if ($cs.debug() > 0) { - if (typeof GLOBAL.console === "object") { - if (typeof GLOBAL.console.trace === "function") - GLOBAL.console.trace(); - else if ( typeof GLOBAL.printStackTrace !== "undefined" && - typeof GLOBAL.console.log === "function") { - trace = GLOBAL.printStackTrace(); - GLOBAL.console.log(trace.join("\n")); - } - } - } - - /* return Error exception object */ - return new Error("[ComponentJS]: ERROR: " + method + ": " + error); - }; - - /* utility function: logging via environment console */ - _cs.log = function (msg) { - /* try ComponentJS debugger */ - if (_cs.hook("ComponentJS:log", "or", msg)) - {} /* do nothing, as plugins have already logged the message */ - - /* try Firebug-style console (in regular browser or Node) */ - else if ( typeof GLOBAL.console !== "undefined" && - typeof GLOBAL.console.log !== "undefined") - GLOBAL.console.log("[ComponentJS]: " + msg); - - /* try API of Appcelerator Titanium */ - else if ( typeof GLOBAL.Titanium !== "undefined" && - typeof GLOBAL.Titanium.API !== "undefined" && - typeof GLOBAL.Titanium.API.log === "function") - GLOBAL.Titanium.API.log("[ComponentJS]: " + msg); - }; - - /* utility function: debugging */ - $cs.debug = (function () { - var debug_level = 9; - return function (level, msg) { - if (arguments.length === 0) - /* return old debug level */ - return debug_level; - else if (arguments.length === 1) - /* configure new debug level */ - debug_level = level; - else { - /* perform runtime logging */ - if (level <= debug_level) { - /* determine indentation based on debug level */ - var indent = ""; - for (var i = 1; i < level; i++) - indent += " "; - - /* display debug message */ - _cs.log("DEBUG[" + level + "]: " + indent + msg); - } - } - }; - })(); - - /* utility function: no operation (for passing as dummy callback) */ - $cs.nop = function () {}; - - /* utility function: annotate an object */ - _cs.annotation = function (obj, name, value) { - var result = null; - var __name__ = "__ComponentJS_" + name + "__"; - if (typeof obj !== "undefined" && obj !== null) { - /* get annotation value */ - if (typeof obj[__name__] !== "undefined") - result = obj[__name__]; - if (typeof value !== "undefined") { - /* set annotation value */ - if (value !== null) - obj[__name__] = value; - else - delete obj[__name__]; - } - } - return result; - }; - - /* utility function: conveniently check for defined variable */ - _cs.isdefined = function (obj) { - return (typeof obj !== "undefined"); - }; - - /* utility function: check whether a field is directly owned by object - (instead of implicitly resolved through the constructor's prototype object) */ - _cs.isown = function (obj, field) { - var isown = Object.hasOwnProperty.call(obj, field); - if (field === "constructor" || field === "prototype") { - isown = isown && Object.propertyIsEnumerable.call(obj, field); - if (obj[field].toString().indexOf("[native code]") !== -1) - isown = false; - } - return isown; - }; - - /* utility function: determine type of anything, - an improved version of the built-in "typeof" operator */ - _cs.istypeof = function (obj) { - var type = typeof obj; - if (type === "object") { - if (obj === null) - /* JavaScript nasty special case: null object */ - type = "null"; - else if (Object.prototype.toString.call(obj) === "[object String]") - /* JavaScript nasty special case: String object */ - type = "string"; - else if (Object.prototype.toString.call(obj) === "[object Number]") - /* JavaScript nasty special case: Number object */ - type = "number"; - else if (Object.prototype.toString.call(obj) === "[object Boolean]") - /* JavaScript nasty special case: Boolean object */ - type = "boolean"; - else if (Object.prototype.toString.call(obj) === "[object Function]") - /* JavaScript nasty special case: Function object */ - type = "function"; - else if (Object.prototype.toString.call(obj) === "[object Array]") - /* JavaScript nasty special case: Array object */ - type = "array"; - else if (_cs.annotation(obj, "type") !== null) - /* ComponentJS special case: "component" */ - type = _cs.annotation(obj, "type"); - } - else if (type === "function") { - /* ComponentJS special case: "{clazz,trait}" */ - if (_cs.annotation(obj, "type") !== null) - type = _cs.annotation(obj, "type"); - } - return type; - }; - - /* utility function: retrieve keys of object */ - _cs.keysof = function (obj) { - var keys = []; - for (var key in obj) { - if (obj.hasOwnProperty(key)) - keys.push(key); - } - return keys; - }; - - /* utility function: JSON encoding of object */ - _cs.json = (function () { - var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; - var meta = { "\b": "\\b", "\t": "\\t", "\n": "\\n", "\f": "\\f", "\r": "\\r", "\"": "\\\"", "\\": "\\\\" }; - var quote = function (string) { - escapable.lastIndex = 0; - return ( - escapable.test(string) ? - "\"" + string.replace(escapable, function (a) { - var c = meta[a]; - return typeof c === "string" ? - c : - "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); - }) + "\"" : - "\"" + string + "\"" - ); - }; - var encode = function (value, seen) { - if (typeof value !== "boolean" && typeof value !== "number" && typeof value !== "string") { - if (typeof seen[value] !== "undefined") - return "null /* CYCLE! */"; - else - seen[value] = true; - } - switch (typeof value) { - case "boolean": value = String(value); break; - case "number": value = (isFinite(value) ? String(value) : "NaN"); break; - case "string": value = quote(value); break; - case "function": - if (_cs.annotation(value, "type") !== null) - value = "<" + _cs.annotation(value, "type") + ">"; - else - value = ""; - break; - case "object": - var a = []; - if (value === null) - value = "null"; - else if (_cs.annotation(value, "type") !== null) - value = "<" + _cs.annotation(value, "type") + ">"; - else if (Object.prototype.toString.call(value) === "[object Function]") - value = ""; - else if ( Object.prototype.toString.call(value) === "[object Array]" || - value instanceof Array) { - for (var i = 0; i < value.length; i++) - a[i] = arguments.callee(value[i], seen); /* RECURSION */ - value = (a.length === 0 ? "[]" : "[" + a.join(",") + "]"); - } - else { - for (var k in value) { - if (Object.hasOwnProperty.call(value, k)) { - var v = arguments.callee(value[k], seen); /* RECURSION */ - a.push(quote(k) + ":" + v); - } - } - value = (a.length === 0 ? "{}" : "{" + a.join(",") + "}"); - } - break; - default: - value = ""; - } - return value; - }; - return function (value) { - return encode(value, {}); - }; - })(); - - /* utility function: deep cloning of arbitrary data-structure */ - _cs.clone = function (source, continue_recursion) { - /* allow recursive cloning to be controlled */ - if (typeof continue_recursion === "undefined") - continue_recursion = function (/* name, value */) { return true; }; - else if (typeof continue_recursion === "string") { - var pattern = continue_recursion; - continue_recursion = function (name /*, value */) { return name.match(pattern); }; - } - - /* helper functions */ - var myself = arguments.callee; - var clone_func = function (f, continue_recursion) { - var g = function ComponentJS_function_clone () { - return f.apply(this, arguments); - }; - g.prototype = f.prototype; - for (var prop in f) { - if (_cs.isown(f, prop)) { - if (continue_recursion(prop, f)) - g[prop] = myself(f[prop], continue_recursion); /* RECURSION */ - else - g[prop] = f[prop]; - } - } - _cs.annotation(g, "clone", true); - return g; - }; - - var target; target = undefined; - if (typeof source === "function") - /* special case: primitive function */ - target = clone_func(source, continue_recursion); - else if (typeof source === "object") { - if (source === null) - /* special case: null object */ - target = null; - else if (Object.prototype.toString.call(source) === "[object String]") - /* special case: String object */ - target = "" + source.valueOf(); - else if (Object.prototype.toString.call(source) === "[object Number]") - /* special case: Number object */ - target = 0 + source.valueOf(); - else if (Object.prototype.toString.call(source) === "[object Boolean]") - /* special case: Boolean object */ - target = !!source.valueOf(); - else if (Object.prototype.toString.call(source) === "[object Function]") - /* special case: Function object */ - target = clone_func(source, continue_recursion); - else if (Object.prototype.toString.call(source) === "[object Date]") - /* special case: Date object */ - target = new Date(source.getTime()); - else if (Object.prototype.toString.call(source) === "[object RegExp]") - /* special case: RegExp object */ - target = new RegExp(source.source); - else if (Object.prototype.toString.call(source) === "[object Array]") { - /* special case: array object */ - var len = source.length; - target = []; - for (var i = 0; i < len; i++) - target.push(myself(source[i], continue_recursion)); /* RECURSION */ - } - else { - /* special case: hash object */ - target = {}; - for (var key in source) { - if (key !== "constructor" && _cs.isown(source, key)) { - if (continue_recursion(key, source)) - target[key] = myself(source[key], continue_recursion); /* RECURSION */ - else - target[key] = source[key]; - } - } - if (typeof source.constructor === "function") - target.constructor = source.constructor; - if (typeof source.prototype === "object") - target.prototype = source.prototype; - } - } - else - /* regular case: anything else - (just primitive data types and undefined value) */ - target = source; - return target; - }; - - /* utility function: extend an object with other object(s) */ - _cs.extend = function (target, source, filter) { - if (typeof filter === "undefined") - filter = function (/* name, value */) { return true; }; - else if (typeof filter === "string") { - var pattern = filter; - filter = function (name /*, value */) { return name.match(pattern); }; - } - for (var key in source) - if (_cs.isown(source, key)) - if (filter(key, source[key])) - target[key] = source[key]; - return target; - }; - - /* utility function: mixin objects into another object by chaining methods */ - _cs.mixin = function (target, source, filter) { - if (typeof filter === "undefined") - filter = function (/* name, value */) { return true; }; - else if (typeof filter === "string") { - var pattern = filter; - filter = function (name /*, value */) { return name.match(pattern); }; - } - for (var key in source) { - if (_cs.isown(source, key)) { - if (filter(key, source[key])) { - if (_cs.istypeof(source[key]) === "function") { - /* method/function */ - var src = _cs.clone(source[key], filter); - _cs.annotation(src, "name", key); - if ( _cs.istypeof(target[key]) === "function" && - _cs.isown(target, key) ) - _cs.annotation(src, "base", target[key]); - target[key] = src; - } - else { - /* property/field */ - target[key] = source[key]; - } - } - } - } - return target; - }; - - /* utility function: concatenate array values */ - _cs.concat = function () { - var target = []; - for (var i = 0; i < arguments.length; i++) { - var source = arguments[i]; - for (var j = 0; j < source.length; j++) - target.push(source[j]); - } - return target; - }; - - /* utility function: slice array values */ - _cs.slice = function (source, start, len) { - var target = []; - if (typeof len === "undefined") - len = source.length; - for (var i = start; i < len; i++) - target.push(source[i]); - return target; - }; - - /* utility function: map array values */ - _cs.map = function (source, mapper) { - var target = []; - for (var i = 0; i < source.length; i++) - target.push(mapper(source[i], i)); - return target; - }; - - /* utility function: filter array values */ - _cs.filter = function (source, filter) { - var target = []; - for (var i = 0; i < source.length; i++) - if (filter(source[i], i)) - target.push(source[i]); - return target; - }; - - /* utility function: iterate over values */ - _cs.foreach = function (source, callback) { - for (var i = 0; i < source.length; i++) - callback(source[i], i); - }; - - /* custom Token class */ - _cs.token = function () { - this.text = ""; - this.tokens = []; - this.pos = 0; - this.len = 0; - }; - _cs.token.prototype = { - /* setter for plain-text input */ - setText: function (text) { - this.text = text; - }, - - /* setter for additional token symbols */ - addToken: function (b1, b2, e2, e1, symbol) { - this.tokens.push({ b1: b1, b2: b2, e2: e2, e1: e1, symbol: symbol }); - this.len++; - }, - - /* peek at the next token or token at particular offset */ - peek: function (offset) { - if (typeof offset === "undefined") - offset = 0; - if (offset >= this.len) - throw new Error("parse error: not enough tokens"); - return this.tokens[this.pos + offset].symbol; - }, - - /* skip one or more tokens */ - skip: function (len) { - if (typeof len === "undefined") - len = 1; - if (len > this.len) - throw new Error("parse error: not enough tokens available to skip: " + this.ctx()); - this.pos += len; - this.len -= len; - }, - - /* consume the current token (by expecting it to be a particular symbol) */ - consume: function (symbol) { - if (this.len <= 0) - throw new Error("parse error: no more tokens available to consume: " + this.ctx()); - if (this.tokens[this.pos].symbol !== symbol) - throw new Error("parse error: expected token symbol \"" + symbol + "\": " + this.ctx()); - this.pos++; - this.len--; - }, - - /* return a textual description of the token parsing context */ - ctx: function (width) { - if (typeof width === "undefined") - width = 78; - var tok = this.tokens[this.pos]; - - /* the current token itself */ - var ctx = "<" + this.text.substr(tok.b2, tok.e2 - tok.b2 + 1) + ">"; - ctx = this.text.substr(tok.b1, tok.b2 - tok.b1) + ctx; - ctx = ctx + this.text.substr(tok.e2, tok.e1 - tok.e2); - - /* the previous and following token(s) */ - var k = (width - ctx.length); - if (k > 0) { - k = Math.floor(k / 2); - var i, str; - if (this.pos > 0) { - /* previous token(s) */ - var k1 = 0; - for (i = this.pos - 1; i >= 0; i--) { - tok = this.tokens[i]; - str = this.text.substr(tok.b1, tok.e1 - tok.b1 + 1); - k1 += str.length; - if (k1 > k) - break; - ctx = str + ctx; - } - if (i > 0) - ctx = "[...]" + ctx; - } - if (this.len > 1) { - /* following token(s) */ - var k2 = 0; - for (i = this.pos + 1; i < this.pos + this.len; i++) { - tok = this.tokens[i]; - str = this.text.substr(tok.b1, tok.e1 - tok.b1 + 1); - k2 += str.length; - if (k2 > k) - break; - ctx = ctx + str; - } - if (i < this.pos + this.len) - ctx = ctx + "[...]"; - } - } - - /* place everything on a single line through escape sequences */ - ctx = ctx.replace(/\r/, "\\r") - .replace(/\n/, "\\n") - .replace(/\t/, "\\t"); - return ctx; - } - }; - - /* API function: validate an arbitrary value against a type specification */ - $cs.validate = function (value, spec, non_cache) { - /* compile validation AST from specification - or reuse cached pre-compiled validation AST */ - var ast; - if (!non_cache) - ast = _cs.validate_cache[spec]; - if (typeof ast === "undefined") - ast = _cs.validate_compile(spec); - if (!non_cache) - _cs.validate_cache[spec] = ast; - - /* execute validation AST against the value */ - return _cs.validate_executor.exec_spec(value, ast); - }; - - /* the internal compile cache */ - _cs.validate_cache = {}; - - /* - * VALIDATION SPECIFICATION COMPILER - */ - - /* compile validation specification into validation AST */ - _cs.validate_compile = function (spec) { - /* tokenize the specification string into a token stream */ - var token = _cs.validate_tokenize(spec); - - /* parse the token stream into an AST */ - return _cs.validate_parser.parse_spec(token); - }; - - /* tokenize the validation specification */ - _cs.validate_tokenize = function (spec) { - /* create new Token abstraction */ - var token = new _cs.token(); - token.setText(spec); - - /* determine individual token symbols */ - var m; - var b = 0; - while (spec !== "") { - m = spec.match(/^(\s*)([^{}\[\]:,?*+()!|\s]+|[{}\[\]:,?*+()!|])(\s*)/); - if (m === null) - throw new Error("parse error: cannot further canonicalize: \"" + spec + "\""); - token.addToken( - b, - b + m[1].length, - b + m[1].length + m[2].length - 1, - b + m[0].length - 1, - m[2] - ); - spec = spec.substr(m[0].length); - b += m[0].length; - } - return token; - }; - - /* parse specification */ - _cs.validate_parser = { - parse_spec: function (token) { - if (token.len <= 0) - return null; - var ast; - var symbol = token.peek(); - if (symbol === "!") - ast = this.parse_not(token); - else if (symbol === "(") - ast = this.parse_group(token); - else if (symbol === "{") - ast = this.parse_hash(token); - else if (symbol === "[") - ast = this.parse_array(token); - else if (symbol.match(/^(?:null|undefined|boolean|number|string|function|object)$/)) - ast = this.parse_primary(token); - else if (symbol.match(/^(?:clazz|trait|component)$/)) - ast = this.parse_special(token); - else if (symbol === "any") - ast = this.parse_any(token); - else if (symbol.match(/^[A-Z][_a-zA-Z$0-9]*$/)) - ast = this.parse_class(token); - else - throw new Error("parse error: invalid token symbol: \"" + token.ctx() + "\""); - return ast; - }, - - /* parse boolean "not" operation */ - parse_not: function (token) { - token.consume("!"); - var ast = this.parse_spec(token); /* RECURSION */ - ast = { type: "not", op: ast }; - return ast; - }, - - /* parse group (for boolean "or" operation) */ - parse_group: function (token) { - token.consume("("); - var ast = this.parse_spec(token); - while (token.peek() === "|") { - token.consume("|"); - var child = this.parse_spec(token); /* RECURSION */ - ast = { type: "or", op1: ast, op2: child }; - } - token.consume(")"); - return ast; - }, - - /* parse hash type specification */ - parse_hash: function (token) { - token.consume("{"); - var elements = []; - while (token.peek() !== "}") { - var key = this.parse_key(token); - var arity = this.parse_arity(token, "?"); - token.consume(":"); - var spec = this.parse_spec(token); /* RECURSION */ - elements.push({ type: "element", key: key, arity: arity, element: spec }); - if (token.peek() === ",") - token.skip(); - else - break; - } - var ast = { type: "hash", elements: elements }; - token.consume("}"); - return ast; - }, - - /* parse array type specification */ - parse_array: function (token) { - token.consume("["); - var elements = []; - while (token.peek() !== "]") { - var spec = this.parse_spec(token); /* RECURSION */ - var arity = this.parse_arity(token, "?*+"); - elements.push({ type: "element", element: spec, arity: arity }); - if (token.peek() === ",") - token.skip(); - else - break; - } - var ast = { type: "array", elements: elements }; - token.consume("]"); - return ast; - }, - - /* parse primary type specification */ - parse_primary: function (token) { - var primary = token.peek(); - if (!primary.match(/^(?:null|undefined|boolean|number|string|function|object)$/)) - throw new Error("parse error: invalid primary type \"" + primary + "\""); - token.skip(); - return { type: "primary", name: primary }; - }, - - /* parse special ComponentJS type specification */ - parse_special: function (token) { - var special = token.peek(); - if (!special.match(/^(?:clazz|trait|component)$/)) - throw new Error("parse error: invalid special type \"" + special + "\""); - token.skip(); - return { type: "special", name: special }; - }, - - /* parse special "any" type specification */ - parse_any: function (token) { - var any = token.peek(); - if (any !== "any") - throw new Error("parse error: invalid any type \"" + any + "\""); - token.skip(); - return { type: "any" }; - }, - - /* parse JavaScript class specification */ - parse_class: function (token) { - var clazz = token.peek(); - if (!clazz.match(/^[A-Z][_a-zA-Z$0-9]*$/)) - throw new Error("parse error: invalid class type \"" + clazz + "\""); - token.skip(); - return { type: "class", name: clazz }; - }, - - /* parse arity specification */ - parse_arity: function (token, charset) { - var arity = [ 1, 1 ]; - if ( token.len >= 5 && - token.peek(0) === "{" && - token.peek(1).match(/^[0-9]+$/) && - token.peek(2) === "," && - token.peek(3).match(/^(?:[0-9]+|oo)$/) && - token.peek(4) === "}" ) { - arity = [ - parseInt(token.peek(1), 10), - ( token.peek(3) === "oo" ? - Number.MAX_VALUE : - parseInt(token.peek(3), 10)) - ]; - token.skip(5); - } - else if ( - token.len >= 1 && - token.peek().length === 1 && - charset.indexOf(token.peek()) >= 0) { - var c = token.peek(); - switch (c) { - case "?": arity = [ 0, 1 ]; break; - case "*": arity = [ 0, Number.MAX_VALUE ]; break; - case "+": arity = [ 1, Number.MAX_VALUE ]; break; - } - token.skip(); - } - return arity; - }, - - /* parse hash key specification */ - parse_key: function (token) { - var key = token.peek(); - if (!key.match(/^[_a-zA-Z$][_a-zA-Z$0-9]*$/)) - throw new Error("parse error: invalid key \"" + key + "\""); - token.skip(); - return key; - } - }; - - /* - * VALIDATION AST EXECUTOR - */ - - _cs.validate_executor = { - /* validate specification (top-level) */ - exec_spec: function (value, node) { - var valid = false; - if (node !== null) { - switch (node.type) { - case "not": valid = this.exec_not (value, node); break; - case "or": valid = this.exec_or (value, node); break; - case "hash": valid = this.exec_hash (value, node); break; - case "array": valid = this.exec_array (value, node); break; - case "primary": valid = this.exec_primary(value, node); break; - case "special": valid = this.exec_special(value, node); break; - case "class": valid = this.exec_class (value, node); break; - case "any": valid = true; break; - default: - throw new Error("validation error: invalid validation AST: " + - "node has unknown type \"" + node.type + "\""); - } - } - return valid; - }, - - /* validate through boolean "not" operation */ - exec_not: function (value, node) { - return !this.exec_spec(value, node.op); /* RECURSION */ - }, - - /* validate through boolean "or" operation */ - exec_or: function (value, node) { - return ( - this.exec_spec(value, node.op1) /* RECURSION */ || - this.exec_spec(value, node.op2) /* RECURSION */ - ); - }, - - /* validate hash type */ - exec_hash: function (value, node) { - var i, el; - var valid = (typeof value === "object"); - var fields = {}; - if (valid) { - /* pass 1: ensure that all mandatory fields exist - and determine map of valid fields for pass 2 */ - for (i = 0; i < node.elements.length; i++) { - el = node.elements[i]; - fields[el.key] = el.element; - if (el.arity[0] > 0 && typeof value[el.key] === "undefined") { - valid = false; - break; - } - } - } - if (valid) { - /* pass 2: ensure that no unknown fields exist - and that all existing fields are valid */ - for (var field in value) { - if ( !Object.hasOwnProperty.call(value, field) || - !Object.propertyIsEnumerable.call(value, field) || - (field === "constructor" || field === "prototype")) - continue; - if ( typeof fields[field] === "undefined" || - !this.exec_spec(value[field], fields[field])) { /* RECURSION */ - valid = false; - break; - } - } - } - return valid; - }, - - /* validate array type */ - exec_array: function (value, node) { - var i, el; - var valid = (typeof value === "object" && value instanceof Array); - if (valid) { - var pos = 0; - for (i = 0; i < node.elements.length; i++) { - el = node.elements[i]; - var found = 0; - while (found < el.arity[1] && pos < value.length) { - if (!this.exec_spec(value[pos], el.element)) /* RECURSION */ - break; - found++; - pos++; - } - if (found < el.arity[0]) { - valid = false; - break; - } - } - if (pos < value.length) - valid = false; - } - return valid; - }, - - /* validate standard JavaScript type */ - exec_primary: function (value, node) { - return (node.name === "null" && value === null) || (typeof value === node.name); - }, - - /* validate custom JavaScript type */ - exec_class: function (value, node) { - /* jshint evil:true */ - return ( typeof value === "object" && - ( Object.prototype.toString.call(value) === "[object " + node.name + "]") || - eval("value instanceof " + node.name) ); - }, - - /* validate special ComponentJS type */ - exec_special: function (value, node) { - var valid = false; - if (typeof value === (node.name === "component" ? "object" : "function")) - valid = (_cs.annotation(value, "type") === node.name); - return valid; - } - }; - - /* utility function: flexible parameter handling */ - $cs.params = function (func_name, func_args, spec) { - /* provide parameter processing hook */ - _cs.hook("ComponentJS:params:" + func_name + ":enter", "none", { args: func_args, spec: spec }); - - /* start with a fresh parameter object */ - var params = {}; - - /* 1. determine number of total positional parameters, - 2. determine number of required positional parameters, - 3. set default values */ - var positional = 0; - var required = 0; - var pos2name = {}; - var name; - for (name in spec) { - if (_cs.isown(spec, name)) { - /* process parameter position */ - if (typeof spec[name].pos !== "undefined") { - pos2name[spec[name].pos] = name; - if (typeof spec[name].pos === "number") - positional++; - if (typeof spec[name].req !== "undefined" && spec[name].req) - required++; - } - - /* process default value */ - if (typeof spec[name].def !== "undefined") - params[name] = spec[name].def; - } - } - - /* determine or at least guess whether we were called with - positional or name-based parameters */ - var name_based = false; - if ( func_args.length === 1 && - _cs.istypeof(func_args[0]) === "object") { - /* ok, looks like a regular call like - "foo({ foo: ..., bar: ...})" */ - name_based = true; - - /* ...but do not be mislead by a positional use like - "foo(bar)" where "bar" is an arbitrary object! */ - for (name in func_args[0]) { - if (_cs.isown(func_args[0], name)) { - if (typeof spec[name] === "undefined") - name_based = false; - } - } - } - - /* common value validity checking */ - var check_validity = function (func, name, value, valid) { - if (typeof valid === "string") { - if (!$cs.validate(value, valid)) - throw _cs.exception(func, "value of parameter \"" + name + "\" not valid"); - } - else if (typeof valid === "object" && valid instanceof RegExp) { - if (!(typeof value === "string" && value.match(valid))) - throw _cs.exception(func, "value of parameter \"" + name + "\" not valid (regexp)"); - } - else if (typeof valid === "function") { - if (!valid(value)) - throw _cs.exception(func, "value of parameter \"" + name + "\" not valid (callback)"); - } - }; - - /* set actual values */ - var i; - var args; - if (name_based) { - /* case 1: name-based parameter specification */ - args = func_args[0]; - for (name in args) { - if (_cs.isown(args, name)) { - if (typeof spec[name] === "undefined") - throw _cs.exception(func_name, "unknown parameter \"" + name + "\""); - check_validity(func_name, name, args[name], spec[name].valid); - params[name] = args[name]; - } - } - for (name in spec) { - if (_cs.isown(spec, name)) { - if ( typeof spec[name].req !== "undefined" && - spec[name].req && - typeof args[name] === "undefined") - throw _cs.exception(func_name, "required parameter \"" + name + "\" missing"); - } - } - } - else { - /* case 2: positional parameter specification */ - if (func_args.length < required) - throw _cs.exception(func_name, "invalid number of arguments " + - "(at least " + required + " required)"); - for (i = 0; i < positional && i < func_args.length; i++) { - check_validity(func_name, pos2name[i], func_args[i], spec[pos2name[i]].valid); - params[pos2name[i]] = func_args[i]; - } - if (i < func_args.length) { - if (typeof pos2name["..."] === "undefined") - throw _cs.exception(func_name, "too many arguments provided"); - args = []; - for (; i < func_args.length; i++) { - check_validity(func_name, pos2name["..."], func_args[i], spec[pos2name["..."]].valid); - args.push(func_args[i]); - } - params[pos2name["..."]] = args; - } - } - - /* provide parameter processing hook */ - _cs.hook("ComponentJS:params:" + func_name + ":leave", "none", { args: func_args, spec: spec, params: params }); - - /* return prepared parameter object */ - return params; - }; - - /* Base16 encoding (number) */ - _cs.base16_number = function (num, min, uppercase) { - var base16 = ""; - if (typeof min === "undefined") - min = 0; - if (typeof uppercase === "undefined") - uppercase = false; - var charset = uppercase ? "0123456789ABCDEF" : "0123456789abcdef"; - while (num > 0 || min > 0) { - base16 = charset.charAt(Math.floor(num % 16)) + base16; - num = Math.floor(num / 16); - if (min > 0) - min--; - } - return base16; - }; - - /* advanced: 128-bit Counter-ID generation */ - _cs.cid = (function () { - /* 128-bit emulated via 4 x 32-bit JavaScript 64-bit-floating-point-based "number" */ - var counter = [ 0, 0, 0, 0 ]; - var base = 4294967296; /* = 2^32 */ - - /* generate the next Counter-ID */ - return function () { - /* increase counter */ - counter[3]++; - var carry = 0; - for (var i = 3; i >= 0; i--) { - carry += counter[i]; - counter[i] = Math.floor(carry % base); - carry = Math.floor(carry / base); - } - - /* return counter */ - return ( - _cs.base16_number(counter[0], 8, true) + - _cs.base16_number(counter[1], 8, true) + - _cs.base16_number(counter[2], 8, true) + - _cs.base16_number(counter[3], 8, true) - ); - }; - })(); - - /* for passing a function as a callback parameter, - wrap the function into a proxy function which - has a particular excecution scope. Also supports - optional cloning which allows to carry a private - context which will be cloned together with function */ - _cs.proxy = function (ctx, func, clonable) { - /* support plain method name */ - if (_cs.istypeof(func) === "string") - if (_cs.istypeof(ctx) === "object") - if (_cs.istypeof(ctx[func]) === "function") - func = ctx[func]; - - /* fallback for clonable parameter */ - if (!_cs.isdefined(clonable)) - clonable = false; - - /* define the generator */ - var generator = function () { - /* generate new wrapper function */ - var proxy = function () { - /* if context is an object, annotate it with - the real "this" pointer of this method call */ - if (_cs.istypeof(arguments.callee.__ctx__) === "object") - arguments.callee.__ctx__.__this__ = this; - - /* just pass execution through to wrapped function - with our attached store as its execution context object */ - return func.apply(arguments.callee.__ctx__, arguments); - }; - - /* create the attached store object - (either with fresh or cloned context) */ - proxy.__ctx__ = (clonable ? _cs.clone(_cs.isdefined(this.__ctx__) ? this.__ctx__ : ctx) : ctx); - - /* add ourself as the cloning function */ - if (clonable) - proxy.clone = generator; - - /* set "guid" property to the same of original function, - so it is garbage collected correctly */ - proxy.guid = func.guid = (func.guid || proxy.guid || _cs.cid()); - - /* return the new wrapper function */ - return proxy; - }; - - /* run the generator once */ - return generator.call({}); - }; - - /* generate a proxy function which memoizes/caches the result of an - idempotent function (a function without side-effects which always - returns the same output value on the same input parameters) */ - _cs.memoize = function (func) { - var f = function () { - var key = _cs.json(_cs.slice(arguments, 0)); - var val; val = undefined; - if (typeof arguments.callee.cache[key] !== "undefined") { - /* take memoized/cached value */ - val = arguments.callee.cache[key]; - } - else { - /* calculate new value and memoize/cache it */ - val = func.apply(this, arguments); - arguments.callee.cache[key] = val; - } - return val; - }; - f.cache = {}; - return f; - }; - - /* generate a proxy function which uses "currying" - to remember its initially supplied arguments */ - _cs.curry = function (func) { - var args_stored = _cs.slice(arguments, 1); - return function () { - var args_supplied = _cs.slice(arguments, 0); - var args = _cs.concat(args_stored, args_supplied); - return func.apply(this, args); - }; - }; - - /* for defining getter/setter style attributes */ - $cs.attribute = function () { - /* determine parameters */ - var params = $cs.params("attribute", arguments, { - name: { pos: 0, req: true }, - def: { pos: 1, req: true }, - validate: { pos: 2, def: null } - }); - - /* return closure-based getter/setter method */ - return _cs.proxy({ value: params.def }, function (value_new, validate_only) { - /* remember old value */ - var value_old = this.value; - - /* act on new value if given */ - if (arguments.length > 0) { - /* check whether new value is valid */ - var is_valid = true; - if (params.validate !== null) { - /* case 1: plain type comparison */ - if ( typeof params.validate === "string" || - typeof params.validate === "boolean" || - typeof params.validate === "number" ) - is_valid = (value_new === params.validate); - - /* case 2: regular expression string match */ - else if ( typeof params.validate === "object" && - params.validate instanceof RegExp ) - is_valid = params.validate.test(value_new); - - /* case 3: flexible callback function check */ - else if (typeof params.validate === "function") - is_valid = params.validate(value_new, value_old, validate_only, params.name); - - /* otherwise: error */ - else - throw _cs.exception("attribute", - "validation value \"" + params.validate + "\" " + - "for attribute \"" + params.name + "\" " + - "is of unsupported type \"" + (typeof params.validate) + "\""); - } - - /* either return validation result... */ - if (typeof validate_only !== "undefined" && validate_only) - return is_valid; - - /* ...or set new valid value... */ - else if (is_valid) { - /* set new value */ - this.value = value_new; - - /* optionally notify observers */ - var obj = this.__this__; - if ( typeof obj !== "undefined" && - typeof obj.notify === "function") - obj.notify.call(obj, "attribute:set:" + params.name, value_new, value_old, params.name); - } - - /* ...or throw an exception */ - else - throw _cs.exception("attribute", - "invalid value \"" + value_new + "\" " + - "for attribute \"" + params.name + "\""); - } - - /* return old value */ - return value_old; - }, true); - }; - - /* internal hook registry */ - _cs.hooks = {}; - - /* internal hook processing */ - _cs.hook_proc = { - "none": { init: undefined, step: function ( ) { } }, - "pass": { init: function (a) { return a[0]; }, step: function (a, b) { return b; } }, - "or": { init: false, step: function (a, b) { return a || b; } }, - "and": { init: true, step: function (a, b) { return a && b; } }, - "mult": { init: 1, step: function (a, b) { return a * b; } }, - "add": { init: 0, step: function (a, b) { return a + b; } }, - "append": { init: "", step: function (a, b) { return a + b; } }, - "push": { init: [], step: function (a, b) { a.push(b); return a; } }, - "concat": { init: [], step: function (a, b) { return _cs.concat(a, b); } }, - "insert": { init: {}, step: function (a, b) { a[b] = true; return a; } }, - "extend": { init: {}, step: function (a, b) { return _cs.extend(a, b); } } - }; - - /* latch into internal ComponentJS hook */ - _cs.latch = function (name, cb) { - /* sanity check arguments */ - if (arguments.length < 2) - throw _cs.exception("latch(internal)", "missing arguments"); - - /* on-the-fly create hook callback registry */ - if (typeof _cs.hooks[name] === "undefined") - _cs.hooks[name] = []; - - /* store callback in hook callback registry */ - var args = _cs.slice(arguments, 2); - var id = _cs.cid(); - _cs.hooks[name].push({ id: id, cb: cb, args: args }); - return id; - }; - - /* unlatch from internal ComponentJS hook */ - _cs.unlatch = function (name, id) { - /* sanity check arguments */ - if (arguments.length !== 2) - throw _cs.exception("unlatch(internal)", "invalid number of arguments"); - if (typeof _cs.hooks[name] === "undefined") - throw _cs.exception("unlatch(internal)", "no such hook"); - - /* search for callback in hook callback registry */ - var k = -1; - for (var i = 0; i < _cs.hooks[name].length; i++) { - if (_cs.hooks[name][i].id === id) { - k = i; - break; - } - } - if (k === -1) - throw _cs.exception("unlatch(internal)", "no such latched callback"); - - /* remove callback from hook callback registry */ - _cs.hooks[name] = _cs.hooks[name].splice(k, 1); - return; - }; - - /* provide internal ComponentJS hook */ - _cs.hook = function (name, proc) { - /* sanity check arguments */ - if (arguments.length < 2) - throw _cs.exception("hook(internal)", "missing argument"); - if (typeof _cs.hook_proc[proc] === "undefined") - throw _cs.exception("hook(internal)", "no such result processing defined"); - - /* start result with the initial value */ - var result = _cs.hook_proc[proc].init; - var args = null; - if (typeof result === "function") { - args = _cs.slice(arguments, 2); - result = result.call(null, args); - } - - /* give all registered callbacks a chance to - execute and modify the current result */ - if (typeof _cs.hooks[name] !== "undefined") { - if (args === null) - args = _cs.slice(arguments, 2); - _cs.foreach(_cs.hooks[name], function (l) { - /* call latched callback */ - var r = l.cb.apply({ - args: l.args, /* latch arguments */ - result: result, /* current result */ - hooks: _cs.hooks[name].length, /* total number of hooks latched */ - _cs: _cs, /* internal ComponentJS API */ - $cs: $cs /* external ComponentJS API */ - }, args); /* hook arguments */ - - /* process/merge results */ - result = _cs.hook_proc[proc].step.call(null, result, r); - }); - } - - /* return the final result */ - return result; - }; - - - /* - ** CLASS SYSTEM - */ - - /* utility function: define a JavaScript "class" */ - _cs.clazz_or_trait = function (params, is_clazz) { - /* - * STEP 1: CREATE NEW CLASS - */ - - /* create technical class constructor */ - var clazz = function () { - /* remember information */ - var obj = this; - var clz = arguments.callee; - var arg = arguments; - - /* support also calls like "foo()" instead of "new foo()" */ - if (!(obj instanceof clz)) - return new clz(); /* RECURSION */ - - /* initialize all mixin traits and this class (or trait) */ - var init = function (obj, clz, arg, exec_cons) { - /* depth-first visit of parent class */ - var extend = _cs.annotation(clz, "extend"); - if (extend !== null) - arguments.callee(obj, extend, arg, false); /* RECURSION */ - - /* depth-first visit of mixin traits */ - var mixin = _cs.annotation(clz, "mixin"); - if (mixin !== null) - for (var i = 0; i < mixin.length; i++) - arguments.callee(obj, mixin[i], arg, true); /* RECURSION */ - - /* establish clones of all own dynamic fields */ - var dynamics = _cs.annotation(clz, "dynamics"); - if (dynamics !== null) { - for (var field in dynamics) { - if (_cs.isown(dynamics, field)) { - if ( _cs.istypeof(dynamics[field]) !== "null" && - _cs.istypeof(dynamics[field].clone) === "function") - obj[field] = dynamics[field].clone(); - else - obj[field] = _cs.clone(dynamics[field]); - } - } - } - - /* explicitly call optional constructor function - NOTICE: a clazz gets supplied the original constructor - parameters (we assume that it knows what to do with - all or at least the N initial parameters as it is a - real parent/base/super class) and has to call its own - parent/base/super constructor itself via this.base(), - but a trait intentionally gets no constructor parameters - passed-through (as it cannot know where it gets mixed - into, so it cannot know what to do with the parameters) */ - if (exec_cons) { - var cons = _cs.annotation(clz, "cons"); - if (cons !== null) { - if (_cs.istypeof(clz) === "clazz") - cons.apply(obj, arg); - else - cons.call(obj); - } - } - }; - init(obj, clz, arg, true); - - return; - }; - - /* - * STEP 2: OPTIONALLY IMPLICITLY INHERIT FROM PARENT CLASS - */ - - var no_internals = function (name /*, value */) { - return !name.match("^(?:base|__ComponentJS_[A-Za-z]+__)$"); - }; - - if (_cs.isdefined(params.extend)) { - /* inherit all static fields */ - _cs.extend(clazz, params.extend, no_internals); - - /* set the prototype chain to inherit from parent class, - but WITHOUT calling the parent class's constructor function */ - var ctor = function () { - this.constructor = clazz; - }; - ctor.prototype = params.extend.prototype; - clazz.prototype = new ctor(); - - /* remember parent class */ - _cs.annotation(clazz, "extend", params.extend); - } - - /* - * STEP 3: OPTIONALLY EXPLICITLY INHERIT FROM MIXIN CLASSES - */ - - if (_cs.isdefined(params.mixin)) { - /* inherit from mixin classes */ - for (var i = 0; i < params.mixin.length; i++) { - /* inherit all static fields */ - _cs.extend(clazz, params.mixin[i], no_internals); - - /* inherit prototype methods */ - _cs.mixin(clazz.prototype, params.mixin[i].prototype, no_internals); - } - - /* remember mixin classes */ - _cs.annotation(clazz, "mixin", params.mixin); - } - - /* - * STEP 4: OPTIONALLY SET OWN FIELDS/METHODS - */ - - /* remember user-supplied constructor function - (and provide fallback implementation) */ - var cons = $cs.nop; - if (_cs.isdefined(params.cons)) - cons = params.cons; - else if (_cs.isdefined(params.extend)) - cons = function () { this.base(); }; - _cs.annotation(clazz, "cons", cons); - - /* provide name for underlying implementation of "base()" for constructor */ - _cs.annotation(cons, "name", "cons"); - if (_cs.isdefined(params.extend)) - _cs.annotation(cons, "base", _cs.annotation(params.extend, "cons")); - - /* remember user-supplied setup function */ - if (_cs.isdefined(params.setup)) - _cs.annotation(clazz, "setup", params.setup); - - /* extend class with own properties and methods */ - if (_cs.isdefined(params.statics)) - _cs.extend(clazz, params.statics); - if (_cs.isdefined(params.protos)) - _cs.mixin(clazz.prototype, params.protos); - - /* remember dynamics for per-object initialization */ - if (_cs.isdefined(params.dynamics)) - _cs.annotation(clazz, "dynamics", params.dynamics); - - /* internal utility method for resolving an annotation on a - possibly cloned function (just for the following "base" method). - Notice: for a cloned function the clone is a wrapper annotated - with the annoation "clone" set to "true"! */ - var resolve = function (func, name) { - var result = _cs.annotation(func, name); - while (result === null && _cs.annotation(func.caller, "clone") === true) { - result = _cs.annotation(func.caller, name); - func = func.caller; - } - return result; - }; - - /* explicitly add "base()" utility method for calling - the base/super/parent function in the inheritance/mixin chain */ - clazz.prototype.base = function () { - /* NOTICE: arguments.callee are we just ourself (this function), while - arguments.callee.caller is the function calling this.base()! - and because our cs.clone() creates wrapper functions we - optionally have to take those into account during resolving, too! */ - var name = resolve(arguments.callee.caller, "name"); - var base = resolve(arguments.callee.caller, "base"); - var extend = _cs.annotation(this.constructor, "extend"); - - /* attempt 1: call base/super/parent function in mixin chain */ - if (_cs.istypeof(base) === "function") - return base.apply(this, arguments); - - /* attempt 2: call base/super/parent function in inheritance chain (directly on object) */ - else if ( _cs.istypeof(name) === "string" && - _cs.istypeof(extend) === "clazz" && - _cs.istypeof(extend[name]) === "function") - return extend[name].apply(this, arguments); - - /* attempt 3: call base/super/parent function in inheritance chain (via prototype object) */ - else if ( _cs.istypeof(name) === "string" && - _cs.istypeof(extend) === "clazz" && - _cs.istypeof(extend.prototype) === "object" && - _cs.istypeof(extend.prototype[name]) === "function") - return extend.prototype[name].apply(this, arguments); - - /* else just give up and throw an exception */ - else - throw _cs.exception("base", "no base method found for method \"" + - name + "\" in inheritance/mixin chain"); - }; - - /* - * STEP 5: ALLOW TRAITS TO POST-ADJUST/SETUP DEFINED CLASS - */ - - /* only classes execute trait setups... */ - if (is_clazz) { - var setup = function (clazz, trait) { - /* depth-first traversal */ - if (_cs.istypeof(_cs.annotation(trait, "mixin")) === "array") { - var mixin = _cs.annotation(trait, "mixin"); - for (var i = 0; i < mixin.length; i++) - arguments.callee(clazz, mixin[i]); /* RECURSION */ - } - - /* execute optionally existing setup function */ - if (_cs.istypeof(_cs.annotation(trait, "setup")) === "function") - _cs.annotation(trait, "setup").call(clazz); - }; - setup(clazz, clazz); - } - - /* - * STEP 6: PROVIDE RESULTS - */ - - /* optionally insert class into global namespace ourself */ - if (typeof params.name === "string") - $cs.ns(params.name, clazz); - - /* return created class */ - return clazz; - }; - - /* API function: define a usual JavaScript "class" */ - $cs.clazz = function () { - /* determine parameters */ - var params = $cs.params("clazz", arguments, { - name: { def: undefined, valid: "string" }, - extend: { def: undefined, valid: "clazz" }, - mixin: { def: undefined, valid: "[trait*]" }, - cons: { def: undefined, valid: "function" }, - dynamics: { def: undefined, valid: "object" }, - protos: { def: undefined, valid: "object" }, - statics: { def: undefined, valid: "object" } - }); - - /* just pass through definition */ - var clazz = _cs.clazz_or_trait(params, true); - - /* mark object as a logical ComponentJS "class" */ - _cs.annotation(clazz, "type", "clazz"); - - /* return created class */ - return clazz; - }; - - /* API function: define a Scala-inspired "trait" */ - $cs.trait = function () { - /* determine parameters */ - var params = $cs.params("trait", arguments, { - name: { def: undefined, valid: "string" }, - mixin: { def: undefined, valid: "[trait*]" }, - cons: { def: undefined, valid: "function" }, - setup: { def: undefined, valid: "function" }, - dynamics: { def: undefined, valid: "object" }, - protos: { def: undefined, valid: "object" }, - statics: { def: undefined, valid: "object" } - }); - - /* just pass through definition */ - var trait = _cs.clazz_or_trait(params, false); - - /* mark object as a logical ComponentJS "trait" */ - _cs.annotation(trait, "type", "trait"); - - /* return created trait */ - return trait; - }; - - - /* - ** GENERIC PATTERN TRAITS - */ - - /* generic pattern: id */ - $cs.pattern.id = $cs.trait({ - dynamics: { - id: $cs.attribute("id", null) - } - }); - - /* generic pattern: name */ - $cs.pattern.name = $cs.trait({ - dynamics: { - name: $cs.attribute("name", "") - } - }); - - /* generic pattern: tree */ - $cs.pattern.tree = $cs.trait({ - mixin: [ - $cs.pattern.name - ], - dynamics: { - parent: $cs.attribute("parent", null), - children: $cs.attribute("children", []) - }, - protos: { - /* method: path to (and including) node as either object array or name string */ - path: function (separator) { - var path, node; - if (typeof separator === "undefined") { - /* return path as object array */ - path = []; - for (node = this; node !== null; node = node.parent()) - path.push(node); - } - else { - /* return path as name string */ - path = ""; - if (this.parent() === null) - path = separator; - else { - for (node = this; node.parent() !== null; node = node.parent()) - path = separator + node.name() + path; - } - } - return path; - }, - - /* method: attach node to tree */ - attach: function (theparent) { - if (this.parent() !== null) - this.detach(); - var children = theparent.children(); - children.push(this); - theparent.children(children); - this.parent(theparent); - }, - - /* method: detach node from tree */ - detach: function () { - if (this.parent() !== null) { - var self = this; - this.parent().children(_cs.filter(this.parent().children(), function (x) { - return x !== self; - })); - this.parent(null); - } - }, - - /* method: walk tree up */ - walk_up: function (callback, ctx) { - var depth, node; - for (depth = 0, node = this; node !== null; node = node.parent(), depth++) - ctx = callback(depth, node, ctx); - return ctx; - }, - - /* method: walk tree downward */ - walk_down: function (callback, ctx) { - var _walk = function (depth, node, ctx) { - if (typeof callback === "function") - ctx = callback(depth, node, ctx, false); - var children = node.children(); - for (var i = 0; i < children.length; i++) - ctx = _walk(depth + 1, children[i], ctx); - if (typeof callback === "function") - ctx = callback(depth, node, ctx, true); - return ctx; - }; - ctx = _walk(0, this, ctx); - return ctx; - }, - - /* method: dump tree as indented string representation */ - _tree_dump: function (callback) { - return this.walk_down(function (depth, node, output, depth_first) { - if (!depth_first) { - for (var n = 0; n < depth; n++) - output += " "; - output += "\"" + node.name() + "\""; - if (typeof callback === "function") - output += ": " + callback(node); - output += "\n"; - } - return output; - }, ""); - } - } - }); - - /* generic pattern: configuration */ - $cs.pattern.config = $cs.trait({ - dynamics: { - /* attributes */ - __config: {} - }, - protos: { - /* method: get/set particular configuration item */ - cfg: function (name, value) { - var result; - if (arguments.length === 0) { - /* return list of keys */ - result = []; - for (var key in this.__config) - if (_cs.isown(this.__config, key)) - result.push(key); - } - else if (arguments.length === 1 && typeof name === "string") { - /* retrieve value */ - result = this.__config[name]; - } - else if (arguments.length === 2 && value !== null) { - /* set value */ - result = this.__config[name]; - this.__config[name] = value; - } - else if (arguments.length === 2) { - /* remove key/value pair */ - result = this.__config[name]; - delete this.__config[name]; - } - else - throw _cs.exception("cfg", "invalid arguments"); - return result; - } - } - }); - - /* generic pattern: spool */ - $cs.pattern.spool = $cs.trait({ - dynamics: { - /* attributes */ - __spool: {} - }, - protos: { - /* spool an action for grouped execution */ - spool: function () { - /* determine parameters */ - var params = $cs.params("spool", arguments, { - name: { pos: 0, req: true }, - ctx: { pos: 1, req: true }, - func: { pos: 2, req: true }, - args: { pos: "...", def: [] } - }); - - /* sanity check parameters */ - if (!_cs.istypeof(params.func).match(/^(string|function)$/)) - throw _cs.exception("spool", "invalid function parameter (neither function object nor method name)"); - if (_cs.istypeof(params.func) === "string") { - if (_cs.istypeof(params.ctx[params.func]) !== "function") - throw _cs.exception("spool", "invalid method name: \"" + params.func + "\""); - params.func = params.ctx[params.func]; - } - - /* spool cleanup action */ - if (!_cs.isdefined(this.__spool[params.name])) - this.__spool[params.name] = []; - this.__spool[params.name].push(params); - return; - }, - - /* return number of actions which are spooled */ - spooled: function () { - /* determine parameters */ - var params = $cs.params("spooled", arguments, { - name: { pos: 0, req: true } - }); - - /* return number of actions which are spooled */ - return ( - _cs.isdefined(this.__spool[params.name]) ? - this.__spool[params.name].length : 0 - ); - }, - - /* execute spooled actions */ - unspool: function () { - /* determine parameters */ - var params = $cs.params("unspool", arguments, { - name: { pos: 0, req: true } - }); - - /* execute spooled actions (in reverse spooling order) */ - var actions = this.__spool[params.name]; - if (!_cs.isdefined(actions)) - throw _cs.exception("unspool", "no such spool: \"" + params.name + "\""); - for (var i = actions.length - 1; i >= 0; i--) - actions[i].func.apply(actions[i].ctx, actions[i].args); - - /* destroy spool of now executed cleanup actions */ - delete this.__spool[params.name]; - return; - } - } - }); - - /* internal utility function: split "[path:]name" - specification into a component object and a spool name */ - _cs.spool_spec_parse = function (comp, spec) { - var info = {}; - info.comp = comp; - info.name = spec; - var m = info.name.match(/^([^:]+):(.+)$/); - if (m !== null) { - info.comp = $cs(comp, m[1]); - info.name = m[2]; - } - return info; - }; - - /* generic pattern: tree property */ - $cs.pattern.property = $cs.trait({ - mixin: [ - $cs.pattern.tree, - $cs.pattern.config - ], - protos: { - /* get/set a property */ - property: function () { - /* determine parameters */ - var params = $cs.params("property", arguments, { - name: { pos: 0, req: true }, - value: { pos: 1, def: undefined }, - scope: { def: undefined }, - bubbling: { def: true }, - targeting: { def: true }, - returnowner: { def: false } - }); - - /* sanity check usage */ - if (!params.targeting && !params.bubbling) - throw _cs.exception("property", "disabling both targeting and bubbling makes no sense"); - - /* start resolving with an undefined value */ - var result; result = undefined; - - /* get old configuration value - (on current node or on any parent node) */ - var v; - for (var scope = [], node = this; - node !== null; - scope.unshift(node.name()), node = node.parent()) { - - /* optionally skip the target component - (usually if a property on the parent components - should be resolved only, but the scoping for the - target component should be still taken into account - on the parent) */ - if (scope.length === 0 && !params.targeting) - continue; - - /* first try: child-scoped property */ - if (scope.length > 0) { - for (var i = scope.length - 1; i >= 0; i--) { - var probePath = scope.slice(0, i + 1).join("/"); - v = node.cfg("ComponentJS:property:" + params.name + "@" + probePath); - if (typeof v !== "undefined") - break; - } - if (typeof v !== "undefined") { - result = (params.returnowner ? node : v); - break; - } - } - - /* second try: unscoped property */ - v = node.cfg("ComponentJS:property:" + params.name); - if (typeof v !== "undefined") { - result = (params.returnowner ? node : v); - break; - } - - /* if we should not bubble, stop immediately */ - if (!params.bubbling) - break; - } - - /* optionally set new configuration value - (on current node only) */ - if (typeof params.value !== "undefined") - if (typeof params.scope !== "undefined") - this.cfg("ComponentJS:property:" + params.name + "@" + params.scope, params.value); - else - this.cfg("ComponentJS:property:" + params.name, params.value); - - /* return result (either the old configuration - value or the owning component) */ - return result; - } - } - }); - - /* generic pattern: specification */ - $cs.pattern.spec = $cs.trait({ - mixin: [ - /* name-based identification (mandatory) */ - $cs.pattern.name - ], - dynamics: { - /* key/value-based specification (optional) */ - __spec: {} - }, - protos: { - /* method: configure specification */ - spec: function () { - var spec = this.__spec; - if (arguments.length === 0) - return spec; - else if (arguments.length === 1 && typeof arguments[0] === "string") - return spec[arguments[0]]; - else { - for (var i = 0; i < arguments.length; i++) { - if (typeof arguments[i] === "string") { - spec[arguments[i]] = arguments[i + 1]; - i++; - } - else if (typeof arguments[i] === "object") { - for (var key in arguments[i]) - if (_cs.isown(arguments[i], key)) - spec[key] = arguments[i][key]; - } - } - } - return; - }, - - /* method: determine whether this object matches the name/spec patterns */ - matches: function (name_pattern, spec_pattern) { - /* step 1: match mandatory name */ - if (typeof name_pattern === "string") { - if (this.name() !== name_pattern) - return false; - } - else if ( typeof name_pattern === "object" && - name_pattern instanceof RegExp) { - if (!(this.name().match(name_pattern))) - return false; - } - else - throw _cs.exception("matches", "invalid name pattern"); - - /* step 2: match optional specification */ - var spec = this.__spec; - for (var key in spec_pattern) { - if (!_cs.isown(spec_pattern, key)) - continue; - if (!_cs.isdefined(spec[key])) - return false; - var value = spec_pattern[key]; - switch (typeof spec[key]) { - case "number": - case "boolean": - if (spec[key] !== value) - return false; - break; - case "string": - if (!( ( typeof value === "string" && - spec[key] === value) || - ( typeof value === "object" && - value instanceof RegExp && - !(spec[key].match(value))))) - return false; - break; - } - } - return true; - } - } - }); - - /* generic pattern: observable */ - $cs.pattern.observable = $cs.trait({ - dynamics: { - /* internal state */ - __listener: {} - }, - protos: { - /* attach a listener */ - listen: function () { - /* determine parameters */ - var params = $cs.params("listen", arguments, { - name: { pos: 0, req: true }, - ctx: { def: this }, - func: { pos: 1, req: true }, - args: { pos: "...", def: [] }, - spec: { def: null } /* customized matching */ - }); - - /* attach listener information */ - var id = _cs.cid(); - this.__listener[id] = params; - return id; - }, - - /* check for an attached listener */ - listening: function () { - /* determine parameters */ - var params = $cs.params("listening", arguments, { - id: { pos: 0, req: true } - }); - - /* check whether listener is attached */ - return (typeof this.__listener[params.id] !== "undefined"); - }, - - /* detach a listener */ - unlisten: function () { - /* determine parameters */ - var params = $cs.params("unlisten", arguments, { - id: { pos: 0, req: true } - }); - - /* detach parameters from component */ - if (typeof this.__listener[params.id] === "undefined") - throw _cs.exception("unlisten", "listener not found"); - var listener = this.__listener[params.id]; - delete this.__listener[params.id]; - return listener; - }, - - /* notify all listeners */ - notify: function () { - /* determine parameters */ - var params = $cs.params("notify", arguments, { - name: { pos: 0, req: true }, - args: { pos: "...", def: [] }, - matches: { def: function (p, l) { return p.name === l.name; } } /* customized matching */ - }); - - /* notify all listeners */ - for (var id in this.__listener) { - if (_cs.isown(this.__listener, id)) { - var listener = this.__listener[id]; - if (params.matches(params, listener)) { - var args = _cs.concat(listener.args, params.args); - listener.func.apply(listener.ctx, args); - } - } - } - } - } - }); - - /* generic pattern: event */ - $cs.pattern.event = $cs.clazz({ - mixin: [ - $cs.pattern.spec - ], - dynamics: { - /* attributes */ - target: $cs.attribute("target", null), /* target object the event is send to */ - propagation: $cs.attribute("propagation", true), /* whether event propagation should continue */ - processing: $cs.attribute("processing", true), /* whether final default event processing should be performed */ - dispatched: $cs.attribute("dispatched", false), /* whether event was dispatched at least once to a subscriber */ - decline: $cs.attribute("decline", false), /* whether event was declined by subscriber */ - state: $cs.attribute("state", "targeting"), /* state of dispatching: capturing, targeting, spreading, bubbling */ - result: $cs.attribute("result", undefined), /* optional result value event subscribers can provide */ - async: $cs.attribute("async", false) /* whether event is dispatched asynchronously */ - } - }); - - /* event factory */ - $cs.event = function () { - /* determine parameters */ - var params = $cs.params("event", arguments, { - name: { pos: 0, req: true }, - spec: { def: {} }, - target: { pos: 1, req: true }, - propagation: { pos: 2, def: true }, - processing: { pos: 3, def: true }, - dispatched: { pos: 4, def: false }, - decline: { pos: 5, def: false }, - state: { pos: 6, def: "targeting" }, - result: { pos: 7, def: undefined }, - async: { pos: 8, def: false } - }); - - /* create new event */ - var ev = new $cs.pattern.event(); - - /* configure event */ - ev.name (params.name); - ev.target (params.target); - ev.propagation(params.propagation); - ev.processing (params.processing); - ev.dispatched (params.dispatched); - ev.decline (params.decline); - ev.state (params.state); - ev.result (params.result); - ev.spec (params.spec); - ev.async (params.async); - - return ev; - }; - - /* generic pattern: eventing */ - $cs.pattern.eventing = $cs.trait({ - dynamics: { - __subscription: {} - }, - protos: { - /* subscribe on an event */ - subscribe: function () { - /* determine parameters */ - var params = $cs.params("subscribe", arguments, { - name: { pos: 0, req: true }, - spec: { def: {} }, - ctx: { def: this }, - func: { pos: 1, req: true }, - args: { pos: "...", def: [] }, - capturing: { def: false }, - spreading: { def: false }, - bubbling: { def: true }, - noevent: { def: false }, - exclusive: { def: false }, - spool: { def: null } - }); - - /* honor exclusive request */ - var subscriptions = this._subscriptions(params.name, params.spec); - if (subscriptions.length === 1 && subscriptions[0].exclusive) - throw _cs.exception("subscribe", "existing exclusive subscription prevents additional one"); - if (params.exclusive && subscriptions.length > 0) - throw _cs.exception("subscribe", "non-exclusive subscription(s) prevent exclusive one"); - - /* attach parameters to component */ - var id = _cs.cid(); - this.__subscription[id] = params; - - /* optionally spool reverse operation */ - if (params.spool !== null) { - var info = _cs.spool_spec_parse(this, params.spool); - info.comp.spool(info.name, this, "unsubscribe", id); - } - - return id; - }, - - /* unsubscribe from an event */ - unsubscribe: function () { - /* determine parameters */ - var params = $cs.params("unsubscribe", arguments, { - id: { pos: 0, req: true } - }); - - /* detach parameters from component */ - if (typeof this.__subscription[params.id] === "undefined") - throw _cs.exception("unsubscribe", "subscription not found"); - delete this.__subscription[params.id]; - return; - }, - - /* determine subscription existence */ - subscription: function () { - /* determine parameters */ - var params = $cs.params("subscription", arguments, { - id: { pos: 0, req: true } - }); - - /* determine whether subscription exists */ - return (typeof this.__subscription[params.id] !== "undefined"); - }, - - /* determine subscriptions (internal) */ - _subscriptions: function () { - /* determine parameters */ - var params = $cs.params("subscriptions", arguments, { - name: { pos: 0, req: true }, - spec: { pos: 1, def: {} } - }); - - /* make an event for matching only */ - var ev = $cs.event({ - name: params.name, - spec: params.spec, - target: $cs.nop - }); - - /* find and return all matching subscriptions */ - var subscriptions = []; - for (var id in this.__subscription) { - if (!_cs.isown(this.__subscription, id)) - continue; - var s = this.__subscription[id]; - if (ev.matches(s.name, s.spec)) - subscriptions.push(s); - } - return subscriptions; - }, - - /* publish an event */ - publish: function () { - var i; - var self = this; - - /* determine parameters */ - var params = $cs.params("publish", arguments, { - name: { pos: 0, req: true }, - spec: { def: {} }, - async: { def: false }, - capturing: { def: true }, - spreading: { def: false }, - bubbling: { def: true }, - completed: { def: $cs.nop }, - resultinit: { def: undefined }, - resultstep: { def: function (a, b) { return b; } }, - directresult: { def: false }, - noresult: { def: false }, - firstonly: { def: false }, - silent: { def: false }, - args: { pos: "...", def: [] } - }); - - /* short-circuit processing (1/2) to speed up cases - where no subscribers exist for a local event */ - var short_circuit = false; - if (!params.capturing && !params.spreading && !params.bubbling) { - var subscribers = false; - for (var id in this.__subscription) { - if (!_cs.isown(this.__subscription, id)) - continue; - subscribers = true; - break; - } - if (!subscribers) { - if (params.noresult) - return; - else if (params.directresult) - return params.resultinit; - else - short_circuit = true; - } - } - - /* create event */ - var ev = $cs.event({ - name: params.name, - spec: params.spec, - async: params.async, - result: params.resultinit, - target: self, - propagation: true, - processing: true, - dispatched: false - }); - - /* short-circuit processing (2/2) */ - if (short_circuit) - return ev; - - /* tracing */ - if (!params.silent) { - $cs.debug(1, "event:" + - " " + ev.target().path("/") + ": publish:" + - " name=" + ev.name() + - " async=" + ev.async() + - " capturing=" + params.capturing + - " spreading=" + params.spreading + - " bubbling=" + params.bubbling + - " directresult=" + params.directresult + - " noresult=" + params.noresult + - " firstonly=" + params.firstonly - ); - } - - /* helper function for dispatching event to single component */ - var event_dispatch_single = function (ev, comp, params, state) { - for (var id in comp.__subscription) { - if (!_cs.isown(comp.__subscription, id)) - continue; - var s = comp.__subscription[id]; - if ( ( (state === "capturing" && s.capturing) || - (state === "targeting" ) || - (state === "spreading" && s.spreading) || - (state === "bubbling" && s.bubbling )) && - ev.matches(s.name, s.spec) ) { - - /* verbosity */ - if (!params.silent) - $cs.debug(1, "event: " + comp.path("/") + ": dispatch on " + state); - - /* further annotate event object */ - ev.state(state); - ev.decline(false); - - /* call subscription method */ - var args = _cs.concat( - s.noevent ? [] : [ ev ], - s.args, - params.args - ); - var result = s.func.apply(s.ctx, args); - - /* process return value */ - if (s.noevent && _cs.isdefined(result)) - ev.result(params.resultstep(ev.result(), result)); - - /* control the further dispatching */ - if (!ev.decline()) { - ev.dispatched(true); - if (params.firstonly) - ev.propagation(false); - } - } - } - }; - - /* helper function for dispatching event to all components on hierarchy path */ - var event_dispatch_all = function (ev, comp, params) { - /* determine component tree path */ - var comp_path; - if (params.capturing || params.bubbling) - comp_path = comp.path(); - - /* phase 1: CAPTURING - optionally dispatch event downwards from root component - towards target component for capturing subscribers */ - if (params.capturing) { - for (i = comp_path.length - 1; i >= 1; i--) { - event_dispatch_single(ev, comp_path[i], params, "capturing"); - if (!ev.propagation()) - break; - } - } - - /* phase 2: TARGETING - dispatch event to target component */ - if (ev.propagation()) - event_dispatch_single(ev, comp, params, "targeting"); - - /* phase 3: SPREADING - dispatch event to all descendant components */ - if (params.spreading && ev.propagation()) { - var visit = function (comp, isTarget) { - var cont = true; - if (!isTarget) { - /* dispatch on non-target component */ - event_dispatch_single(ev, comp, params, "spreading"); - if (!ev.propagation()) { - /* if propagation should stop, reset the flag again - as in the spreading phase propagation stops only(!) - for the particular sub-tree, not the propagation - process as a whole! */ - ev.propagation(true); - cont = false; - } - } - if (cont) { - /* dispatch onto all direct child components */ - var children = comp.children(); - for (var i = 0; i < children.length; i++) - visit(children[i], false); - } - }; - visit(comp, true); - } - - /* phase 4: BUBBLING - dispatch event upwards from target component towards - root component for bubbling (regular) subscribers */ - if (params.bubbling && ev.propagation()) { - for (i = 1; i < comp_path.length; i++) { - event_dispatch_single(ev, comp_path[i], params, "bubbling"); - if (!ev.propagation()) - break; - } - } - - /* notify publisher on dispatch completion */ - params.completed.call(comp, ev); - }; - - /* perform event publishing, - either asynchronous or synchronous */ - if (ev.async()) - /* global setTimeout:false */ - setTimeout(_cs.hook("ComponentJS:settimeout:func", "pass", function () { - event_dispatch_all(ev, self, params); - }), 0); - else - event_dispatch_all(ev, self, params); - - /* return the event, directly the result value or no result value at all */ - if (params.noresult) - return; - else if (params.directresult) - return ev.result(); - else - return ev; - } - } - }); - - /* generic pattern: command */ - $cs.pattern.command = $cs.clazz({ - mixin: [ - $cs.pattern.observable - ], - dynamics: { - /* standard attributes */ - ctx: $cs.attribute("ctx", null), - func: $cs.attribute("func", $cs.nop), - args: $cs.attribute("args", []), - async: $cs.attribute("async", false), - - /* usually observed attribute */ - enabled: $cs.attribute({ - name: "enabled", - def: true, - validate: function (v) { return typeof v === "boolean"; } - }) - }, - protos: { - /* method: execute the command */ - execute: function (caller_args, caller_result) { - if (!this.enabled()) - return; - var args = []; - if (this.async()) { - args.push(function (value) { - if (typeof caller_result === "function") - caller_result(value); - }); - } - args = _cs.concat(args, this.args(), caller_args); - return this.func().apply(this.ctx(), args); - } - } - }); - - /* command factory */ - $cs.command = function () { - /* determine parameters */ - var params = $cs.params("command", arguments, { - ctx: { def: null }, - func: { pos: 0, req: true }, - args: { pos: "...", def: [] }, - async: { def: false }, - enabled: { def: true }, - wrap: { def: false } - }); - - /* create new command */ - var cmd = new $cs.pattern.command(); - - /* configure command */ - cmd.ctx (params.ctx); - cmd.func (params.func); - cmd.args (params.args); - cmd.async (params.async); - cmd.enabled(params.enabled); - - /* optionally wrap into convenient "execute" closure */ - var result = cmd; - if (params.wrap) { - result = function () { - var args = _cs.concat(arguments); - var cb = null; - if (arguments.callee.command.async()) - cb = args.pop(); - return arguments.callee.command.execute.call(arguments.callee.command, args, cb); - }; - result.command = cmd; - } - - return result; - }; - - /* component states */ - _cs.states = [ - { /* component is not existing (bootstrapping state transitions only) */ - enter: null, - leave: null, - state: "dead", - color: "#000000" - } - ]; - - /* clear all state transitions (except for "dead" state) */ - _cs.states_clear = function () { - _cs.states = _cs.slice(_cs.states, 0, 1); - return; - }; - - /* add a state transition */ - _cs.states_add = function (target, enter, leave, color, source) { - /* create new state configuration */ - var state = { - enter: enter, - leave: leave, - state: target, - color: color - }; - - /* determine storage position */ - var pos = 1; - while (pos < _cs.states.length) { - if ( source !== null && - _cs.states[pos].state === source) - break; - pos++; - } - - /* store state */ - _cs.states.splice(pos, 0, state); - }; - - /* determine state index via state name */ - _cs.state_name2idx = function (name) { - var idx = -1; - var i; - for (i = 0; i < _cs.states.length; i++) { - if (_cs.states[i].state === name) { - idx = i; - break; - } - } - return idx; - }; - - /* perform a state enter/leave method call */ - _cs.state_method_call = function (type, comp, method) { - var result = true; - var obj = comp.obj(); - if (obj !== null && typeof obj[method] === "function") { - var info = { type: type, comp: comp, method: method, ctx: obj, func: obj[method] }; - _cs.hook("ComponentJS:state-method-call", "none", info); - result = info.func.call(info.ctx); - } - return result; - }; - - /* set of current state transition requests - (modeled via a map to the components) */ - _cs.state_requests = {}; - - /* spawn all progression runs (asynchronously) */ - _cs.state_progression = function () { - /* global setTimeout:false */ - setTimeout(_cs.hook("ComponentJS:settimeout:func", "pass", function () { - /* try to process the transition requests */ - var remove = []; - for (var cid in _cs.state_requests) { - if (!_cs.isown(_cs.state_requests, cid)) - continue; - var req = _cs.state_requests[cid]; - if (_cs.state_progression_single(req)) - remove.push(cid); - } - - /* perform deferred removal of original fields */ - _cs.foreach(remove, function (cid) { - delete _cs.state_requests[cid]; - }); - - /* give plugins a chance to react */ - _cs.hook("ComponentJS:state-change", "none"); - }), 0); - }; - - /* execute single progression run */ - _cs.state_progression_single = function (req) { - var done = false; - _cs.state_progression_run(req.comp, req.state); - if (_cs.states[req.comp.__state].state === req.state) { - if (typeof req.callback === "function") - req.callback.call(req.comp, req.state); - done = true; - } - return done; - }; - - /* perform a single synchronous progression run for a particular component */ - _cs.state_progression_run = function (comp, arg, _direction) { - var i, children; - var state, enter, leave, spooled; - - /* handle optional argument (USED INTERNALLY ONLY) */ - if (typeof _direction === "undefined") - _direction = "upward-and-downward"; - - /* determine index of state by name */ - var state_new = _cs.state_name2idx(arg); - if (state_new === -1) - throw _cs.exception("state", "invalid argument \"" + arg + "\""); - - /* perform upward/downward state transition(s) */ - if (comp.__state < state_new) { - /* transition to higher state */ - while (comp.__state < state_new) { - /* determine names of state and enter method */ - state = _cs.states[comp.__state + 1].state; - enter = _cs.states[comp.__state + 1].enter; - - /* mandatory transition parent component to higher state first */ - if (comp.parent() !== null) { - if (comp.parent().state_compare(state) < 0) { - _cs.state_progression_run(comp.parent(), state, "upward"); /* RECURSION */ - if (comp.parent().state_compare(state) < 0) { - $cs.debug(1, - "state: " + comp.path("/") + ": transition (increase) " + - "REJECTED BY PARENT COMPONENT (" + comp.parent().path("/") + "): " + - "@" + _cs.states[comp.__state].state + " --(" + enter + ")--> " + - "@" + _cs.states[comp.__state + 1].state + ": SUSPENDING CURRENT TRANSITION RUN" - ); - return; - } - } - } - - /* transition current component to higher state second */ - if (_cs.isdefined(comp.__state_guards[enter])) { - $cs.debug(1, - "state: " + comp.path("/") + ": transition (increase) REJECTED BY ENTER GUARD: " + - "@" + _cs.states[comp.__state].state + " --(" + enter + ")--> " + - "@" + _cs.states[comp.__state + 1].state + ": SUSPENDING CURRENT TRANSITION RUN" - ); - return; - } - comp.__state++; - $cs.debug(1, - "state: " + comp.path("/") + ": transition (increase): " + - "@" + _cs.states[comp.__state - 1].state + " --(" + enter + ")--> " + - "@" + _cs.states[comp.__state].state - ); - _cs.hook("ComponentJS:state-invalidate", "none", "states"); - _cs.hook("ComponentJS:state-change", "none"); - - /* execute enter method */ - if (_cs.state_method_call("enter", comp, enter) === false) { - /* FULL STOP: state enter method rejected state transition */ - $cs.debug(1, - "state: " + comp.path("/") + ": transition (increase) REJECTED BY ENTER METHOD: " + - "@" + _cs.states[comp.__state - 1].state + " --(" + enter + ")--> " + - "@" + _cs.states[comp.__state].state + ": SUSPENDING CURRENT TRANSITION RUN" - ); - comp.__state--; - return; - } - - /* notify subscribers about new state */ - comp.publish({ - name: "ComponentJS:state:" + _cs.states[comp.__state].state, - noresult: true, - capturing: false, - spreading: false, - bubbling: false, - async: true, - silent: true - }); - - /* optionally automatically transition - child component(s) to higher state third */ - if (_direction === "upward-and-downward" || _direction === "downward") { - children = comp.children(); - for (i = 0; i < children.length; i++) { - if (children[i].state_compare(state) < 0) { - if ( children[i].state_auto_increase() || - children[i].property("ComponentJS:state-auto-increase") === true) { - _cs.state_progression_run(children[i], state, "downward"); /* RECURSION */ - if (children[i].state_compare(state) < 0) { - /* enqueue state transition for child */ - _cs.state_requests[children[i].id()] = - { comp: children[i], state: state }; - _cs.hook("ComponentJS:state-invalidate", "none", "requests"); - _cs.hook("ComponentJS:state-change", "none"); - } - } - } - } - } - } - } - else if (comp.__state > state_new) { - /* transition to lower state */ - while (comp.__state > state_new) { - /* determine names of state and leave method */ - state = _cs.states[comp.__state].state; - leave = _cs.states[comp.__state].leave; - var state_lower = _cs.states[comp.__state - 1].state; - - /* mandatory transition children component(s) to lower state first */ - children = comp.children(); - for (i = 0; i < children.length; i++) { - if (children[i].state_compare(state_lower) > 0) { - _cs.state_progression_run(children[i], state_lower, "downward"); /* RECURSION */ - if (children[i].state_compare(state_lower) > 0) { - $cs.debug(1, - "state: " + comp.path("/") + ": transition (decrease) " + - "REJECTED BY CHILD COMPONENT (" + children[i].path("/") + "): " + - "@" + _cs.states[comp.__state - 1].state + " <--(" + leave + ")-- " + - "@" + _cs.states[comp.__state].state + ": SUSPENDING CURRENT TRANSITION RUN" - ); - return; - } - } - } - - /* transition current component to lower state second */ - if (_cs.isdefined(comp.__state_guards[leave])) { - $cs.debug(1, - "state: " + comp.path("/") + ": transition (decrease) REJECTED BY LEAVE GUARD: " + - "@" + _cs.states[comp.__state - 1].state + " <--(" + leave + ")-- " + - "@" + _cs.states[comp.__state].state + ": SUSPENDING CURRENT TRANSITION RUN" - ); - return; - } - comp.__state--; - $cs.debug(1, - "state: " + comp.path("/") + ": transition (decrease): " + - "@" + _cs.states[comp.__state].state + " <--(" + leave + ")-- " + - "@" + _cs.states[comp.__state + 1].state - ); - _cs.hook("ComponentJS:state-invalidate", "none", "states"); - _cs.hook("ComponentJS:state-change", "none"); - - /* execute leave method */ - if (_cs.state_method_call("leave", comp, leave) === false) { - /* FULL STOP: state leave method rejected state transition */ - $cs.debug(1, - "state: " + comp.path("/") + ": transition (decrease) REJECTED BY LEAVE METHOD: " + - "@" + _cs.states[comp.__state].state + " <--(" + leave + ")-- " + - "@" + _cs.states[comp.__state + 1].state + ": SUSPENDING CURRENT TRANSITION RUN" - ); - comp.__state++; - return; - } - else { - /* in case leave method successful or not present - automatically unspool still pending actions - on spool named exactly like the left state */ - spooled = comp.spooled(state); - if (spooled > 0) { - $cs.debug(1, "state: " + comp.path("/") + ": auto-unspooling " + spooled + " operation(s)"); - comp.unspool(state); - } - } - - /* notify subscribers about new state */ - comp.publish({ - name: "ComponentJS:state:" + _cs.states[comp.__state].state, - noresult: true, - capturing: false, - spreading: false, - bubbling: false, - async: true, - silent: true - }); - - /* optionally automatically transition - parent component to lower state third */ - if (_direction === "upward-and-downward" || _direction === "upward") { - if (comp.parent() !== null) { - if (comp.parent().state_compare(state_lower) > 0) { - if ( comp.parent().state_auto_decrease() || - comp.parent().property("ComponentJS:state-auto-decrease") === true) { - _cs.state_progression_run(comp.parent(), state_lower, "upward"); /* RECURSION */ - if (comp.parent().state_compare(state_lower) > 0) { - /* enqueue state transition for parent */ - _cs.state_requests[comp.parent().id()] = - { comp: comp.parent(), state: state_lower }; - _cs.hook("ComponentJS:state-invalidate", "none", "requests"); - _cs.hook("ComponentJS:state-change", "none"); - } - } - } - } - } - } - } - }; - - /* generic pattern for state management */ - $cs.pattern.state = $cs.trait({ - mixin: [ - $cs.pattern.tree - ], - dynamics: { - /* attributes */ - __state: 0, /* = dead */ - __state_guards: {}, - state_auto_increase: $cs.attribute("state_auto_increase", false), - state_auto_decrease: $cs.attribute("state_auto_decrease", false) - }, - protos: { - /* get state or set state (or at least trigger transition) */ - state: function () { - /* special case: just retrieve current state */ - var state_old = _cs.states[this.__state].state; - if (arguments.length === 0) - return state_old; - - /* determine parameters */ - var params = $cs.params("state", arguments, { - state: { pos: 0, req: true, - valid: function (s) { return _cs.state_name2idx(s) !== -1; } }, - callback: { pos: 1, def: undefined }, - sync: { def: false } - }); - - /* if requested state is still not reached... */ - if (_cs.states[this.__state].state !== params.state) { - var enqueue = true; - var request = { - comp: this, - state: params.state, - callback: params.callback - }; - if (params.sync) { - /* perform new state transition request (synchronously) */ - if (_cs.state_progression_single(request)) - enqueue = false; - } - if (enqueue) { - /* enqueue new state transition request and trigger - state transition progression (asynchronously) */ - _cs.state_requests[this.id()] = request; - _cs.hook("ComponentJS:state-invalidate", "none", "requests"); - _cs.state_progression(); - } - } - else { - /* still run its optional callback function */ - if (typeof params.callback === "function") - params.callback.call(this, params.state); - } - - /* return old (and perhaps still current) state */ - return state_old; - }, - - /* compare state of component */ - state_compare: function () { - /* determine parameters */ - var params = $cs.params("state", arguments, { - state: { pos: 0, req: true, - valid: function (s) { return _cs.state_name2idx(s) !== -1; } } - }); - - /* determine index of state by name */ - var state = _cs.state_name2idx(params.state); - - /* compare given state against state of component */ - return (this.__state - state); - }, - - /* guard a state enter/leave method */ - guard: function () { - /* determine parameters */ - var params = $cs.params("guard", arguments, { - method: { pos: 0, valid: "string", req: true }, - level: { pos: 1, valid: "number", req: true } - }); - - /* sanity check enter/leave method name */ - var valid = false; - var i; - for (i = 0; i < _cs.states.length; i++) { - if ( _cs.states[i].enter === params.method || - _cs.states[i].leave === params.method) { - valid = true; - break; - } - } - if (!valid) - throw _cs.exception("guard", "no such declared enter/leave method: \"" + - params.method + "\""); - - /* ensure the guard slot exists */ - if (!_cs.isdefined(this.__state_guards[params.method])) - this.__state_guards[params.method] = 0; - - /* activate/deactivate guard */ - var deactivate = false; - if (params.level > 0) - /* increase guard level */ - this.__state_guards[params.method] += params.level; - else if (params.level < 0) { - /* decrease guard level */ - if (this.__state_guards[params.method] < (-params.level)) - throw _cs.exception("guard", "guard level decrease request too large"); - this.__state_guards[params.method] += params.level; - if (this.__state_guards[params.method] === 0) - deactivate = true; - } - else { - /* reset guard level */ - this.__state_guards[params.method] = 0; - deactivate = true; - } - if (deactivate) { - /* finally deactivate guard */ - delete this.__state_guards[params.method]; - - /* give all pending state transitions - (which now might proceed) a chance */ - _cs.state_progression(); - } - } - } - }); - - /* generic pattern: service */ - $cs.pattern.service = $cs.trait({ - mixin: [ - $cs.pattern.eventing - ], - protos: { - /* register a service */ - register: function () { - /* determine parameters */ - var params = $cs.params("register", arguments, { - name: { pos: 0, req: true }, - ctx: { def: this }, - func: { pos: 1, req: true }, - args: { pos: "...", def: [] }, - spool: { def: null }, - capturing: { def: false }, - spreading: { def: false }, - bubbling: { def: true } - }); - - /* create command object to wrap service */ - var cmd = $cs.command({ - ctx: params.ctx, - func: params.func, - args: params.args, - wrap: true - }); - - /* publish changes to command's callable status */ - cmd.command.listen({ - name: "attribute:set:enabled", - args: [ this, params.name ], - func: function (comp, name, value_new, value_old) { - comp.publish({ - name: "ComponentJS:service:" + name + ":callable", - args: [ value_new, value_old ], - capturing: false, - spreading: false, - bubbling: false, - async: true, - noresult: true - }); - } - }); - - /* subscribe to service event */ - var id = this.subscribe({ - name: "ComponentJS:service:" + params.name, - ctx: params.ctx, - func: cmd, - noevent: true, - capturing: params.capturing, - spreading: params.spreading, - bubbling: params.bubbling, - exclusive: true - }); - - /* optionally spool reverse operation */ - if (params.spool !== null) { - var info = _cs.spool_spec_parse(this, params.spool); - info.comp.spool(info.name, this, "unregister", id); - } - - return id; - }, - - /* determine registration existence */ - registration: function () { - /* determine parameters */ - var params = $cs.params("registration", arguments, { - id: { pos: 0, req: true } - }); - - /* determine whether registration exists */ - return this.subscription(params.id); - }, - - /* unregister a service */ - unregister: function () { - /* determine parameters */ - var params = $cs.params("unregister", arguments, { - id: { pos: 0, req: true } - }); - - /* unsubscribe from service event */ - this.unsubscribe(params.id); - return; - }, - - /* make a service callable (enable/disable it) */ - callable: function () { - /* determine parameters */ - var params = $cs.params("callable", arguments, { - name: { pos: 0, req: true }, - value: { pos: 1, def: undefined } - }); - - /* find service command */ - var subscriptions = this._subscriptions(params.name); - if (subscriptions.length !== 1) - return undefined; - var cmd = subscriptions[0].func().command; - - /* get or set "enabled" attribute */ - return cmd.enabled(params.value); - }, - - /* call a service */ - call: function () { - /* determine parameters */ - var params = $cs.params("call", arguments, { - name: { pos: 0, req: true }, - args: { pos: "...", def: [] }, - capturing: { def: false }, - spreading: { def: false }, - bubbling: { def: true } - }); - - /* dispatch service event onto target component */ - var ev = this.publish({ - name: "ComponentJS:service:" + params.name, - args: params.args, - capturing: params.capturing, - spreading: params.spreading, - bubbling: params.bubbling, - firstonly: true, - async: false - }); - - /* ensure that the service event was successfully dispatched - at least once (or our result value would have no meaning) */ - if (!ev.dispatched()) - throw _cs.exception("call", "no such registered service found:" + - " \"" + params.name + "\""); - - /* return the result value */ - return ev.result(); - } - } - }); - - /* generic pattern: shadow object */ - $cs.pattern.shadow = $cs.trait({ - dynamics: { - __obj: null - }, - protos: { - /* get/set corresponding object */ - obj: function (obj) { - if (typeof obj === "undefined") - /* get current object */ - return this.__obj; - else if (typeof obj === "object") { - /* set new object */ - if (obj !== null) { - _cs.annotation(obj, "comp", this); - this.__obj = obj; - } - else { - if (this.__obj !== null) - _cs.annotation(this.__obj, "comp", null); - this.__obj = null; - } - } - else - throw _cs.exception("obj", "invalid argument"); - return this; - }, - - /* get/set attribute in corresponding object */ - access: function (name, value) { - /* sanity check scenario */ - if (typeof name === "undefined") - throw _cs.exception("access", "no attribute name given"); - var obj = this.obj(); - if (obj === null) - throw _cs.exception("access", "still no object attached"); - if (typeof obj[name] === "undefined") - throw _cs.exception("access", "invalid attribute \"" + name + "\""); - - /* access the attribute */ - var value_old = obj[name]; - if (typeof value !== "undefined") - obj[name] = value; - return value_old; - }, - - /* invoke method on corresponding object */ - invoke: function (name) { - /* sanity check scenario */ - if (typeof name === "undefined") - throw _cs.exception("invoke", "no method name given"); - var obj = this.obj(); - if (obj === null) - throw _cs.exception("invoke", "still no object attached"); - if (typeof obj[name] === "undefined") - throw _cs.exception("invoke", "invalid method \"" + name + "\""); - if (_cs.istypeof(obj[name]) !== "function") - throw _cs.exception("invoke", "anything named \"" + name + "\" existing, but not a function"); - - /* call method */ - var args = _cs.slice(arguments, 1); - return obj[name].apply(obj, args); - } - } - }); - - /* generic pattern: socket */ - $cs.pattern.socket = $cs.trait({ - mixin: [ - $cs.pattern.tree, - $cs.pattern.property - ], - dynamics: { - __sockets: {}, - __plugs: {} - }, - protos: { - /* define a socket */ - socket: function () { - /* determine parameters */ - var params = $cs.params("socket", arguments, { - name: { def: "default" }, - scope: { def: null }, - ctx: { pos: 0, req: true }, - plug: { pos: 1, req: true }, - unplug: { pos: 2, req: true }, - spool: { def: null } - }); - - /* sanity check parameters */ - if ( _cs.istypeof(params.plug) === "string" && - _cs.istypeof(params.ctx[params.plug]) !== "function") - throw _cs.exception("socket", "no plug method named \"" + params.plug + "\" found on context object"); - else if ( _cs.istypeof(params.plug) !== "string" && - _cs.istypeof(params.plug) !== "function") - throw _cs.exception("socket", "plug operation neither method name nor function"); - if ( _cs.istypeof(params.unplug) === "string" && - _cs.istypeof(params.ctx[params.unplug]) !== "function") - throw _cs.exception("socket", "no unplug method named \"" + params.unplug + "\" found on context object"); - else if ( _cs.istypeof(params.unplug) !== "string" && - _cs.istypeof(params.unplug) !== "function") - throw _cs.exception("socket", "unplug operation neither method name nor function"); - - /* remember parameters as (optionally scoped) component property */ - var name = "ComponentJS:socket:" + params.name; - if (params.scope !== null) - name += "@" + params.scope; - $cs(this).property(name, params); - - /* remember socket under an id */ - var id = _cs.cid(); - this.__sockets[id] = name; - - /* optionally spool reverse operation */ - if (params.spool !== null) { - var info = _cs.spool_spec_parse(this, params.spool); - info.comp.spool(info.name, this, "unsocket", id); - } - - return id; - }, - - /* destroy a socket */ - unsocket: function () { - /* determine parameters */ - var params = $cs.params("unsocket", arguments, { - id: { pos: 0, req: true } - }); - - /* remove parameters from component */ - if (typeof this.__sockets[params.id] === "undefined") - throw _cs.exception("unsocket", "socket not found"); - - /* remove corresponding property */ - var name = this.__sockets[params.id]; - $cs(this).property(name, null); - - /* remove socket information */ - delete this.__sockets[params.id]; - return; - }, - - /* create a linking/pass-through socket */ - link: function () { - /* determine parameters */ - var params = $cs.params("link", arguments, { - name: { def: "default" }, - scope: { def: null }, - target: { pos: 0, req: true }, - socket: { pos: 1, req: true }, - spool: { def: null } - }); - - /* create a socket and pass-through the - plug/unplug operations to the target */ - return this.socket({ - name: params.name, - scope: params.scope, - spool: params.spool, - ctx: {}, - plug: function (obj) { - var id = _cs.annotation(obj, "link"); - if (id !== null) - throw _cs.exception("link:plug: cannot plug, you have to unplug first"); - id = $cs(params.target).plug({ - name: params.socket, - object: obj, - targeting: true - }); - _cs.annotation(obj, "link", id); - }, - unplug: function (obj) { - var id = _cs.annotation(obj, "link"); - if (id === null) - throw _cs.exception("link:unplug: cannot unplug, you have to plug first"); - $cs(params.target).unplug({ - id: id, - targeting: true - }); - _cs.annotation(obj, "link", null); - } - }); - }, - - /* destroy a link */ - unlink: function () { - /* determine parameters */ - var params = $cs.params("unlink", arguments, { - id: { pos: 0, req: true } - }); - - return this.unsocket(params.id); - }, - - /* plug into a defined socket */ - plug: function () { - /* determine parameters */ - var params = $cs.params("plug", arguments, { - name: { def: "default" }, - object: { pos: 0, req: true }, - spool: { def: null }, - targeting: { def: false } - }); - - /* remember plug operation */ - var id = _cs.cid(); - this.__plugs[id] = params; - - /* pass-though operation to common helper function */ - _cs.plugger("plug", this, params.name, params.object, params.targeting); - - /* optionally spool reverse operation */ - if (params.spool !== null) { - var info = _cs.spool_spec_parse(this, params.spool); - info.comp.spool(info.name, this, "unplug", id); - } - - return id; - }, - - /* unplug from a defined socket */ - unplug: function () { - /* determine parameters */ - var params = $cs.params("unplug", arguments, { - id: { pos: 0, req: true }, - targeting: { def: false } - }); - - /* determine plugging information */ - if (typeof this.__plugs[params.id] === "undefined") - throw _cs.exception("unplug", "plugging not found"); - var name = this.__plugs[params.id].name; - var object = this.__plugs[params.id].object; - - /* pass-though operation to common helper function */ - _cs.plugger("unplug", this, name, object, params.targeting); - - /* remove plugging */ - delete this.__plugs[params.id]; - return; - } - } - }); - - /* internal "plug/unplug to socket" helper functionality */ - _cs.plugger = function (op, origin, name, object, targeting) { - /* resolve the socket property on the parents components - NOTICE 1: we explicitly skip the origin component here as - resolving the socket property also on the origin - component might otherwise return the potentially existing - socket for the child components of the orgin component. - NOTICE 2: we intentionally skip the origin and do not directly - resolve on the parent component as we want to take - scoped sockets (on the parent component) into account! */ - var property = "ComponentJS:socket:" + name; - var socket = origin.property({ name: property, targeting: targeting }); - if (!_cs.isdefined(socket)) - throw _cs.exception(op, "no socket found on parent component(s)"); - - /* determine the actual component owning the socket (for logging purposes only) */ - var owner = origin.property({ name: property, targeting: targeting, returnowner: true }); - $cs.debug(1, "socket: " + owner.path("/") + ": " + name + - " <--(" + op + ")-- " + origin.path("/")); - - /* perform plug/unplug operation */ - if (_cs.istypeof(socket[op]) === "string") - socket.ctx[socket[op]].call(socket.ctx, object, origin); - else if (_cs.istypeof(socket[op]) === "function") - socket[op].call(socket.ctx, object, origin); - else - throw _cs.exception(op, "failed to perform \"" + op + "\" operation"); - }; - - /* utility function: mark a component */ - $cs.mark = function (obj, name) { - var marker = _cs.annotation(obj, "marker"); - if (marker === null) - marker = {}; - marker[name] = true; - _cs.annotation(obj, "marker", marker); - }; - - /* utility function: determine whether a component is marked */ - $cs.marked = function (obj, name) { - var marker = _cs.annotation(obj, "marker"); - if (marker === null) - marker = {}; - return (marker[name] === true); - }; - - /* generic pattern for marking components */ - $cs.pattern.marker = $cs.trait({ - protos: { - mark: function (name) { - $cs.mark(this.obj(), name); - }, - marked: function (name) { - return $cs.marked(this.obj(), name); - } - } - }); - - /* convenient marker traits */ - $cs.marker = { - service: $cs.trait({ cons: function () { $cs.mark(this, "service"); } }), - controller: $cs.trait({ cons: function () { $cs.mark(this, "controller"); } }), - model: $cs.trait({ cons: function () { $cs.mark(this, "model"); } }), - view: $cs.trait({ cons: function () { $cs.mark(this, "view"); } }) - }; - - /* load store via optional plugin */ - _cs.store_load = function (comp) { - if (comp.__store === null) { - _cs.hook("ComponentJS:store-load", "none", comp); - if ( comp.__store === null || - typeof comp.__store !== "object") - comp.__store = {}; - } - }; - - /* save store via optional plugin */ - _cs.store_save = function (comp) { - if (comp.__store !== null) - _cs.hook("ComponentJS:store-save", "none", comp); - }; - - /* generic pattern for store management */ - $cs.pattern.store = $cs.trait({ - dynamics: { - __store: null - }, - protos: { - store: function () { - var key, val; - if (arguments.length === 0) { - /* get all keys */ - _cs.store_load(this); - var keys = []; - for (key in this.__store) - keys.push(key); - return keys; - } - else if (arguments.length === 1 && arguments[0] === null) { - /* clear store */ - this.__store = {}; - _cs.store_save(this); - return null; - } - else if (arguments.length === 1 && typeof arguments[0] === "string") { - /* get value */ - _cs.store_load(this); - key = arguments[0]; - if (typeof this.__store[key] === "undefined") - return null; - else - return this.__store[key]; - } - else if (arguments.length === 2 && arguments[1] === null) { - /* delete value */ - _cs.store_load(this); - key = arguments[0]; - delete this.__store[key]; - _cs.store_save(this); - return null; - } - else if (arguments.length === 2) { - /* set value */ - _cs.store_load(this); - key = arguments[0]; - val = arguments[1]; - this.__store[key] = val; - _cs.store_save(this); - return val; - } - else - throw _cs.exception("store", "invalid argument(s)"); - } - } - }); - - /* generic pattern for model management */ - $cs.pattern.model = $cs.trait({ - protos: { - /* define model */ - model: function () { - /* determine parameters */ - var params = $cs.params("model", arguments, { - model: { pos: 0, def: null } - }); - - /* simplify further processing */ - var model = params.model; - if (model === null) - model = undefined; - - /* sanity check model */ - var name; - if (_cs.isdefined(model)) { - for (name in model) { - if (typeof model[name].value === "undefined") - model[name].value = ""; - if (typeof model[name].valid === "undefined") - model[name].valid = "string"; - if (typeof model[name].autoreset === "undefined") - model[name].autoreset = false; - if (typeof model[name].store === "undefined") - model[name].store = false; - for (var key in model[name]) { - if (key !== "value" && key !== "valid" && key !== "autoreset" && key !== "store") - throw _cs.exception("model", "invalid specification key \"" + - key + "\" in specification of model field \"" + name + "\""); - } - if (!$cs.validate(model[name].value, model[name].valid)) - throw _cs.exception("model", "model field \"" + name + "\" has " + - "default value " + _cs.json(model[name].value) + ", which does not validate " + - "against validation \"" + model[name].valid + "\""); - } - } - - /* try to load stored model values */ - var store = this.store("model"); - if (store !== null) { - if (_cs.isdefined(model)) { - for (name in model) { - if (model[name].store) { - if (_cs.isdefined(store[name])) - model[name].value = store[name]; - } - } - } - } - - /* retrieve old model */ - var model_old = this.property({ name: "ComponentJS:model", bubbling: false }); - - /* store model */ - if (_cs.isdefined(model)) { - if (_cs.isdefined(model_old)) { - /* merge model into existing one */ - var model_new = {}; - _cs.extend(model_new, model_old); - _cs.extend(model_new, model); - this.property("ComponentJS:model", model_new); - model = model_new; - } - else { - /* set initial model */ - this.property("ComponentJS:model", model); - } - - /* optionally save stored model values */ - store = {}; - var save = false; - for (name in model) { - if (model[name].store) { - store[name] = model[name].value; - save = true; - } - } - if (save) - this.store("model", store); - } - - /* return old model */ - return model_old; - }, - - /* get/set model value */ - value: function () { - /* determine parameters */ - var params = $cs.params("value", arguments, { - name: { pos: 0, req: true }, - value: { pos: 1, def: undefined }, - force: { pos: 2, def: false }, - returnowner: { def: false } - }); - - /* determine component owning model with requested value */ - var owner = null; - var model = null; - var comp = this; - while (comp !== null) { - owner = comp.property({ name: "ComponentJS:model", returnowner: true }); - if (!_cs.isdefined(owner)) - throw _cs.exception("value", "no model found containing value \"" + params.name + "\""); - model = owner.property("ComponentJS:model"); - if (_cs.isdefined(model[params.name])) - break; - comp = owner.parent(); - } - if (comp === null) - throw _cs.exception("value", "no model found containing value \"" + params.name + "\""); - - /* get new model value */ - var value_new = params.value; - - /* get old model value */ - var ev; - var value_old = model[params.name].value; - var result; - if (typeof value_new === "undefined") { - if (owner.property({ name: "ComponentJS:model:subscribers:get", bubbling: false }) === true) { - /* send event to observers for value get and allow observers - to reject value get operation and/or change old value to get */ - ev = owner.publish({ - name: "ComponentJS:model:" + params.name + ":get", - args: [ value_old ], - capturing: false, - spreading: false, - bubbling: false, - async: false - }); - if (ev.processing()) { - /* re-fetch value from model - (in case the callback set a new value directly) */ - value_old = model[params.name].value; - - /* allow value to be overridden by event result */ - result = ev.result(); - if (typeof result !== "undefined") - value_old = result; - } - } - } - - /* optionally set new model value */ - if ( typeof value_new !== "undefined" && - (params.force || value_old !== value_new)) { - - /* check validity of new value */ - if (!$cs.validate(value_new, model[params.name].valid)) - throw _cs.exception("value", "model field \"" + params.name + "\" receives " + - "new value " + _cs.json(value_new) + ", which does not validate " + - "against validation \"" + model[params.name].valid + "\""); - - /* send event to observers for value set operation and allow observers - to reject value set operation and/or change new value to set */ - var cont = true; - if (owner.property({ name: "ComponentJS:model:subscribers:set", bubbling: false }) === true) { - ev = owner.publish({ - name: "ComponentJS:model:" + params.name + ":set", - args: [ value_new, value_old ], - capturing: false, - spreading: false, - bubbling: false, - async: false - }); - if (!ev.processing()) - cont = false; - else { - /* allow value to be overridden */ - result = ev.result(); - if (typeof result !== "undefined") - value_new = result; - } - } - if (cont && !model[params.name].autoreset) { - /* set value in model */ - model[params.name].value = value_new; - - /* synchronize model with underlying store */ - if (model[params.name].store) { - var store = owner.store("model"); - store[params.name] = model[params.name].value; - owner.store("model", store); - } - - /* send event to observers after value finally changed */ - if (owner.property({ name: "ComponentJS:model:subscribers:changed", bubbling: false }) === true) { - owner.publish({ - name: "ComponentJS:model:" + params.name + ":changed", - args: [ value_new, value_old ], - noresult: true, - capturing: false, - spreading: false, - bubbling: false, - async: true - }); - } - } - } - - /* return old model value */ - return (params.returnowner ? owner : value_old); - }, - - /* touch a model value and trigger event */ - touch: function () { - /* determine parameters */ - var params = $cs.params("touch", arguments, { - name: { pos: 0, req: true } - }); - - /* simply force value to same value in order to trigger event */ - this.value({ - name: params.name, - value: this.value(params.name), - force: true - }); - }, - - /* start observing model value change */ - observe: function () { - /* determine parameters */ - var params = $cs.params("observe", arguments, { - name: { pos: 0, req: true }, - func: { pos: 1, req: true }, - touch: { def: false }, - operation: { def: "set" }, - spool: { def: null } - }); - - /* determine the actual component owning the model - as we want to subscribe the change event there only */ - var owner = null; - var model = null; - var comp = this; - while (comp !== null) { - owner = comp.property({ name: "ComponentJS:model", returnowner: true }); - if (!_cs.isdefined(owner)) - throw _cs.exception("observe", "no model found containing value \"" + params.name + "\""); - model = owner.property("ComponentJS:model"); - if (_cs.isdefined(model[params.name])) - break; - comp = owner.parent(); - } - if (comp === null) - throw _cs.exception("observe", "no model found containing value \"" + params.name + "\""); - - /* subscribe to model value change event */ - var id = owner.subscribe({ - name: "ComponentJS:model:" + params.name + ":" + params.operation, - capturing: false, - spreading: false, - bubbling: false, - func: params.func - }); - - /* mark component for having subscribers of operation - (for performance optimization reasons) */ - owner.property("ComponentJS:model:subscribers:" + params.operation, true); - - /* optionally spool reverse operation */ - if (params.spool !== null) { - var info = _cs.spool_spec_parse(this, params.spool); - info.comp.spool(info.name, this, "unobserve", id); - } - - /* if requested, touch the model value once (for an initial observer run) */ - if (params.touch) - this.touch(params.name); - - return id; - }, - - /* stop observing model value change */ - unobserve: function () { - /* determine parameters */ - var params = $cs.params("unobserve", arguments, { - id: { pos: 0, req: true } - }); - - /* determine the actual component owning the model - as we want to unsubscribe the change event there only */ - var owner = null; - var comp = this; - while (comp !== null) { - owner = comp.property({ name: "ComponentJS:model", returnowner: true }); - if (!_cs.isdefined(owner)) - throw _cs.exception("unobserve", "no model subscription found"); - if (owner.subscription(params.id)) - break; - comp = owner.parent(); - } - if (comp === null) - throw _cs.exception("unobserve", "no model subscription found"); - - /* subscribe to model value change event */ - owner.unsubscribe(params.id); - } - } - }); - - - /* - ** COMPONENT API - */ - - /* component class definition (placeholder) */ - _cs.comp = null; - - /* singleton component instances (placeholder) */ - _cs.none = null; - _cs.root = null; - - /* component mixins (default) */ - _cs.comp_mixins = [ - $cs.pattern.id, - $cs.pattern.name, - $cs.pattern.tree, - $cs.pattern.config, - $cs.pattern.spool, - $cs.pattern.state, - $cs.pattern.service, - $cs.pattern.eventing, - $cs.pattern.property, - $cs.pattern.shadow, - $cs.pattern.socket, - $cs.pattern.model, - $cs.pattern.store, - $cs.pattern.marker - ]; - - /* component constructor */ - _cs.comp_cons = function (name, parent, children) { - /* component marking */ - _cs.annotation(this, "type", "component"); - if (_cs.istypeof(name) !== "string") - name = ""; - this.name(name); - - /* component tree and object attachment */ - this.parent(_cs.istypeof(parent) === "object" ? parent : null); - this.children(_cs.istypeof(children) === "array" ? children : []); - }; - - /* component prototype methods */ - _cs.comp_protos = { - /* create a sub-component */ - create: function () { - return $cs.create.apply(this, _cs.concat([ this ], arguments)); - }, - - /* destroy sub-component (or just this component) */ - destroy: function () { - return $cs.destroy.apply(this, _cs.concat([ this ], arguments)); - }, - - /* check for existance of a component */ - exists: function () { - return (this.name() !== ""); - } - }; - - /* internal bootstrapping flag */ - _cs.bootstrapped = false; - - /* initialize library */ - $cs.bootstrap = function () { - /* sanity check environment */ - if (_cs.bootstrapped) - throw _cs.exception("bootstrap", "library already bootstrapped"); - - /* give plugins a chance to modify the component class definition */ - _cs.hook("ComponentJS:bootstrap:comp:mixin", "none", _cs.comp_mixins); - _cs.hook("ComponentJS:bootstrap:comp:protos", "none", _cs.comp_protos); - - /* lazy define component class - (to give plugins a chance to have added mixins) */ - _cs.comp = $cs.clazz({ - mixin: _cs.comp_mixins, - cons: _cs.comp_cons, - protos: _cs.comp_protos - }); - - /* create singleton component: root of the tree */ - _cs.root = new _cs.comp("", null, []); - - /* create singleton component: special return value on lookups */ - _cs.none = new _cs.comp("", null, []); - - /* reasonable error catching for _cs.none usage - ATTENTION: method "exists" intentionally is missing here, - because it is required to be called on _cs.none, of course! */ - var methods = [ - "call", "callable", "create", "destroy", "guard", "hook", "invoke", - "latch", "link", "model", "observe", "plug", "property", - "publish", "register", "registration", "socket", "spool", - "spooled", "state", "state_compare", "store", "subscribe", - "subscription", "touch", "unlatch", "unobserve", "unplug", - "unregister", "unspool", "unsubscribe", "value" - ]; - _cs.foreach(methods, function (method) { - _cs.none[method] = function () { - throw _cs.exception(method, "no such component " + - "(you are calling method \"" + method + "\" on component \"\")"); - }; - }); - - /* give plugins a chance to bootstrap, too */ - _cs.hook("ComponentJS:bootstrap", "none"); - - /* set new state */ - _cs.bootstrapped = true; - - return; - }; - - /* shutdown library */ - $cs.shutdown = function () { - /* sanity check environment */ - if (!_cs.bootstrapped) - throw _cs.exception("shutdown", "library still not bootstrapped"); - - /* give plugins a chance to shutdown, too */ - _cs.hook("ComponentJS:shutdown", "none"); - - /* tear down the whole component tree */ - _cs.foreach(_cs.root.children(), function (child) { - child.destroy(); - }); - _cs.root.state({ state: "dead", sync: true }); - - /* destroy singleton "" component */ - _cs.none = null; - - /* destroy singleton "" component */ - _cs.root = null; - - /* destroy component class */ - _cs.comp = null; - - /* set new state */ - _cs.bootstrapped = false; - - return; - }; - - /* lookup component by path */ - _cs.lookup = function (base, path) { - /* handle special calling conventions */ - if (arguments.length === 1) { - if (_cs.istypeof(arguments[0]) === "string") { - /* special calling via path only: $cs("foo") -> $cs(_cs.root, "foo") */ - path = base; - base = _cs.root; - } - else - /* special calling via base only: $cs(this) -> $cs(this, "") */ - path = ""; - } - - /* handle special cases for path in advance */ - if (typeof path !== "string") - return _cs.none; - else if (path === "") - return _cs.root; - else if (path === "") - return _cs.none; - - /* bootstrap component matching */ - var comp; - if (path.substr(0, 1) === "/") { - /* ignore base */ - comp = _cs.root; - path = path.substring(1); - } - else { - /* use base */ - var base_type = _cs.istypeof(base); - var base_comp = _cs.annotation(base, "comp"); - if (base_type !== "component" && base_comp !== null) - /* success: found component object via shadow object */ - comp = base_comp; - else if (base_type !== "component") - /* failure: found other object which is not already component */ - throw _cs.exception("lookup", "invalid base component (type is \"" + base_type + "\")"); - else - /* success: found component object */ - comp = base; - } - - if (path !== "") { - /* lookup components */ - var comps = []; - _cs.lookup_step(comps, comp, path.split("/"), 0); - - /* post-process component result set */ - if (comps.length === 0) - /* no component found */ - comp = _cs.none; - else if (comps.length === 1) - /* single and hence unambitous component found */ - comp = comps[0]; - else { - /* more than one result found: try to reduce duplicates first */ - var seen = {}; - comps = _cs.filter(comps, function (comp) { - var id = comp.id(); - var take = (typeof seen[id] === "undefined"); - seen[id] = true; - return take; - }); - if (comps.length === 1) - /* after de-duplication now only a single component found */ - comp = comps[0]; - else { - /* error: still more than one component found */ - var components = ""; - for (var i = 0; i < comps.length; i++) - components += " " + comps[i].path("/"); - throw _cs.exception("lookup", - "ambiguous component path \"" + path + "\" at " + comp.path("/") + ": " + - "expected only 1 component, but found " + comps.length + " components:" + - components - ); - } - } - } - - /* return component */ - return comp; - }; - - /* lookup component(s) at "comp", reachable via path segment "path[i]" */ - _cs.lookup_step = function (result, comp, path, i) { - var j, children, nodes; - if (i >= path.length) - /* stop recursion */ - result.push(comp); - else if (path[i] === ".") - /* CASE 1: current component (= no-op) */ - _cs.lookup_step(result, comp, path, i + 1); /* RECURSION */ - else if (path[i] === "..") { - /* CASE 2: parent component */ - if (comp.parent() !== null) - _cs.lookup_step(result, comp.parent(), path, i + 1); /* RECURSION */ - } - else if (path[i] === "*") { - /* CASE 3: all child components */ - children = comp.children(); - for (j = 0; j < children.length; j++) - _cs.lookup_step(result, children[j], path, i + 1); /* RECURSION */ - } - else if (path[i] === "") { - /* CASE 4: all descendent components */ - nodes = comp.walk_down(function (depth, node, nodes, depth_first) { - if (!depth_first) - nodes.push(node); - return nodes; - }, []); - for (j = 0; j < nodes.length; j++) - _cs.lookup_step(result, nodes[j], path, i + 1); /* RECURSION */ - } - else { - /* CASE 5: a specific child component */ - children = comp.children(); - for (j = 0; j < children.length; j++) { - if (children[j].name() === path[i]) { - _cs.lookup_step(result, children[j], path, i + 1); /* RECURSION */ - break; - } - } - } - }; - - /* top-level API: create one or more components */ - $cs.create = function () { - /* sanity check environment */ - if (!_cs.bootstrapped) { - /* give warning but still be backward compatible */ - var msg = "ComponentJS: WARNING: component system still not bootstrapped " + - "(please call \"bootstrap\" method before first \"create\" method call!)"; - /* global alert:false */ - if (typeof alert === "function") - alert(msg); - /* global console:false */ - else if (typeof console !== "undefined" && typeof console.log === "function") - console.log(msg); - $cs.bootstrap(); - } - - /* sanity check arguments */ - if (arguments.length < 2) - throw _cs.exception("create", "invalid number of arguments"); - - /* initialize processing state */ - var k = 0; - var comp = null; - var base = null; - var base_stack = []; - - /* determine base component */ - if (_cs.istypeof(arguments[k]) === "string") { - if (arguments[k].substr(0, 1) !== "/") - throw _cs.exception("create", "either base component has to be given " + - "or the tree specification has to start with the root component (\"/\")"); - comp = _cs.root; - } - else { - base = arguments[k++]; - if (_cs.istypeof(base) !== "component") { - base = _cs.annotation(base, "comp"); - if (base === null) - throw _cs.exception("create", "invalid base argument " + - "(not an object attached to a component)"); - } - } - - /* tokenize the tree specification */ - var token = []; - var spec = arguments[k++]; - var m; - while (spec !== "") { - m = spec.match(/^\s*([^\/{},]+|[\/{},])/); - if (m === null) - break; - token.push(m[1]); - spec = spec.substr(m[1].length); - } - - /* return the tree specification, marked at token k */ - var at_pos = function (token, k) { - var str = ""; - for (var i = 0; i < k && i < token.length; i++) - str += token[i]; - if (i < token.length) { - str += "<"; - str += token[i++]; - str += ">"; - for (; i < token.length; i++) - str += token[i]; - } - return str; - }; - - /* iterate over all tokens... */ - for (var i = 0; i < token.length; i++) { - if (token[i] === "/") { - /* switch base */ - if (comp === null) - throw _cs.exception("create", "no parent component for step-down at " + at_pos(token, i)); - base = comp; - } - else if (token[i] === "{") { - /* save base */ - base_stack.push(base); - } - else if (token[i] === ",") { - /* reset base */ - if (base_stack.length === 0) - throw _cs.exception("create", "no open brace section for parallelism at " + at_pos(token, i)); - base = base_stack[base_stack.length - 1]; - } - else if (token[i] === "}") { - /* restore base */ - if (base_stack.length === 0) - throw _cs.exception("create", "no more open brace section for closing at " + at_pos(token, i)); - base = base_stack.pop(); - comp = null; - } - else { - /* create new component */ - if (base === null) - throw _cs.exception("create", "no base component at " + at_pos(token, i)); - comp = _cs.create_single(base, token[i], arguments[k++]); - } - } - if (base_stack.length > 0) - throw _cs.exception("create", "still open brace sections at end of tree specification"); - - /* return (last created) component */ - return comp; - }; - - /* internal: create a single component */ - _cs.create_single = function (base, path, clazz) { - /* sanity check parameters */ - if (typeof path !== "string") - throw _cs.exception("create", "invalid path argument (not a string)"); - - /* split path into existing tree and the not existing component leaf node */ - var m = path.match(/^(.*?)\/?([^\/]+)$/); - if (!m[0]) - throw _cs.exception("create", "invalid path \"" + path + "\""); - var path_tree = m[1]; - var path_leaf = m[2]; - - /* create new component id */ - var id = _cs.cid(); - - /* substitute special "{id}" constructs in leaf path */ - path_leaf = path_leaf.replace(/\{id\}/g, id); - - /* lookup parent component (has to be existing) */ - var comp_parent = _cs.lookup(base, path_tree); - if (comp_parent === _cs.none) - throw _cs.exception("create", "parent component path \"" + - path_tree + "\" not already existing (please create first)"); - - /* attempt to lookup leaf component (has to be not existing) */ - var comp = _cs.lookup(comp_parent, path_leaf); - if (comp !== _cs.none) - throw _cs.exception("create", "leaf component path \"" + - path_leaf + "\" already existing (please destroy first)"); - - /* instanciate class */ - var obj = null; - switch (_cs.istypeof(clazz)) { - case "clazz": - case "trait": - case "function": - /* standard case: $cs.create(..., MyClass) - ComponentJS clazz/trait or foreign "class" */ - obj = new clazz(); - break; - case "object": - /* special case: $cs.create(..., new MyClass(arg1, arg2)) - manual instanciation because of parameter passing */ - obj = clazz; - break; - case "null": - /* special case: $cs.create(..., null) - early component create & late object attachment */ - break; - default: - throw _cs.exception("create", "invalid class argument"); - } - - /* create new corresponding component object in tree */ - comp = new _cs.comp(path_leaf); - - /* mark with component id */ - comp.id(id); - - /* attach to tree */ - comp.attach(comp_parent); - - /* remember bi-directional relationship between component and object */ - comp.obj(obj); - - /* debug hint */ - $cs.debug(1, "component: " + comp.path("/") + ": created component [" + comp.id() + "]"); - - /* give plugins a chance to react (before creation of a component) */ - _cs.hook("ComponentJS:comp-created", "none", comp); - - /* switch state from "dead" to "created" - (here synchronously as one expects that after a creation of a - component, the state is really already "created", of course) */ - comp.state({ state: "created", sync: true }); - - /* give plugins a chance to react (after creation of a component) */ - _cs.hook("ComponentJS:state-invalidate", "none", "components"); - _cs.hook("ComponentJS:state-change", "none"); - - /* return new component */ - return comp; - }; - - /* top-level API: destroy a component */ - $cs.destroy = function () { - /* sanity check arguments */ - if (arguments.length !== 1 && arguments.length !== 2) - throw _cs.exception("destroy", "invalid number of arguments"); - - /* determine component */ - var comp = _cs.lookup.apply(this, arguments); - if (comp === _cs.none) - throw _cs.exception("destroy", "no such component found to destroy"); - else if (comp === _cs.root) - throw _cs.exception("destroy", "root component cannot be destroyed"); - var path = comp.path("/"); - var id = comp.id(); - - /* switch component state to "dead" - (here synchronously as one expects that after a destruction of a - component, the state is really already "dead", of course) */ - comp.state({ state: "dead", sync: true }); - - /* give plugins a chance to react (before final destruction of a component) */ - _cs.hook("ComponentJS:comp-destroyed", "none", comp); - - /* detach component from component tree */ - comp.detach(); - - /* remove bi-directional relationship between component and object */ - comp.obj(null); - - /* debug hint */ - $cs.debug(1, "component: " + path + ": destroyed component [" + id + "]"); - - /* give plugins a chance to react (after final destruction of a component) */ - _cs.hook("ComponentJS:state-invalidate", "none", "components"); - _cs.hook("ComponentJS:state-change", "none"); - - return; - }; - - /* define a state transition */ - $cs.transition = function () { - /* special case */ - if (arguments.length === 1 && arguments[0] === null) { - /* remove all user-defined transitions */ - _cs.states_clear(); - return; - } - - /* determine parameters */ - var params = $cs.params("transition", arguments, { - target: { pos: 0, req: true }, - enter: { pos: 1, req: true }, - leave: { pos: 2, req: true }, - color: { pos: 3, def: "#000000" }, - source: { def: null } - }); - - /* add new state */ - _cs.states_add( - params.target, - params.enter, - params.leave, - params.color, - params.source - ); - }; - - /* initialize state transition set with a reasonable default */ - $cs.transition("created", "create", "destroy", "#cc3333"); /* created and attached to component tree */ - $cs.transition("configured", "setup", "teardown", "#eabc43"); /* configured and wired */ - $cs.transition("prepared", "prepare", "cleanup", "#f2ec00"); /* prepared and ready for rendering */ - $cs.transition("materialized", "render", "release", "#6699cc"); /* rendered onto the DOM tree */ - $cs.transition("visible", "show", "hide", "#669933"); /* visible to the user */ - $cs.transition("enabled", "enable", "disable", "#336600"); /* enabled for interaction */ - - - /* - ** GLOBAL LIBRARY EXPORTING - */ - - /* export our global API... */ - if ( ( typeof EXPORTS === "object" && - typeof GLOBAL.ComponentJS_export === "undefined") || - ( typeof GLOBAL.ComponentJS_export !== "undefined" && - GLOBAL.ComponentJS_export === "CommonJS" )) - /* ...to scoped CommonJS environment */ - EXPORTS.ComponentJS = $cs; - else if ( ( typeof DEFINE === "function" && - typeof DEFINE.amd === "object" && - typeof GLOBAL.ComponentJS_export === "undefined") || - ( typeof GLOBAL.ComponentJS_export !== "undefined" && - GLOBAL.ComponentJS_export === "AMD" )) - /* ...to scoped AMD environment */ - DEFINE("ComponentJS", function () { - return $cs; - }); - else { - /* ...to regular global environment */ - $cs.symbol("ComponentJS"); - } - - /* internal plugin registry */ - _cs.plugins = {}; - - /* external plugin API */ - $cs.plugin = function (name, callback) { - if (arguments.length === 0) { - /* use case 1: return list of registered plugins */ - var plugins = []; - for (name in _cs.plugins) { - if (!_cs.isown(_cs.plugins, name)) - continue; - plugins.push(name); - } - return plugins; - } - else if (arguments.length === 1) { - /* use case 2: check whether particular plugin was registered */ - if (typeof name !== "string") - throw _cs.exception("plugin", "invalid plugin name parameter"); - return (typeof _cs.plugins[name] !== "undefined"); - } - else if (arguments.length === 2) { - /* use case 3: register a new plugin */ - if (typeof name !== "string") - throw _cs.exception("plugin", "invalid plugin name parameter"); - if (typeof _cs.plugins[name] !== "undefined") - throw _cs.exception("plugin", "plugin named \"" + name + "\" already registered"); - callback.call(this, _cs, $cs, GLOBAL); - _cs.plugins[name] = true; - } - else - throw _cs.exception("plugin", "invalid number of parameters"); - }; - - -})( - /* global window:false */ - /* global global:false */ - /* global exports:false */ - /* global define:false */ - ( typeof window !== "undefined" ? - window : - ( typeof global !== "undefined" ? - global : - ( typeof this !== "undefined" ? - this : - {} ))), - ( typeof exports === "object" ? - exports : - undefined ), - ( typeof define === "function" ? - define : - undefined ) -); diff --git a/examples/componentjs/bower_components/componentjs/component.plugin.debugger.js b/examples/componentjs/bower_components/componentjs/component.plugin.debugger.js deleted file mode 100644 index 6ceeb6f070..0000000000 --- a/examples/componentjs/bower_components/componentjs/component.plugin.debugger.js +++ /dev/null @@ -1,1234 +0,0 @@ -/* -** ComponentJS -- Component System for JavaScript -** Copyright (c) 2009-2013 Ralf S. Engelschall -** -** 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 = "
" + html + "
"; - - /* remove all nodes from target node */ - while (this[i].firstChild) - this[i].removeChild(this[i].firstChild); - - /* add all nodes in our
...
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 + "" - ); - _cs.jq("html body", _cs.dbg.document).html( - "" + - "
" + - "
" + title + "
" + - "
" + - "
" + - "
+
" + - "
0
" + - "
-
" + - "
Export
" + - "
" + - "
" + - "
" + - "
" - ); - }; - - /* 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("&", "&") - .replace("<", "<") - .replace(">", ">") - .replace("\n", "
"); - _cs.dbg_logline++; - msg = msg.replace(/(DEBUG\[\d+\]: )([^:]+)/, - "$1" + - "$2" - ); - msg = msg.replace(/(\s+)(\/[^:\s]*)/g, "$1$2"); - msg = msg.replace(/(\s+)(@[a-z]+)/g, "$1$2"); - msg = msg.replace(/((?:<)?--\()([a-z]+)(\)--(?:>)?)/g, - "$1" + - "$2" + - "$3" - ); - _cs.dbg_logbook = - "" + - "" + - "" + - "" + - "" + - "
" + _cs.dbg_logline + "." + msg + "
" + _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(//, ">"); - html += "" + - "Name:" + - "" + name + "" + - ""; - html += "" + - "Path:" + - "" + comp.path("/") + "" + - ""; - - /* 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 = "none"; - html += "" + - "Markers:" + - "" + markers + "" + - ""; - - /* state and guards */ - html += "" + - "State:" + - "" + comp.state() + "" + - ""; - 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 += "" + method + " (" + comp.__state_guards[method] + "), "; - guards = guards.replace(/, $/, ""); - if (guards === "") - guards = "none"; - html += "" + - "Guards:" + - "" + guards + "" + - ""; - - /* 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 += "" + name + " (" + comp.__spool[name].length + "), "; - spools = spools.replace(/, $/, ""); - if (spools === "") - spools = "none"; - html += "" + - "Spools:" + - "" + spools + "" + - ""; - - /* 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 += "" + name + ", "; - values = values.replace(/, $/, ""); - if (values === "") - values = "none"; - html += "" + - "Model Values:" + - "" + values + "" + - ""; - - /* 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 += "" + id - .replace(/^ComponentJS:property:ComponentJS:socket:/, "") + ", "; - sockets = sockets.replace(/, $/, ""); - if (sockets === "") - sockets = "none"; - html += "" + - "Sockets:" + - "" + sockets + "" + - ""; - - /* 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 += "" + comp.__subscription[id].name + ", "; - subscriptions = subscriptions.replace(/, $/, ""); - if (subscriptions === "") - subscriptions = "none"; - html += "" + - "Event Subscriptions:" + - "" + subscriptions + "" + - ""; - - /* 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 += "" + comp.__subscription[id].name - .replace(/^ComponentJS:service:/, "") + ", "; - registrations = registrations.replace(/, $/, ""); - if (registrations === "") - registrations = "none"; - html += "" + - "Service Registrations:" + - "" + registrations + "" + - ""; - - /* 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 += "" + comp.__subscription[id].name - .replace(/^ComponentJS:hook:/, "") + ", "; - hooks = hooks.replace(/, $/, ""); - if (hooks === "") - hooks = "none"; - html += "" + - "Hook Points:" + - "" + hooks + "" + - ""; - - /* finish and return table */ - html = "" + html + "
"; - 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: " + T + ", " + - "Pending Transition Requests: " + reqs + "" - ); - - _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); - }); - -}); diff --git a/examples/componentjs/bower_components/componentjs/component.plugin.extjs.js b/examples/componentjs/bower_components/componentjs/component.plugin.extjs.js deleted file mode 100644 index d55dc91d74..0000000000 --- a/examples/componentjs/bower_components/componentjs/component.plugin.extjs.js +++ /dev/null @@ -1,60 +0,0 @@ -/* -** ComponentJS -- Component System for JavaScript -** Copyright (c) 2009-2013 Ralf S. Engelschall -** -** 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); - }); -}); diff --git a/examples/componentjs/bower_components/componentjs/component.plugin.jquery.js b/examples/componentjs/bower_components/componentjs/component.plugin.jquery.js deleted file mode 100644 index 1deaee192f..0000000000 --- a/examples/componentjs/bower_components/componentjs/component.plugin.jquery.js +++ /dev/null @@ -1,66 +0,0 @@ -/* -** ComponentJS -- Component System for JavaScript -** Copyright (c) 2009-2013 Ralf S. Engelschall -** -** 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); - }); -}); diff --git a/examples/componentjs/bower_components/director/build/director.js b/examples/componentjs/bower_components/director/build/director.js deleted file mode 100644 index 0befbe0751..0000000000 --- a/examples/componentjs/bower_components/director/build/director.js +++ /dev/null @@ -1,712 +0,0 @@ - - -// -// 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(" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/componentjs/readme.md b/examples/componentjs/readme.md deleted file mode 100644 index 9b1d2aa769..0000000000 --- a/examples/componentjs/readme.md +++ /dev/null @@ -1,124 +0,0 @@ - -# 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 — 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
- The MVC framework. -- [jQuery](http://jquery.com/) 2.0.3
- The DOM manipulation and eventing library. -- [jQuery-Markup](http://plugins.jquery.com/markup/) 1.0.26
- The view mask template integration library. -- [Nunjucks](http://jlongster.github.io/nunjucks/) 1.0.0
- The view mask template engine library. -- [Flatiron Director](https://github.com/flatiron/director) 1.2.0
- The URL routing library. -- [UUID.js](https://github.com/aurigadl/uuid-js) 0.7.5
- The UUID generation library. -- [Lo-Dash](http://lodash.com/) 2.3.0
- The collection utility library. -- [todomvc-common](https://github.com/tastejs/todomvc-common) 0.1.9
- 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) diff --git a/index.html b/index.html index 09501b375a..6f914297a8 100644 --- a/index.html +++ b/index.html @@ -266,9 +266,6 @@

Examples

  • Ractive.js
  • -
  • - ComponentJS -
  • React + Alt
  • diff --git a/learn.json b/learn.json index 5edc1a9353..fadc026187 100644 --- a/learn.json +++ b/learn.json @@ -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.", diff --git a/tests/knownIssues.js b/tests/knownIssues.js index a5926dc28f..f8af614538 100644 --- a/tests/knownIssues.js +++ b/tests/knownIssues.js @@ -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', From a8183d1eb47556ee6615f9cbd055ad83d92aaeac Mon Sep 17 00:00:00 2001 From: Arthur Verschaeve Date: Sun, 6 Nov 2016 17:39:03 +0100 Subject: [PATCH 08/26] Remove a spine item from `knownIssues.js` This was fixed in 1cf5060a48d1c105398734784017b82ca1ab3324 --- tests/knownIssues.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/knownIssues.js b/tests/knownIssues.js index f8af614538..934c85b453 100644 --- a/tests/knownIssues.js +++ b/tests/knownIssues.js @@ -67,10 +67,6 @@ module.exports = [ 'TodoMVC - lavaca_require, Routing, should respect the back button', 'TodoMVC - somajs_require, Routing, should respect the back button', - // the following are covered by this issue: - // https://github.com/tastejs/todomvc/issues/795 - 'TodoMVC - spine, Mark all as completed, complete all checkbox should update state when items are completed / cleared', - // the following implementations do not support routing 'TodoMVC - extjs_deftjs, Routing, should allow me to display active items', 'TodoMVC - extjs_deftjs, Routing, should allow me to display completed items', From 4fcd6b4f1d33763751e9bd5533f3bdc5ea96631e Mon Sep 17 00:00:00 2001 From: Arthur Verschaeve Date: Sun, 6 Nov 2016 17:42:36 +0100 Subject: [PATCH 09/26] Remove some `typescript-backbone` items from `knownIssues.js` These issues were fixed in #830 --- tests/knownIssues.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/knownIssues.js b/tests/knownIssues.js index 934c85b453..a2dce7606b 100644 --- a/tests/knownIssues.js +++ b/tests/knownIssues.js @@ -7,13 +7,6 @@ module.exports = [ 'TodoMVC - durandal, Routing, should allow me to display all items', 'TodoMVC - durandal, Routing, should highlight the currently applied filter', - // see: https://github.com/tastejs/todomvc/issues/830 - // typescript-backbone has a nested div within the li element, and sets /li/div[@class=done] - 'TodoMVC - typescript-backbone, Mark all as completed, should allow me to mark all items as completed / cleared', - 'TodoMVC - typescript-backbone, Item, should allow me to mark items as complete', - 'TodoMVC - typescript-backbone, Item, should allow me to un-mark items as complete', - 'TodoMVC - typescript-backbone, Item, should allow me to edit an item', - // https://github.com/tastejs/todomvc/issues/828 // routing should default to all 'TodoMVC - sammyjs, Routing, should highlight the currently applied filter', From 975e46531e6ddbc466f10e993cf8e09eedded344 Mon Sep 17 00:00:00 2001 From: Arthur Verschaeve Date: Tue, 15 Nov 2016 19:39:24 +0100 Subject: [PATCH 10/26] Drop `thorax` and `thorax_lumbar` example Close #1715 --- .jscsrc | 2 - examples/thorax/bower.json | 8 - .../bower_components/backbone/backbone.js | 1533 -- .../handlebars.js/handlebars.js | 2239 -- .../thorax/bower_components/jquery/jquery.js | 8755 -------- .../thorax/bower_components/thorax/thorax.js | 2669 --- .../bower_components/todomvc-common/base.css | 554 - .../bower_components/todomvc-common/base.js | 217 - .../bower_components/todomvc-common/bg.png | Bin 2126 -> 0 bytes .../bower_components/underscore/underscore.js | 1226 -- examples/thorax/index.html | 79 - examples/thorax/js/app.js | 14 - examples/thorax/js/collections/todos.js | 44 - .../thorax/js/lib/backbone-localstorage.js | 130 - examples/thorax/js/models/todo.js | 38 - examples/thorax/js/routers/router.js | 23 - examples/thorax/js/views/app.js | 69 - examples/thorax/js/views/stats.js | 55 - examples/thorax/js/views/todo-item.js | 80 - examples/thorax/readme.md | 41 - examples/thorax_lumbar/bower.json | 11 - .../backbone.localStorage.js | 258 - .../bower_components/backbone/backbone.js | 1533 -- .../handlebars.js/handlebars.js | 2239 -- .../bower_components/jquery/jquery.js | 8755 -------- .../lumbar-loader/lumbar-loader-backbone.js | 50 - .../lumbar-loader/lumbar-loader-events.js | 24 - .../lumbar-loader-localstorage.js | 57 - .../lumbar-loader/lumbar-loader-standard.js | 34 - .../lumbar-loader/lumbar-loader.js | 125 - .../bower_components/script.js/dist/script.js | 118 - .../bower_components/thorax/thorax.js | 2669 --- .../bower_components/todomvc-common/base.css | 554 - .../bower_components/todomvc-common/base.js | 217 - .../bower_components/todomvc-common/bg.png | Bin 2126 -> 0 bytes .../bower_components/underscore/underscore.js | 1226 -- examples/thorax_lumbar/lumbar.json | 87 - examples/thorax_lumbar/package.json | 10 - examples/thorax_lumbar/public/base.js | 17301 ---------------- examples/thorax_lumbar/public/index.html | 11 - examples/thorax_lumbar/public/todomvc.js | 355 - examples/thorax_lumbar/readme.md | 35 - examples/thorax_lumbar/src/js/app.js | 13 - .../thorax_lumbar/src/js/collections/todos.js | 44 - examples/thorax_lumbar/src/js/init.js | 12 - examples/thorax_lumbar/src/js/models/todo.js | 38 - .../thorax_lumbar/src/js/routers/todomvc.js | 24 - examples/thorax_lumbar/src/js/views/app.js | 70 - examples/thorax_lumbar/src/js/views/stats.js | 55 - .../thorax_lumbar/src/js/views/todo-item.js | 81 - .../src/templates/app.handlebars | 26 - .../src/templates/stats.handlebars | 15 - index.html | 6 - learn.json | 37 - tests/knownIssues.js | 2 - 55 files changed, 53868 deletions(-) delete mode 100644 examples/thorax/bower.json delete mode 100644 examples/thorax/bower_components/backbone/backbone.js delete mode 100644 examples/thorax/bower_components/handlebars.js/handlebars.js delete mode 100644 examples/thorax/bower_components/jquery/jquery.js delete mode 100644 examples/thorax/bower_components/thorax/thorax.js delete mode 100644 examples/thorax/bower_components/todomvc-common/base.css delete mode 100644 examples/thorax/bower_components/todomvc-common/base.js delete mode 100644 examples/thorax/bower_components/todomvc-common/bg.png delete mode 100644 examples/thorax/bower_components/underscore/underscore.js delete mode 100644 examples/thorax/index.html delete mode 100644 examples/thorax/js/app.js delete mode 100644 examples/thorax/js/collections/todos.js delete mode 100644 examples/thorax/js/lib/backbone-localstorage.js delete mode 100644 examples/thorax/js/models/todo.js delete mode 100644 examples/thorax/js/routers/router.js delete mode 100644 examples/thorax/js/views/app.js delete mode 100644 examples/thorax/js/views/stats.js delete mode 100644 examples/thorax/js/views/todo-item.js delete mode 100644 examples/thorax/readme.md delete mode 100644 examples/thorax_lumbar/bower.json delete mode 100644 examples/thorax_lumbar/bower_components/Backbone.localStorage/backbone.localStorage.js delete mode 100644 examples/thorax_lumbar/bower_components/backbone/backbone.js delete mode 100644 examples/thorax_lumbar/bower_components/handlebars.js/handlebars.js delete mode 100644 examples/thorax_lumbar/bower_components/jquery/jquery.js delete mode 100644 examples/thorax_lumbar/bower_components/lumbar-loader/lumbar-loader-backbone.js delete mode 100644 examples/thorax_lumbar/bower_components/lumbar-loader/lumbar-loader-events.js delete mode 100644 examples/thorax_lumbar/bower_components/lumbar-loader/lumbar-loader-localstorage.js delete mode 100644 examples/thorax_lumbar/bower_components/lumbar-loader/lumbar-loader-standard.js delete mode 100644 examples/thorax_lumbar/bower_components/lumbar-loader/lumbar-loader.js delete mode 100644 examples/thorax_lumbar/bower_components/script.js/dist/script.js delete mode 100644 examples/thorax_lumbar/bower_components/thorax/thorax.js delete mode 100644 examples/thorax_lumbar/bower_components/todomvc-common/base.css delete mode 100644 examples/thorax_lumbar/bower_components/todomvc-common/base.js delete mode 100644 examples/thorax_lumbar/bower_components/todomvc-common/bg.png delete mode 100644 examples/thorax_lumbar/bower_components/underscore/underscore.js delete mode 100644 examples/thorax_lumbar/lumbar.json delete mode 100644 examples/thorax_lumbar/package.json delete mode 100644 examples/thorax_lumbar/public/base.js delete mode 100644 examples/thorax_lumbar/public/index.html delete mode 100644 examples/thorax_lumbar/public/todomvc.js delete mode 100644 examples/thorax_lumbar/readme.md delete mode 100644 examples/thorax_lumbar/src/js/app.js delete mode 100644 examples/thorax_lumbar/src/js/collections/todos.js delete mode 100644 examples/thorax_lumbar/src/js/init.js delete mode 100644 examples/thorax_lumbar/src/js/models/todo.js delete mode 100644 examples/thorax_lumbar/src/js/routers/todomvc.js delete mode 100644 examples/thorax_lumbar/src/js/views/app.js delete mode 100644 examples/thorax_lumbar/src/js/views/stats.js delete mode 100644 examples/thorax_lumbar/src/js/views/todo-item.js delete mode 100644 examples/thorax_lumbar/src/templates/app.handlebars delete mode 100644 examples/thorax_lumbar/src/templates/stats.handlebars diff --git a/.jscsrc b/.jscsrc index 42b6af28f8..4cdfa7c114 100644 --- a/.jscsrc +++ b/.jscsrc @@ -45,8 +45,6 @@ "examples/olives/olives-todo.js", "examples/polymer/elements/elements.build.js", "examples/spine/js/**", - "examples/thorax/js/lib/backbone-localstorage.js", - "examples/thorax_lumbar/public/*.js", "examples/typescript-*/js/**/*.js", "examples/vanilladart/**/*.js", "examples/vanilla-es6/dist/bundle.js", diff --git a/examples/thorax/bower.json b/examples/thorax/bower.json deleted file mode 100644 index a353650ed3..0000000000 --- a/examples/thorax/bower.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "todomvc-thorax", - "version": "0.0.0", - "dependencies": { - "todomvc-common": "~0.3.0", - "thorax": "~2.0.0" - } -} diff --git a/examples/thorax/bower_components/backbone/backbone.js b/examples/thorax/bower_components/backbone/backbone.js deleted file mode 100644 index 7391faac34..0000000000 --- a/examples/thorax/bower_components/backbone/backbone.js +++ /dev/null @@ -1,1533 +0,0 @@ -// Backbone.js 0.9.9 - -// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. -// Backbone may be freely distributed under the MIT license. -// For all details and documentation: -// http://backbonejs.org - -(function(){ - - // Initial Setup - // ------------- - - // Save a reference to the global object (`window` in the browser, `exports` - // on the server). - var root = this; - - // Save the previous value of the `Backbone` variable, so that it can be - // restored later on, if `noConflict` is used. - var previousBackbone = root.Backbone; - - // Create a local reference to array methods. - var array = []; - var push = array.push; - var slice = array.slice; - var splice = array.splice; - - // The top-level namespace. All public Backbone classes and modules will - // be attached to this. Exported for both CommonJS and the browser. - var Backbone; - if (typeof exports !== 'undefined') { - Backbone = exports; - } else { - Backbone = root.Backbone = {}; - } - - // Current version of the library. Keep in sync with `package.json`. - Backbone.VERSION = '0.9.9'; - - // Require Underscore, if we're on the server, and it's not already present. - var _ = root._; - if (!_ && (typeof require !== 'undefined')) _ = require('underscore'); - - // For Backbone's purposes, jQuery, Zepto, or Ender owns the `$` variable. - Backbone.$ = root.jQuery || root.Zepto || root.ender; - - // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable - // to its previous owner. Returns a reference to this Backbone object. - Backbone.noConflict = function() { - root.Backbone = previousBackbone; - return this; - }; - - // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option - // will fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and - // set a `X-Http-Method-Override` header. - Backbone.emulateHTTP = false; - - // Turn on `emulateJSON` to support legacy servers that can't deal with direct - // `application/json` requests ... will encode the body as - // `application/x-www-form-urlencoded` instead and will send the model in a - // form param named `model`. - Backbone.emulateJSON = false; - - // Backbone.Events - // --------------- - - // Regular expression used to split event strings. - var eventSplitter = /\s+/; - - // Implement fancy features of the Events API such as multiple event - // names `"change blur"` and jQuery-style event maps `{change: action}` - // in terms of the existing API. - var eventsApi = function(obj, action, name, rest) { - if (!name) return true; - if (typeof name === 'object') { - for (var key in name) { - obj[action].apply(obj, [key, name[key]].concat(rest)); - } - } else if (eventSplitter.test(name)) { - var names = name.split(eventSplitter); - for (var i = 0, l = names.length; i < l; i++) { - obj[action].apply(obj, [names[i]].concat(rest)); - } - } else { - return true; - } - }; - - // Optimized internal dispatch function for triggering events. Tries to - // keep the usual cases speedy (most Backbone events have 3 arguments). - var triggerEvents = function(obj, events, args) { - var ev, i = -1, l = events.length; - switch (args.length) { - case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); - return; - case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0]); - return; - case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0], args[1]); - return; - case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0], args[1], args[2]); - return; - default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); - } - }; - - // A module that can be mixed in to *any object* in order to provide it with - // custom events. You may bind with `on` or remove with `off` callback - // functions to an event; `trigger`-ing an event fires all callbacks in - // succession. - // - // var object = {}; - // _.extend(object, Backbone.Events); - // object.on('expand', function(){ alert('expanded'); }); - // object.trigger('expand'); - // - var Events = Backbone.Events = { - - // Bind one or more space separated events, or an events map, - // to a `callback` function. Passing `"all"` will bind the callback to - // all events fired. - on: function(name, callback, context) { - if (!(eventsApi(this, 'on', name, [callback, context]) && callback)) return this; - this._events || (this._events = {}); - var list = this._events[name] || (this._events[name] = []); - list.push({callback: callback, context: context, ctx: context || this}); - return this; - }, - - // Bind events to only be triggered a single time. After the first time - // the callback is invoked, it will be removed. - once: function(name, callback, context) { - if (!(eventsApi(this, 'once', name, [callback, context]) && callback)) return this; - var self = this; - var once = _.once(function() { - self.off(name, once); - callback.apply(this, arguments); - }); - once._callback = callback; - this.on(name, once, context); - return this; - }, - - // Remove one or many callbacks. If `context` is null, removes all - // callbacks with that function. If `callback` is null, removes all - // callbacks for the event. If `events` is null, removes all bound - // callbacks for all events. - off: function(name, callback, context) { - var list, ev, events, names, i, l, j, k; - if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this; - if (!name && !callback && !context) { - this._events = {}; - return this; - } - - names = name ? [name] : _.keys(this._events); - for (i = 0, l = names.length; i < l; i++) { - name = names[i]; - if (list = this._events[name]) { - events = []; - if (callback || context) { - for (j = 0, k = list.length; j < k; j++) { - ev = list[j]; - if ((callback && callback !== (ev.callback._callback || ev.callback)) || - (context && context !== ev.context)) { - events.push(ev); - } - } - } - this._events[name] = events; - } - } - - return this; - }, - - // Trigger one or many events, firing all bound callbacks. Callbacks are - // passed the same arguments as `trigger` is, apart from the event name - // (unless you're listening on `"all"`, which will cause your callback to - // receive the true name of the event as the first argument). - trigger: function(name) { - if (!this._events) return this; - var args = slice.call(arguments, 1); - if (!eventsApi(this, 'trigger', name, args)) return this; - var events = this._events[name]; - var allEvents = this._events.all; - if (events) triggerEvents(this, events, args); - if (allEvents) triggerEvents(this, allEvents, arguments); - return this; - }, - - // An inversion-of-control version of `on`. Tell *this* object to listen to - // an event in another object ... keeping track of what it's listening to. - listenTo: function(object, events, callback) { - var listeners = this._listeners || (this._listeners = {}); - var id = object._listenerId || (object._listenerId = _.uniqueId('l')); - listeners[id] = object; - object.on(events, callback || this, this); - return this; - }, - - // Tell this object to stop listening to either specific events ... or - // to every object it's currently listening to. - stopListening: function(object, events, callback) { - var listeners = this._listeners; - if (!listeners) return; - if (object) { - object.off(events, callback, this); - if (!events && !callback) delete listeners[object._listenerId]; - } else { - for (var id in listeners) { - listeners[id].off(null, null, this); - } - this._listeners = {}; - } - return this; - } - }; - - // Aliases for backwards compatibility. - Events.bind = Events.on; - Events.unbind = Events.off; - - // Allow the `Backbone` object to serve as a global event bus, for folks who - // want global "pubsub" in a convenient place. - _.extend(Backbone, Events); - - // Backbone.Model - // -------------- - - // Create a new model, with defined attributes. A client id (`cid`) - // is automatically generated and assigned for you. - var Model = Backbone.Model = function(attributes, options) { - var defaults; - var attrs = attributes || {}; - this.cid = _.uniqueId('c'); - this.changed = {}; - this.attributes = {}; - this._changes = []; - if (options && options.collection) this.collection = options.collection; - if (options && options.parse) attrs = this.parse(attrs); - if (defaults = _.result(this, 'defaults')) _.defaults(attrs, defaults); - this.set(attrs, {silent: true}); - this._currentAttributes = _.clone(this.attributes); - this._previousAttributes = _.clone(this.attributes); - this.initialize.apply(this, arguments); - }; - - // Attach all inheritable methods to the Model prototype. - _.extend(Model.prototype, Events, { - - // A hash of attributes whose current and previous value differ. - changed: null, - - // The default name for the JSON `id` attribute is `"id"`. MongoDB and - // CouchDB users may want to set this to `"_id"`. - idAttribute: 'id', - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // Return a copy of the model's `attributes` object. - toJSON: function(options) { - return _.clone(this.attributes); - }, - - // Proxy `Backbone.sync` by default. - sync: function() { - return Backbone.sync.apply(this, arguments); - }, - - // Get the value of an attribute. - get: function(attr) { - return this.attributes[attr]; - }, - - // Get the HTML-escaped value of an attribute. - escape: function(attr) { - return _.escape(this.get(attr)); - }, - - // Returns `true` if the attribute contains a value that is not null - // or undefined. - has: function(attr) { - return this.get(attr) != null; - }, - - // Set a hash of model attributes on the object, firing `"change"` unless - // you choose to silence it. - set: function(key, val, options) { - var attr, attrs; - if (key == null) return this; - - // Handle both `"key", value` and `{key: value}` -style arguments. - if (_.isObject(key)) { - attrs = key; - options = val; - } else { - (attrs = {})[key] = val; - } - - // Extract attributes and options. - var silent = options && options.silent; - var unset = options && options.unset; - - // Run validation. - if (!this._validate(attrs, options)) return false; - - // Check for changes of `id`. - if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; - - var now = this.attributes; - - // For each `set` attribute... - for (attr in attrs) { - val = attrs[attr]; - - // Update or delete the current value, and track the change. - unset ? delete now[attr] : now[attr] = val; - this._changes.push(attr, val); - } - - // Signal that the model's state has potentially changed, and we need - // to recompute the actual changes. - this._hasComputed = false; - - // Fire the `"change"` events. - if (!silent) this.change(options); - return this; - }, - - // Remove an attribute from the model, firing `"change"` unless you choose - // to silence it. `unset` is a noop if the attribute doesn't exist. - unset: function(attr, options) { - return this.set(attr, void 0, _.extend({}, options, {unset: true})); - }, - - // Clear all attributes on the model, firing `"change"` unless you choose - // to silence it. - clear: function(options) { - var attrs = {}; - for (var key in this.attributes) attrs[key] = void 0; - return this.set(attrs, _.extend({}, options, {unset: true})); - }, - - // Fetch the model from the server. If the server's representation of the - // model differs from its current attributes, they will be overriden, - // triggering a `"change"` event. - fetch: function(options) { - options = options ? _.clone(options) : {}; - if (options.parse === void 0) options.parse = true; - var model = this; - var success = options.success; - options.success = function(resp, status, xhr) { - if (!model.set(model.parse(resp), options)) return false; - if (success) success(model, resp, options); - }; - return this.sync('read', this, options); - }, - - // Set a hash of model attributes, and sync the model to the server. - // If the server returns an attributes hash that differs, the model's - // state will be `set` again. - save: function(key, val, options) { - var attrs, current, done; - - // Handle both `"key", value` and `{key: value}` -style arguments. - if (key == null || _.isObject(key)) { - attrs = key; - options = val; - } else if (key != null) { - (attrs = {})[key] = val; - } - options = options ? _.clone(options) : {}; - - // If we're "wait"-ing to set changed attributes, validate early. - if (options.wait) { - if (attrs && !this._validate(attrs, options)) return false; - current = _.clone(this.attributes); - } - - // Regular saves `set` attributes before persisting to the server. - var silentOptions = _.extend({}, options, {silent: true}); - if (attrs && !this.set(attrs, options.wait ? silentOptions : options)) { - return false; - } - - // Do not persist invalid models. - if (!attrs && !this._validate(null, options)) return false; - - // After a successful server-side save, the client is (optionally) - // updated with the server-side state. - var model = this; - var success = options.success; - options.success = function(resp, status, xhr) { - done = true; - var serverAttrs = model.parse(resp); - if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs); - if (!model.set(serverAttrs, options)) return false; - if (success) success(model, resp, options); - }; - - // Finish configuring and sending the Ajax request. - var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); - if (method == 'patch') options.attrs = attrs; - var xhr = this.sync(method, this, options); - - // When using `wait`, reset attributes to original values unless - // `success` has been called already. - if (!done && options.wait) { - this.clear(silentOptions); - this.set(current, silentOptions); - } - - return xhr; - }, - - // Destroy this model on the server if it was already persisted. - // Optimistically removes the model from its collection, if it has one. - // If `wait: true` is passed, waits for the server to respond before removal. - destroy: function(options) { - options = options ? _.clone(options) : {}; - var model = this; - var success = options.success; - - var destroy = function() { - model.trigger('destroy', model, model.collection, options); - }; - - options.success = function(resp) { - if (options.wait || model.isNew()) destroy(); - if (success) success(model, resp, options); - }; - - if (this.isNew()) { - options.success(); - return false; - } - - var xhr = this.sync('delete', this, options); - if (!options.wait) destroy(); - return xhr; - }, - - // Default URL for the model's representation on the server -- if you're - // using Backbone's restful methods, override this to change the endpoint - // that will be called. - url: function() { - var base = _.result(this, 'urlRoot') || _.result(this.collection, 'url') || urlError(); - if (this.isNew()) return base; - return base + (base.charAt(base.length - 1) === '/' ? '' : '/') + encodeURIComponent(this.id); - }, - - // **parse** converts a response into the hash of attributes to be `set` on - // the model. The default implementation is just to pass the response along. - parse: function(resp) { - return resp; - }, - - // Create a new model with identical attributes to this one. - clone: function() { - return new this.constructor(this.attributes); - }, - - // A model is new if it has never been saved to the server, and lacks an id. - isNew: function() { - return this.id == null; - }, - - // Call this method to manually fire a `"change"` event for this model and - // a `"change:attribute"` event for each changed attribute. - // Calling this will cause all objects observing the model to update. - change: function(options) { - var changing = this._changing; - this._changing = true; - - // Generate the changes to be triggered on the model. - var triggers = this._computeChanges(true); - - this._pending = !!triggers.length; - - for (var i = triggers.length - 2; i >= 0; i -= 2) { - this.trigger('change:' + triggers[i], this, triggers[i + 1], options); - } - - if (changing) return this; - - // Trigger a `change` while there have been changes. - while (this._pending) { - this._pending = false; - this.trigger('change', this, options); - this._previousAttributes = _.clone(this.attributes); - } - - this._changing = false; - return this; - }, - - // Determine if the model has changed since the last `"change"` event. - // If you specify an attribute name, determine if that attribute has changed. - hasChanged: function(attr) { - if (!this._hasComputed) this._computeChanges(); - if (attr == null) return !_.isEmpty(this.changed); - return _.has(this.changed, attr); - }, - - // Return an object containing all the attributes that have changed, or - // false if there are no changed attributes. Useful for determining what - // parts of a view need to be updated and/or what attributes need to be - // persisted to the server. Unset attributes will be set to undefined. - // You can also pass an attributes object to diff against the model, - // determining if there *would be* a change. - changedAttributes: function(diff) { - if (!diff) return this.hasChanged() ? _.clone(this.changed) : false; - var val, changed = false, old = this._previousAttributes; - for (var attr in diff) { - if (_.isEqual(old[attr], (val = diff[attr]))) continue; - (changed || (changed = {}))[attr] = val; - } - return changed; - }, - - // Looking at the built up list of `set` attribute changes, compute how - // many of the attributes have actually changed. If `loud`, return a - // boiled-down list of only the real changes. - _computeChanges: function(loud) { - this.changed = {}; - var already = {}; - var triggers = []; - var current = this._currentAttributes; - var changes = this._changes; - - // Loop through the current queue of potential model changes. - for (var i = changes.length - 2; i >= 0; i -= 2) { - var key = changes[i], val = changes[i + 1]; - if (already[key]) continue; - already[key] = true; - - // Check if the attribute has been modified since the last change, - // and update `this.changed` accordingly. If we're inside of a `change` - // call, also add a trigger to the list. - if (current[key] !== val) { - this.changed[key] = val; - if (!loud) continue; - triggers.push(key, val); - current[key] = val; - } - } - if (loud) this._changes = []; - - // Signals `this.changed` is current to prevent duplicate calls from `this.hasChanged`. - this._hasComputed = true; - return triggers; - }, - - // Get the previous value of an attribute, recorded at the time the last - // `"change"` event was fired. - previous: function(attr) { - if (attr == null || !this._previousAttributes) return null; - return this._previousAttributes[attr]; - }, - - // Get all of the attributes of the model at the time of the previous - // `"change"` event. - previousAttributes: function() { - return _.clone(this._previousAttributes); - }, - - // Run validation against the next complete set of model attributes, - // returning `true` if all is well. If a specific `error` callback has - // been passed, call that instead of firing the general `"error"` event. - _validate: function(attrs, options) { - if (!this.validate) return true; - attrs = _.extend({}, this.attributes, attrs); - var error = this.validate(attrs, options); - if (!error) return true; - if (options && options.error) options.error(this, error, options); - this.trigger('error', this, error, options); - return false; - } - - }); - - // Backbone.Collection - // ------------------- - - // Provides a standard collection class for our sets of models, ordered - // or unordered. If a `comparator` is specified, the Collection will maintain - // its models in sort order, as they're added and removed. - var Collection = Backbone.Collection = function(models, options) { - options || (options = {}); - if (options.model) this.model = options.model; - if (options.comparator !== void 0) this.comparator = options.comparator; - this._reset(); - this.initialize.apply(this, arguments); - if (models) this.reset(models, _.extend({silent: true}, options)); - }; - - // Define the Collection's inheritable methods. - _.extend(Collection.prototype, Events, { - - // The default model for a collection is just a **Backbone.Model**. - // This should be overridden in most cases. - model: Model, - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // The JSON representation of a Collection is an array of the - // models' attributes. - toJSON: function(options) { - return this.map(function(model){ return model.toJSON(options); }); - }, - - // Proxy `Backbone.sync` by default. - sync: function() { - return Backbone.sync.apply(this, arguments); - }, - - // Add a model, or list of models to the set. Pass **silent** to avoid - // firing the `add` event for every new model. - add: function(models, options) { - var i, args, length, model, existing, needsSort; - var at = options && options.at; - var sort = ((options && options.sort) == null ? true : options.sort); - models = _.isArray(models) ? models.slice() : [models]; - - // Turn bare objects into model references, and prevent invalid models - // from being added. - for (i = models.length - 1; i >= 0; i--) { - if(!(model = this._prepareModel(models[i], options))) { - this.trigger("error", this, models[i], options); - models.splice(i, 1); - continue; - } - models[i] = model; - - existing = model.id != null && this._byId[model.id]; - // If a duplicate is found, prevent it from being added and - // optionally merge it into the existing model. - if (existing || this._byCid[model.cid]) { - if (options && options.merge && existing) { - existing.set(model.attributes, options); - needsSort = sort; - } - models.splice(i, 1); - continue; - } - - // Listen to added models' events, and index models for lookup by - // `id` and by `cid`. - model.on('all', this._onModelEvent, this); - this._byCid[model.cid] = model; - if (model.id != null) this._byId[model.id] = model; - } - - // See if sorting is needed, update `length` and splice in new models. - if (models.length) needsSort = sort; - this.length += models.length; - args = [at != null ? at : this.models.length, 0]; - push.apply(args, models); - splice.apply(this.models, args); - - // Sort the collection if appropriate. - if (needsSort && this.comparator && at == null) this.sort({silent: true}); - - if (options && options.silent) return this; - - // Trigger `add` events. - while (model = models.shift()) { - model.trigger('add', model, this, options); - } - - return this; - }, - - // Remove a model, or a list of models from the set. Pass silent to avoid - // firing the `remove` event for every model removed. - remove: function(models, options) { - var i, l, index, model; - options || (options = {}); - models = _.isArray(models) ? models.slice() : [models]; - for (i = 0, l = models.length; i < l; i++) { - model = this.get(models[i]); - if (!model) continue; - delete this._byId[model.id]; - delete this._byCid[model.cid]; - index = this.indexOf(model); - this.models.splice(index, 1); - this.length--; - if (!options.silent) { - options.index = index; - model.trigger('remove', model, this, options); - } - this._removeReference(model); - } - return this; - }, - - // Add a model to the end of the collection. - push: function(model, options) { - model = this._prepareModel(model, options); - this.add(model, _.extend({at: this.length}, options)); - return model; - }, - - // Remove a model from the end of the collection. - pop: function(options) { - var model = this.at(this.length - 1); - this.remove(model, options); - return model; - }, - - // Add a model to the beginning of the collection. - unshift: function(model, options) { - model = this._prepareModel(model, options); - this.add(model, _.extend({at: 0}, options)); - return model; - }, - - // Remove a model from the beginning of the collection. - shift: function(options) { - var model = this.at(0); - this.remove(model, options); - return model; - }, - - // Slice out a sub-array of models from the collection. - slice: function(begin, end) { - return this.models.slice(begin, end); - }, - - // Get a model from the set by id. - get: function(obj) { - if (obj == null) return void 0; - return this._byId[obj.id != null ? obj.id : obj] || this._byCid[obj.cid || obj]; - }, - - // Get the model at the given index. - at: function(index) { - return this.models[index]; - }, - - // Return models with matching attributes. Useful for simple cases of `filter`. - where: function(attrs) { - if (_.isEmpty(attrs)) return []; - return this.filter(function(model) { - for (var key in attrs) { - if (attrs[key] !== model.get(key)) return false; - } - return true; - }); - }, - - // Force the collection to re-sort itself. You don't need to call this under - // normal circumstances, as the set will maintain sort order as each item - // is added. - sort: function(options) { - if (!this.comparator) { - throw new Error('Cannot sort a set without a comparator'); - } - - if (_.isString(this.comparator) || this.comparator.length === 1) { - this.models = this.sortBy(this.comparator, this); - } else { - this.models.sort(_.bind(this.comparator, this)); - } - - if (!options || !options.silent) this.trigger('sort', this, options); - return this; - }, - - // Pluck an attribute from each model in the collection. - pluck: function(attr) { - return _.invoke(this.models, 'get', attr); - }, - - // Smartly update a collection with a change set of models, adding, - // removing, and merging as necessary. - update: function(models, options) { - var model, i, l, existing; - var add = [], remove = [], modelMap = {}; - var idAttr = this.model.prototype.idAttribute; - options = _.extend({add: true, merge: true, remove: true}, options); - if (options.parse) models = this.parse(models); - - // Allow a single model (or no argument) to be passed. - if (!_.isArray(models)) models = models ? [models] : []; - - // Proxy to `add` for this case, no need to iterate... - if (options.add && !options.remove) return this.add(models, options); - - // Determine which models to add and merge, and which to remove. - for (i = 0, l = models.length; i < l; i++) { - model = models[i]; - existing = this.get(model.id || model.cid || model[idAttr]); - if (options.remove && existing) modelMap[existing.cid] = true; - if ((options.add && !existing) || (options.merge && existing)) { - add.push(model); - } - } - if (options.remove) { - for (i = 0, l = this.models.length; i < l; i++) { - model = this.models[i]; - if (!modelMap[model.cid]) remove.push(model); - } - } - - // Remove models (if applicable) before we add and merge the rest. - if (remove.length) this.remove(remove, options); - if (add.length) this.add(add, options); - return this; - }, - - // When you have more items than you want to add or remove individually, - // you can reset the entire set with a new list of models, without firing - // any `add` or `remove` events. Fires `reset` when finished. - reset: function(models, options) { - options || (options = {}); - if (options.parse) models = this.parse(models); - for (var i = 0, l = this.models.length; i < l; i++) { - this._removeReference(this.models[i]); - } - options.previousModels = this.models; - this._reset(); - if (models) this.add(models, _.extend({silent: true}, options)); - if (!options.silent) this.trigger('reset', this, options); - return this; - }, - - // Fetch the default set of models for this collection, resetting the - // collection when they arrive. If `add: true` is passed, appends the - // models to the collection instead of resetting. - fetch: function(options) { - options = options ? _.clone(options) : {}; - if (options.parse === void 0) options.parse = true; - var collection = this; - var success = options.success; - options.success = function(resp, status, xhr) { - var method = options.update ? 'update' : 'reset'; - collection[method](resp, options); - if (success) success(collection, resp, options); - }; - return this.sync('read', this, options); - }, - - // Create a new instance of a model in this collection. Add the model to the - // collection immediately, unless `wait: true` is passed, in which case we - // wait for the server to agree. - create: function(model, options) { - var collection = this; - options = options ? _.clone(options) : {}; - model = this._prepareModel(model, options); - if (!model) return false; - if (!options.wait) collection.add(model, options); - var success = options.success; - options.success = function(model, resp, options) { - if (options.wait) collection.add(model, options); - if (success) success(model, resp, options); - }; - model.save(null, options); - return model; - }, - - // **parse** converts a response into a list of models to be added to the - // collection. The default implementation is just to pass it through. - parse: function(resp) { - return resp; - }, - - // Create a new collection with an identical list of models as this one. - clone: function() { - return new this.constructor(this.models); - }, - - // Proxy to _'s chain. Can't be proxied the same way the rest of the - // underscore methods are proxied because it relies on the underscore - // constructor. - chain: function() { - return _(this.models).chain(); - }, - - // Reset all internal state. Called when the collection is reset. - _reset: function() { - this.length = 0; - this.models = []; - this._byId = {}; - this._byCid = {}; - }, - - // Prepare a model or hash of attributes to be added to this collection. - _prepareModel: function(attrs, options) { - if (attrs instanceof Model) { - if (!attrs.collection) attrs.collection = this; - return attrs; - } - options || (options = {}); - options.collection = this; - var model = new this.model(attrs, options); - if (!model._validate(attrs, options)) return false; - return model; - }, - - // Internal method to remove a model's ties to a collection. - _removeReference: function(model) { - if (this === model.collection) delete model.collection; - model.off('all', this._onModelEvent, this); - }, - - // Internal method called every time a model in the set fires an event. - // Sets need to update their indexes when models change ids. All other - // events simply proxy through. "add" and "remove" events that originate - // in other collections are ignored. - _onModelEvent: function(event, model, collection, options) { - if ((event === 'add' || event === 'remove') && collection !== this) return; - if (event === 'destroy') this.remove(model, options); - if (model && event === 'change:' + model.idAttribute) { - delete this._byId[model.previous(model.idAttribute)]; - if (model.id != null) this._byId[model.id] = model; - } - this.trigger.apply(this, arguments); - } - - }); - - // Underscore methods that we want to implement on the Collection. - var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl', - 'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select', - 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', - 'max', 'min', 'sortedIndex', 'toArray', 'size', 'first', 'head', 'take', - 'initial', 'rest', 'tail', 'last', 'without', 'indexOf', 'shuffle', - 'lastIndexOf', 'isEmpty']; - - // Mix in each Underscore method as a proxy to `Collection#models`. - _.each(methods, function(method) { - Collection.prototype[method] = function() { - var args = slice.call(arguments); - args.unshift(this.models); - return _[method].apply(_, args); - }; - }); - - // Underscore methods that take a property name as an argument. - var attributeMethods = ['groupBy', 'countBy', 'sortBy']; - - // Use attributes instead of properties. - _.each(attributeMethods, function(method) { - Collection.prototype[method] = function(value, context) { - var iterator = _.isFunction(value) ? value : function(model) { - return model.get(value); - }; - return _[method](this.models, iterator, context); - }; - }); - - // Backbone.Router - // --------------- - - // Routers map faux-URLs to actions, and fire events when routes are - // matched. Creating a new one sets its `routes` hash, if not set statically. - var Router = Backbone.Router = function(options) { - options || (options = {}); - if (options.routes) this.routes = options.routes; - this._bindRoutes(); - this.initialize.apply(this, arguments); - }; - - // Cached regular expressions for matching named param parts and splatted - // parts of route strings. - var optionalParam = /\((.*?)\)/g; - var namedParam = /:\w+/g; - var splatParam = /\*\w+/g; - var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; - - // Set up all inheritable **Backbone.Router** properties and methods. - _.extend(Router.prototype, Events, { - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // Manually bind a single named route to a callback. For example: - // - // this.route('search/:query/p:num', 'search', function(query, num) { - // ... - // }); - // - route: function(route, name, callback) { - if (!_.isRegExp(route)) route = this._routeToRegExp(route); - if (!callback) callback = this[name]; - Backbone.history.route(route, _.bind(function(fragment) { - var args = this._extractParameters(route, fragment); - callback && callback.apply(this, args); - this.trigger.apply(this, ['route:' + name].concat(args)); - Backbone.history.trigger('route', this, name, args); - }, this)); - return this; - }, - - // Simple proxy to `Backbone.history` to save a fragment into the history. - navigate: function(fragment, options) { - Backbone.history.navigate(fragment, options); - return this; - }, - - // Bind all defined routes to `Backbone.history`. We have to reverse the - // order of the routes here to support behavior where the most general - // routes can be defined at the bottom of the route map. - _bindRoutes: function() { - if (!this.routes) return; - var route, routes = _.keys(this.routes); - while ((route = routes.pop()) != null) { - this.route(route, this.routes[route]); - } - }, - - // Convert a route string into a regular expression, suitable for matching - // against the current location hash. - _routeToRegExp: function(route) { - route = route.replace(escapeRegExp, '\\$&') - .replace(optionalParam, '(?:$1)?') - .replace(namedParam, '([^\/]+)') - .replace(splatParam, '(.*?)'); - return new RegExp('^' + route + '$'); - }, - - // Given a route, and a URL fragment that it matches, return the array of - // extracted parameters. - _extractParameters: function(route, fragment) { - return route.exec(fragment).slice(1); - } - - }); - - // Backbone.History - // ---------------- - - // Handles cross-browser history management, based on URL fragments. If the - // browser does not support `onhashchange`, falls back to polling. - var History = Backbone.History = function() { - this.handlers = []; - _.bindAll(this, 'checkUrl'); - - // #1653 - Ensure that `History` can be used outside of the browser. - if (typeof window !== 'undefined') { - this.location = window.location; - this.history = window.history; - } - }; - - // Cached regex for stripping a leading hash/slash and trailing space. - var routeStripper = /^[#\/]|\s+$/g; - - // Cached regex for stripping leading and trailing slashes. - var rootStripper = /^\/+|\/+$/g; - - // Cached regex for detecting MSIE. - var isExplorer = /msie [\w.]+/; - - // Cached regex for removing a trailing slash. - var trailingSlash = /\/$/; - - // Has the history handling already been started? - History.started = false; - - // Set up all inheritable **Backbone.History** properties and methods. - _.extend(History.prototype, Events, { - - // The default interval to poll for hash changes, if necessary, is - // twenty times a second. - interval: 50, - - // Gets the true hash value. Cannot use location.hash directly due to bug - // in Firefox where location.hash will always be decoded. - getHash: function(window) { - var match = (window || this).location.href.match(/#(.*)$/); - return match ? match[1] : ''; - }, - - // Get the cross-browser normalized URL fragment, either from the URL, - // the hash, or the override. - getFragment: function(fragment, forcePushState) { - if (fragment == null) { - if (this._hasPushState || !this._wantsHashChange || forcePushState) { - fragment = this.location.pathname; - var root = this.root.replace(trailingSlash, ''); - if (!fragment.indexOf(root)) fragment = fragment.substr(root.length); - } else { - fragment = this.getHash(); - } - } - return fragment.replace(routeStripper, ''); - }, - - // Start the hash change handling, returning `true` if the current URL matches - // an existing route, and `false` otherwise. - start: function(options) { - if (History.started) throw new Error("Backbone.history has already been started"); - History.started = true; - - // Figure out the initial configuration. Do we need an iframe? - // Is pushState desired ... is it available? - this.options = _.extend({}, {root: '/'}, this.options, options); - this.root = this.options.root; - this._wantsHashChange = this.options.hashChange !== false; - this._wantsPushState = !!this.options.pushState; - this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState); - var fragment = this.getFragment(); - var docMode = document.documentMode; - var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7)); - - // Normalize root to always include a leading and trailing slash. - this.root = ('/' + this.root + '/').replace(rootStripper, '/'); - - if (oldIE && this._wantsHashChange) { - this.iframe = Backbone.$('