From 1e2fcbf1ba375fca43c398943e360a0e7f1db6ec Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Mon, 9 Feb 2015 14:50:50 +0700 Subject: [PATCH] Revert "Merge branch 'pr/1138'" This reverts commit c898a78d00ab80b4b2f91c45fa9fe4baebd3a482, reversing changes made to 480cf2980b289e6838365d2a6924890c7629f91f. --- .travis.yml | 18 +- examples/ampersand/.gitignore | 13 - examples/ampersand/index.html | 5 +- .../node_modules/todomvc-app-css/index.css | 394 - .../node_modules/todomvc-common/base.css | 141 - examples/{ => ampersand}/package.json | 8 +- examples/angularjs-perf/.gitignore | 9 - examples/angularjs-perf/bower.json | 8 + .../angular/angular.js | 0 .../bower_components/todomvc-common/base.css | 554 + .../bower_components}/todomvc-common/base.js | 27 - .../bower_components/todomvc-common/bg.png | Bin 0 -> 2126 bytes .../todomvc-common/bower.json | 22 + examples/angularjs-perf/index.html | 7 +- .../node_modules/todomvc-app-css/index.css | 394 - .../node_modules/todomvc-common/base.css | 141 - examples/angularjs-perf/package.json | 8 - examples/ariatemplates/.gitignore | 6 - examples/ariatemplates/bower.json | 7 + .../bower_components/todomvc-common/base.css | 554 + .../bower_components}/todomvc-common/base.js | 27 - .../bower_components/todomvc-common/bg.png | Bin 0 -> 2126 bytes examples/ariatemplates/index.html | 6 +- .../ariatemplates/js/view/TodoStyle.tpl.css | 177 +- .../node_modules/todomvc-app-css/index.css | 394 - .../node_modules/todomvc-common/base.css | 141 - examples/ariatemplates/package.json | 7 - examples/batman/.gitignore | 11 - examples/batman/bower.json | 8 + .../batman/lib/dist/batman.js | 0 .../bower_components/todomvc-common/base.css | 554 + .../bower_components}/todomvc-common/base.js | 27 - .../bower_components/todomvc-common/bg.png | Bin 0 -> 2126 bytes examples/batman/index.html | 7 +- .../node_modules/todomvc-app-css/index.css | 394 - .../node_modules/todomvc-common/base.css | 141 - examples/batman/package.json | 17 +- examples/emberjs/.gitignore | 22 - examples/emberjs/bower.json | 12 + .../ember-data/ember-data.js | 17431 ++-- .../localstorage_adapter.js | 2 +- .../ember}/ember.js | 85888 +++++++--------- .../handlebars}/handlebars.js | 1665 +- .../jquery/dist/jquery.js | 539 +- .../bower_components/todomvc-common/base.css | 554 + .../bower_components}/todomvc-common/base.js | 27 - .../bower_components/todomvc-common/bg.png | Bin 0 -> 2126 bytes examples/emberjs/index.html | 19 +- .../node_modules/todomvc-app-css/index.css | 394 - .../node_modules/todomvc-common/base.css | 141 - .../node_modules/todomvc-common/base.js | 244 - examples/emberjs/package.json | 11 - examples/enyo_backbone/.gitignore | 6 - examples/enyo_backbone/bower.json | 7 + .../bower_components/todomvc-common/base.css | 554 + .../bower_components/todomvc-common/base.js | 217 + .../bower_components/todomvc-common/bg.png | Bin 0 -> 2126 bytes examples/enyo_backbone/index.html | 5 +- .../node_modules/todomvc-app-css/index.css | 394 - .../node_modules/todomvc-common/base.css | 141 - .../node_modules/todomvc-common/base.js | 244 - examples/enyo_backbone/package.json | 7 - examples/extjs_deftjs/.gitignore | 9 +- examples/extjs_deftjs/bower.json | 7 + .../bower_components/todomvc-common/base.css | 554 + .../bower_components/todomvc-common/base.js | 217 + .../bower_components/todomvc-common/bg.png | Bin 0 -> 2126 bytes examples/extjs_deftjs/index.html | 5 +- .../node_modules/todomvc-app-css/index.css | 394 - .../node_modules/todomvc-common/base.css | 141 - .../node_modules/todomvc-common/base.js | 244 - examples/extjs_deftjs/package.json | 7 - examples/flight/.gitignore | 42 +- examples/flight/app/js/main.js | 12 +- examples/flight/bower.json | 16 + .../depot/depot.js | 40 +- .../es5-shim/es5-sham.js | 142 +- .../bower_components/es5-shim/es5-shim.js | 778 + .../flight/lib/advice.js | 0 .../flight/lib/base.js | 0 .../flight/lib/component.js | 0 .../flight/lib/compose.js | 0 .../flight/lib/debug.js | 0 .../flight/lib/logger.js | 0 .../flight/lib/registry.js | 0 .../flight/lib/utils.js | 0 .../bower_components}/jquery/dist/jquery.js | 918 +- .../requirejs-text/text.js | 10 +- .../requirejs/require.js | 0 .../bower_components/todomvc-common/base.css | 554 + .../bower_components/todomvc-common/base.js | 217 + .../bower_components/todomvc-common/bg.png | Bin 0 -> 2126 bytes examples/flight/index.html | 7 +- examples/flight/karma.conf.js | 17 +- .../flight/node_modules/es5-shim/es5-shim.js | 1360 - .../node_modules/todomvc-app-css/index.css | 394 - .../node_modules/todomvc-common/base.css | 141 - .../node_modules/todomvc-common/base.js | 244 - examples/flight/package.json | 41 +- examples/flight/test/test-main.js | 6 +- examples/foam/Todo.js | 5 +- .../todomvc-app-css/index.css | 394 - examples/foam/readme.md | 2 +- examples/jquery/.gitignore | 16 +- examples/jquery/bower.json | 9 + .../director/build/director.js | 62 +- .../handlebars}/handlebars.js | 0 .../bower_components}/jquery/dist/jquery.js | 539 +- .../bower_components/todomvc-common/base.css | 554 + .../bower_components/todomvc-common/base.js | 217 + .../bower_components/todomvc-common/bg.png | Bin 0 -> 2126 bytes examples/jquery/index.html | 11 +- .../node_modules/todomvc-app-css/index.css | 394 - .../node_modules/todomvc-common/base.css | 141 - .../node_modules/todomvc-common/base.js | 244 - examples/jquery/package.json | 12 - examples/meteor/.meteor/.id | 7 - examples/meteor/.meteor/platforms | 2 - examples/meteor/.meteor/versions | 49 - examples/plastronjs/.gitignore | 6 - examples/plastronjs/bower.json | 7 + .../bower_components/todomvc-common/base.css | 554 + .../bower_components/todomvc-common/base.js | 217 + .../bower_components/todomvc-common/bg.png | Bin 0 -> 2126 bytes examples/plastronjs/index.html | 5 +- .../node_modules/todomvc-app-css/index.css | 394 - .../node_modules/todomvc-common/base.css | 141 - .../node_modules/todomvc-common/base.js | 244 - examples/plastronjs/package.json | 7 - examples/rappidjs/.gitignore | 6 - examples/rappidjs/bower.json | 7 + .../bower_components/todomvc-common/base.css | 554 + .../bower_components/todomvc-common/base.js | 217 + .../bower_components/todomvc-common/bg.png | Bin 0 -> 2126 bytes examples/rappidjs/index.html | 5 +- .../node_modules/todomvc-app-css/index.css | 394 - .../node_modules/todomvc-common/base.css | 141 - .../node_modules/todomvc-common/base.js | 244 - examples/rappidjs/package.json | 7 - examples/react/.gitignore | 12 - examples/react/bower.json | 9 + .../director/build/director.js | 62 +- .../react}/JSXTransformer.js | 0 .../react}/react-with-addons.js | 0 .../bower_components/todomvc-common/base.css | 554 + .../bower_components/todomvc-common/base.js | 217 + .../bower_components/todomvc-common/bg.png | Bin 0 -> 2126 bytes examples/react/index.html | 11 +- .../node_modules/todomvc-app-css/index.css | 394 - .../node_modules/todomvc-common/base.css | 141 - .../react/node_modules/todomvc-common/base.js | 244 - examples/react/package.json | 9 - examples/vanillajs/.gitignore | 6 - examples/vanillajs/bower.json | 10 + .../jasmine/lib/jasmine-core/jasmine-html.js | 681 + .../jasmine/lib/jasmine-core/jasmine.css | 82 + .../jasmine/lib/jasmine-core/jasmine.js | 2600 + .../bower_components/todomvc-common/base.css | 554 + .../bower_components/todomvc-common/base.js | 217 + .../bower_components/todomvc-common/bg.png | Bin 0 -> 2126 bytes examples/vanillajs/index.html | 5 +- .../node_modules/todomvc-app-css/index.css | 394 - .../node_modules/todomvc-common/base.css | 141 - .../node_modules/todomvc-common/base.js | 244 - examples/vanillajs/package.json | 10 - examples/vanillajs/test/SpecRunner.html | 8 +- examples/yui/.gitignore | 6 - examples/yui/bower.json | 6 + .../bower_components/todomvc-common/base.css | 554 + .../bower_components/todomvc-common/base.js | 217 + .../bower_components/todomvc-common/bg.png | Bin 0 -> 2126 bytes examples/yui/index.html | 7 +- .../node_modules/todomvc-app-css/index.css | 394 - .../yui/node_modules/todomvc-common/base.css | 141 - .../yui/node_modules/todomvc-common/base.js | 244 - examples/yui/package.json | 7 - index.html | 6 +- 177 files changed, 60494 insertions(+), 72769 deletions(-) delete mode 100644 examples/ampersand/.gitignore delete mode 100644 examples/ampersand/node_modules/todomvc-app-css/index.css delete mode 100644 examples/ampersand/node_modules/todomvc-common/base.css rename examples/{ => ampersand}/package.json (73%) delete mode 100644 examples/angularjs-perf/.gitignore create mode 100644 examples/angularjs-perf/bower.json rename examples/angularjs-perf/{node_modules => bower_components}/angular/angular.js (100%) create mode 100644 examples/angularjs-perf/bower_components/todomvc-common/base.css rename examples/{ariatemplates/node_modules => angularjs-perf/bower_components}/todomvc-common/base.js (87%) create mode 100644 examples/angularjs-perf/bower_components/todomvc-common/bg.png create mode 100644 examples/angularjs-perf/bower_components/todomvc-common/bower.json delete mode 100644 examples/angularjs-perf/node_modules/todomvc-app-css/index.css delete mode 100644 examples/angularjs-perf/node_modules/todomvc-common/base.css delete mode 100644 examples/angularjs-perf/package.json delete mode 100644 examples/ariatemplates/.gitignore create mode 100644 examples/ariatemplates/bower.json create mode 100644 examples/ariatemplates/bower_components/todomvc-common/base.css rename examples/{batman/node_modules => ariatemplates/bower_components}/todomvc-common/base.js (87%) create mode 100644 examples/ariatemplates/bower_components/todomvc-common/bg.png delete mode 100644 examples/ariatemplates/node_modules/todomvc-app-css/index.css delete mode 100644 examples/ariatemplates/node_modules/todomvc-common/base.css delete mode 100644 examples/ariatemplates/package.json delete mode 100644 examples/batman/.gitignore create mode 100644 examples/batman/bower.json rename examples/batman/{node_modules => bower_components}/batman/lib/dist/batman.js (100%) create mode 100644 examples/batman/bower_components/todomvc-common/base.css rename examples/{angularjs-perf/node_modules => batman/bower_components}/todomvc-common/base.js (87%) create mode 100644 examples/batman/bower_components/todomvc-common/bg.png delete mode 100644 examples/batman/node_modules/todomvc-app-css/index.css delete mode 100644 examples/batman/node_modules/todomvc-common/base.css delete mode 100644 examples/emberjs/.gitignore create mode 100644 examples/emberjs/bower.json rename examples/emberjs/{node_modules => bower_components}/ember-data/ember-data.js (52%) rename examples/emberjs/{node_modules => bower_components}/ember-localstorage-adapter/localstorage_adapter.js (99%) rename examples/emberjs/{node_modules/components-ember => bower_components/ember}/ember.js (61%) rename examples/{jquery/node_modules/handlebars/dist => emberjs/bower_components/handlebars}/handlebars.js (59%) rename examples/emberjs/{node_modules => bower_components}/jquery/dist/jquery.js (95%) create mode 100644 examples/emberjs/bower_components/todomvc-common/base.css rename examples/{ampersand/node_modules => emberjs/bower_components}/todomvc-common/base.js (87%) create mode 100644 examples/emberjs/bower_components/todomvc-common/bg.png delete mode 100644 examples/emberjs/node_modules/todomvc-app-css/index.css delete mode 100644 examples/emberjs/node_modules/todomvc-common/base.css delete mode 100644 examples/emberjs/node_modules/todomvc-common/base.js delete mode 100644 examples/emberjs/package.json delete mode 100644 examples/enyo_backbone/.gitignore create mode 100644 examples/enyo_backbone/bower.json create mode 100644 examples/enyo_backbone/bower_components/todomvc-common/base.css create mode 100644 examples/enyo_backbone/bower_components/todomvc-common/base.js create mode 100644 examples/enyo_backbone/bower_components/todomvc-common/bg.png delete mode 100644 examples/enyo_backbone/node_modules/todomvc-app-css/index.css delete mode 100644 examples/enyo_backbone/node_modules/todomvc-common/base.css delete mode 100644 examples/enyo_backbone/node_modules/todomvc-common/base.js delete mode 100644 examples/enyo_backbone/package.json create mode 100644 examples/extjs_deftjs/bower.json create mode 100644 examples/extjs_deftjs/bower_components/todomvc-common/base.css create mode 100644 examples/extjs_deftjs/bower_components/todomvc-common/base.js create mode 100644 examples/extjs_deftjs/bower_components/todomvc-common/bg.png delete mode 100644 examples/extjs_deftjs/node_modules/todomvc-app-css/index.css delete mode 100644 examples/extjs_deftjs/node_modules/todomvc-common/base.css delete mode 100644 examples/extjs_deftjs/node_modules/todomvc-common/base.js delete mode 100644 examples/extjs_deftjs/package.json create mode 100644 examples/flight/bower.json rename examples/flight/{node_modules => bower_components}/depot/depot.js (88%) rename examples/flight/{node_modules => bower_components}/es5-shim/es5-sham.js (68%) create mode 100644 examples/flight/bower_components/es5-shim/es5-shim.js rename examples/flight/{node_modules => bower_components}/flight/lib/advice.js (100%) rename examples/flight/{node_modules => bower_components}/flight/lib/base.js (100%) rename examples/flight/{node_modules => bower_components}/flight/lib/component.js (100%) rename examples/flight/{node_modules => bower_components}/flight/lib/compose.js (100%) rename examples/flight/{node_modules => bower_components}/flight/lib/debug.js (100%) rename examples/flight/{node_modules => bower_components}/flight/lib/logger.js (100%) rename examples/flight/{node_modules => bower_components}/flight/lib/registry.js (100%) rename examples/flight/{node_modules => bower_components}/flight/lib/utils.js (100%) rename examples/{jquery/node_modules => flight/bower_components}/jquery/dist/jquery.js (91%) rename examples/flight/{node_modules => bower_components}/requirejs-text/text.js (98%) rename examples/flight/{node_modules => bower_components}/requirejs/require.js (100%) create mode 100644 examples/flight/bower_components/todomvc-common/base.css create mode 100644 examples/flight/bower_components/todomvc-common/base.js create mode 100644 examples/flight/bower_components/todomvc-common/bg.png delete mode 100644 examples/flight/node_modules/es5-shim/es5-shim.js delete mode 100644 examples/flight/node_modules/todomvc-app-css/index.css delete mode 100644 examples/flight/node_modules/todomvc-common/base.css delete mode 100644 examples/flight/node_modules/todomvc-common/base.js delete mode 100644 examples/foam/bower_components/todomvc-app-css/index.css create mode 100644 examples/jquery/bower.json rename examples/jquery/{node_modules => bower_components}/director/build/director.js (93%) rename examples/{emberjs/node_modules/handlebars/dist => jquery/bower_components/handlebars}/handlebars.js (100%) rename examples/{flight/node_modules => jquery/bower_components}/jquery/dist/jquery.js (95%) create mode 100644 examples/jquery/bower_components/todomvc-common/base.css create mode 100644 examples/jquery/bower_components/todomvc-common/base.js create mode 100644 examples/jquery/bower_components/todomvc-common/bg.png delete mode 100644 examples/jquery/node_modules/todomvc-app-css/index.css delete mode 100644 examples/jquery/node_modules/todomvc-common/base.css delete mode 100644 examples/jquery/node_modules/todomvc-common/base.js delete mode 100644 examples/jquery/package.json delete mode 100644 examples/meteor/.meteor/.id delete mode 100644 examples/meteor/.meteor/platforms delete mode 100644 examples/meteor/.meteor/versions delete mode 100644 examples/plastronjs/.gitignore create mode 100644 examples/plastronjs/bower.json create mode 100644 examples/plastronjs/bower_components/todomvc-common/base.css create mode 100644 examples/plastronjs/bower_components/todomvc-common/base.js create mode 100644 examples/plastronjs/bower_components/todomvc-common/bg.png delete mode 100644 examples/plastronjs/node_modules/todomvc-app-css/index.css delete mode 100644 examples/plastronjs/node_modules/todomvc-common/base.css delete mode 100644 examples/plastronjs/node_modules/todomvc-common/base.js delete mode 100644 examples/plastronjs/package.json delete mode 100644 examples/rappidjs/.gitignore create mode 100644 examples/rappidjs/bower.json create mode 100644 examples/rappidjs/bower_components/todomvc-common/base.css create mode 100644 examples/rappidjs/bower_components/todomvc-common/base.js create mode 100644 examples/rappidjs/bower_components/todomvc-common/bg.png delete mode 100644 examples/rappidjs/node_modules/todomvc-app-css/index.css delete mode 100644 examples/rappidjs/node_modules/todomvc-common/base.css delete mode 100644 examples/rappidjs/node_modules/todomvc-common/base.js delete mode 100644 examples/rappidjs/package.json delete mode 100644 examples/react/.gitignore create mode 100644 examples/react/bower.json rename examples/react/{node_modules => bower_components}/director/build/director.js (93%) rename examples/react/{node_modules/react/dist => bower_components/react}/JSXTransformer.js (100%) rename examples/react/{node_modules/react/dist => bower_components/react}/react-with-addons.js (100%) create mode 100644 examples/react/bower_components/todomvc-common/base.css create mode 100644 examples/react/bower_components/todomvc-common/base.js create mode 100644 examples/react/bower_components/todomvc-common/bg.png delete mode 100644 examples/react/node_modules/todomvc-app-css/index.css delete mode 100644 examples/react/node_modules/todomvc-common/base.css delete mode 100644 examples/react/node_modules/todomvc-common/base.js delete mode 100644 examples/react/package.json delete mode 100644 examples/vanillajs/.gitignore create mode 100644 examples/vanillajs/bower.json create mode 100644 examples/vanillajs/bower_components/jasmine/lib/jasmine-core/jasmine-html.js create mode 100644 examples/vanillajs/bower_components/jasmine/lib/jasmine-core/jasmine.css create mode 100644 examples/vanillajs/bower_components/jasmine/lib/jasmine-core/jasmine.js create mode 100644 examples/vanillajs/bower_components/todomvc-common/base.css create mode 100644 examples/vanillajs/bower_components/todomvc-common/base.js create mode 100644 examples/vanillajs/bower_components/todomvc-common/bg.png delete mode 100644 examples/vanillajs/node_modules/todomvc-app-css/index.css delete mode 100644 examples/vanillajs/node_modules/todomvc-common/base.css delete mode 100644 examples/vanillajs/node_modules/todomvc-common/base.js delete mode 100644 examples/vanillajs/package.json delete mode 100644 examples/yui/.gitignore create mode 100644 examples/yui/bower.json create mode 100644 examples/yui/bower_components/todomvc-common/base.css create mode 100644 examples/yui/bower_components/todomvc-common/base.js create mode 100644 examples/yui/bower_components/todomvc-common/bg.png delete mode 100644 examples/yui/node_modules/todomvc-app-css/index.css delete mode 100644 examples/yui/node_modules/todomvc-common/base.css delete mode 100644 examples/yui/node_modules/todomvc-common/base.js delete mode 100644 examples/yui/package.json diff --git a/.travis.yml b/.travis.yml index e25c6990bb..99a0dd7cdd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,12 +2,14 @@ # run on your project. The secure: variable must be generated by running `travis encrypt` # on a github oauth key that you can generate using curl. -sudo: false -language: node_js +language: + node_js +sudo: + false node_js: - - '0.12' + - 0.10 notifications: - irc: 'irc.freenode.org#tastejs' + irc: "irc.freenode.org#tastejs" branches: only: - master @@ -24,7 +26,7 @@ env: # travis encrypt GH_OAUTH_TOKEN=XXXXXXXXXXXXXXX # # User specific env variables - - secure: 'fHgfjMpYuliwMr2QLnjYZExIViNrxprf9dhXRBLZ6P9Hz7P6m1BMYrI/xEG8X+fFbCi0+n3AXh8SEMHi9ou/Pty/cx12z4w/z3B2BHMxh4XBwpZHs+AB4IXkLiwwWoP4QFy4vTipgYnMDMq9CRhlRbhZEpenQBmaTEc472By1uM=' + - secure: "fHgfjMpYuliwMr2QLnjYZExIViNrxprf9dhXRBLZ6P9Hz7P6m1BMYrI/xEG8X+fFbCi0+n3AXh8SEMHi9ou/Pty/cx12z4w/z3B2BHMxh4XBwpZHs+AB4IXkLiwwWoP4QFy4vTipgYnMDMq9CRhlRbhZEpenQBmaTEc472By1uM=" - GH_OWNER: tastejs - GH_PROJECT_NAME: todomvc @@ -43,8 +45,8 @@ after_success: - cp -R ../dist/* . - cp ../dist/.* . - git add -f . - - git config user.email 'travis@rdrei.net' - - git config user.name 'TasteBot' - - git commit -am 'update the build files for gh-pages [ci skip]' + - git config user.email "travis@rdrei.net" + - git config user.name "TasteBot" + - git commit -am "update the build files for gh-pages [ci skip]" # Any command that using GH_OAUTH_TOKEN must pipe the output to /dev/null to not expose your oauth token - git push https://${GH_OAUTH_TOKEN}@github.com/${GH_OWNER}/${GH_PROJECT_NAME} HEAD:gh-pages > /dev/null 2>&1 diff --git a/examples/ampersand/.gitignore b/examples/ampersand/.gitignore deleted file mode 100644 index f24e65db1c..0000000000 --- a/examples/ampersand/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -node_modules/.bin -node_modules/browserify -node_modules/watchify -node_modules/jadeify -node_modules/jade -node_modules/debounce -node_modules/ampersand-* - -node_modules/todomvc-app-css -!node_modules/todomvc-app-css/index.css -node_modules/todomvc-common -!node_modules/todomvc-common/base.css -!node_modules/todomvc-common/base.js diff --git a/examples/ampersand/index.html b/examples/ampersand/index.html index 12ca61bfc0..ce3126ade1 100644 --- a/examples/ampersand/index.html +++ b/examples/ampersand/index.html @@ -3,8 +3,7 @@ Ampersand.js • TodoMVC - - +
@@ -38,7 +37,7 @@

todos

Written by Henrik Joreteg, Luke Karrys, and Philip Roberts

Part of TodoMVC

- + diff --git a/examples/ampersand/node_modules/todomvc-app-css/index.css b/examples/ampersand/node_modules/todomvc-app-css/index.css deleted file mode 100644 index 4308848800..0000000000 --- a/examples/ampersand/node_modules/todomvc-app-css/index.css +++ /dev/null @@ -1,394 +0,0 @@ -html, -body { - margin: 0; - padding: 0; -} - -button { - margin: 0; - padding: 0; - border: 0; - background: none; - font-size: 100%; - vertical-align: baseline; - font-family: inherit; - font-weight: inherit; - color: inherit; - -webkit-appearance: none; - -ms-appearance: none; - appearance: none; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; -} - -body { - font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; - line-height: 1.4em; - background: #f5f5f5; - color: #4d4d4d; - min-width: 230px; - max-width: 550px; - margin: 0 auto; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; - font-weight: 300; -} - -button, -input[type="checkbox"] { - outline: none; -} - -.hidden { - display: none; -} - -#todoapp { - background: #fff; - margin: 130px 0 40px 0; - position: relative; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), - 0 25px 50px 0 rgba(0, 0, 0, 0.1); -} - -#todoapp input::-webkit-input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp input::-moz-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp input::input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp h1 { - position: absolute; - top: -155px; - width: 100%; - font-size: 100px; - font-weight: 100; - text-align: center; - color: rgba(175, 47, 47, 0.15); - -webkit-text-rendering: optimizeLegibility; - -moz-text-rendering: optimizeLegibility; - -ms-text-rendering: optimizeLegibility; - text-rendering: optimizeLegibility; -} - -#new-todo, -.edit { - position: relative; - margin: 0; - width: 100%; - font-size: 24px; - font-family: inherit; - font-weight: inherit; - line-height: 1.4em; - border: 0; - outline: none; - color: inherit; - padding: 6px; - border: 1px solid #999; - box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); - -ms-box-sizing: border-box; - box-sizing: border-box; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; -} - -#new-todo { - padding: 16px 16px 16px 60px; - border: none; - background: rgba(0, 0, 0, 0.003); - box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); -} - -#main { - position: relative; - z-index: 2; - border-top: 1px solid #e6e6e6; -} - -label[for='toggle-all'] { - display: none; -} - -#toggle-all { - position: absolute; - top: -55px; - left: -12px; - width: 60px; - height: 34px; - text-align: center; - border: none; /* Mobile Safari */ -} - -#toggle-all:before { - content: '❯'; - font-size: 22px; - color: #e6e6e6; - padding: 10px 27px 10px 27px; -} - -#toggle-all:checked:before { - color: #737373; -} - -#todo-list { - margin: 0; - padding: 0; - list-style: none; -} - -#todo-list li { - position: relative; - font-size: 24px; - border-bottom: 1px solid #ededed; -} - -#todo-list li:last-child { - border-bottom: none; -} - -#todo-list li.editing { - border-bottom: none; - padding: 0; -} - -#todo-list li.editing .edit { - display: block; - width: 506px; - padding: 13px 17px 12px 17px; - margin: 0 0 0 43px; -} - -#todo-list li.editing .view { - display: none; -} - -#todo-list li .toggle { - text-align: center; - width: 40px; - /* auto, since non-WebKit browsers doesn't support input styling */ - height: auto; - position: absolute; - top: 0; - bottom: 0; - margin: auto 0; - border: none; /* Mobile Safari */ - -webkit-appearance: none; - -ms-appearance: none; - appearance: none; -} - -#todo-list li .toggle:after { - content: url('data:image/svg+xml;utf8,'); -} - -#todo-list li .toggle:checked:after { - content: url('data:image/svg+xml;utf8,'); -} - -#todo-list li label { - white-space: pre; - word-break: break-word; - padding: 15px 60px 15px 15px; - margin-left: 45px; - display: block; - line-height: 1.2; - transition: color 0.4s; -} - -#todo-list li.completed label { - color: #d9d9d9; - text-decoration: line-through; -} - -#todo-list li .destroy { - display: none; - position: absolute; - top: 0; - right: 10px; - bottom: 0; - width: 40px; - height: 40px; - margin: auto 0; - font-size: 30px; - color: #cc9a9a; - margin-bottom: 11px; - transition: color 0.2s ease-out; -} - -#todo-list li .destroy:hover { - color: #af5b5e; -} - -#todo-list li .destroy:after { - content: '×'; -} - -#todo-list li:hover .destroy { - display: block; -} - -#todo-list li .edit { - display: none; -} - -#todo-list li.editing:last-child { - margin-bottom: -1px; -} - -#footer { - color: #777; - padding: 10px 15px; - height: 20px; - text-align: center; - border-top: 1px solid #e6e6e6; -} - -#footer:before { - content: ''; - position: absolute; - right: 0; - bottom: 0; - left: 0; - height: 50px; - overflow: hidden; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), - 0 8px 0 -3px #f6f6f6, - 0 9px 1px -3px rgba(0, 0, 0, 0.2), - 0 16px 0 -6px #f6f6f6, - 0 17px 2px -6px rgba(0, 0, 0, 0.2); -} - -#todo-count { - float: left; - text-align: left; -} - -#todo-count strong { - font-weight: 300; -} - -#filters { - margin: 0; - padding: 0; - list-style: none; - position: absolute; - right: 0; - left: 0; -} - -#filters li { - display: inline; -} - -#filters li a { - color: inherit; - margin: 3px; - padding: 3px 7px; - text-decoration: none; - border: 1px solid transparent; - border-radius: 3px; -} - -#filters li a.selected, -#filters li a:hover { - border-color: rgba(175, 47, 47, 0.1); -} - -#filters li a.selected { - border-color: rgba(175, 47, 47, 0.2); -} - -#clear-completed, -html #clear-completed:active { - float: right; - position: relative; - line-height: 20px; - text-decoration: none; - cursor: pointer; - visibility: hidden; - position: relative; -} - -#clear-completed::after { - visibility: visible; - content: 'Clear completed'; - position: absolute; - right: 0; - white-space: nowrap; -} - -#clear-completed:hover::after { - text-decoration: underline; -} - -#info { - margin: 65px auto 0; - color: #bfbfbf; - font-size: 10px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - text-align: center; -} - -#info p { - line-height: 1; -} - -#info a { - color: inherit; - text-decoration: none; - font-weight: 400; -} - -#info a:hover { - text-decoration: underline; -} - -/* - Hack to remove background from Mobile Safari. - Can't use it globally since it destroys checkboxes in Firefox -*/ -@media screen and (-webkit-min-device-pixel-ratio:0) { - #toggle-all, - #todo-list li .toggle { - background: none; - } - - #todo-list li .toggle { - height: 40px; - } - - #toggle-all { - -webkit-transform: rotate(90deg); - transform: rotate(90deg); - -webkit-appearance: none; - appearance: none; - } -} - -@media (max-width: 430px) { - #footer { - height: 50px; - } - - #filters { - bottom: 10px; - } -} diff --git a/examples/ampersand/node_modules/todomvc-common/base.css b/examples/ampersand/node_modules/todomvc-common/base.css deleted file mode 100644 index da65968a73..0000000000 --- a/examples/ampersand/node_modules/todomvc-common/base.css +++ /dev/null @@ -1,141 +0,0 @@ -hr { - margin: 20px 0; - border: 0; - border-top: 1px dashed #c5c5c5; - border-bottom: 1px dashed #f7f7f7; -} - -.learn a { - font-weight: normal; - text-decoration: none; - color: #b83f45; -} - -.learn a:hover { - text-decoration: underline; - color: #787e7e; -} - -.learn h3, -.learn h4, -.learn h5 { - margin: 10px 0; - font-weight: 500; - line-height: 1.2; - color: #000; -} - -.learn h3 { - font-size: 24px; -} - -.learn h4 { - font-size: 18px; -} - -.learn h5 { - margin-bottom: 0; - font-size: 14px; -} - -.learn ul { - padding: 0; - margin: 0 0 30px 25px; -} - -.learn li { - line-height: 20px; -} - -.learn p { - font-size: 15px; - font-weight: 300; - line-height: 1.3; - margin-top: 0; - margin-bottom: 0; -} - -#issue-count { - display: none; -} - -.quote { - border: none; - margin: 20px 0 60px 0; -} - -.quote p { - font-style: italic; -} - -.quote p:before { - content: '“'; - font-size: 50px; - opacity: .15; - position: absolute; - top: -20px; - left: 3px; -} - -.quote p:after { - content: '”'; - font-size: 50px; - opacity: .15; - position: absolute; - bottom: -42px; - right: 3px; -} - -.quote footer { - position: absolute; - bottom: -40px; - right: 0; -} - -.quote footer img { - border-radius: 3px; -} - -.quote footer a { - margin-left: 5px; - vertical-align: middle; -} - -.speech-bubble { - position: relative; - padding: 10px; - background: rgba(0, 0, 0, .04); - border-radius: 5px; -} - -.speech-bubble:after { - content: ''; - position: absolute; - top: 100%; - right: 30px; - border: 13px solid transparent; - border-top-color: rgba(0, 0, 0, .04); -} - -.learn-bar > .learn { - position: absolute; - width: 272px; - top: 8px; - left: -300px; - padding: 10px; - border-radius: 5px; - background-color: rgba(255, 255, 255, .6); - transition-property: left; - transition-duration: 500ms; -} - -@media (min-width: 899px) { - .learn-bar { - width: auto; - padding-left: 300px; - } - - .learn-bar > .learn { - left: 8px; - } -} diff --git a/examples/package.json b/examples/ampersand/package.json similarity index 73% rename from examples/package.json rename to examples/ampersand/package.json index 776af6fdd3..72a2a60d72 100644 --- a/examples/package.json +++ b/examples/ampersand/package.json @@ -1,4 +1,8 @@ { + "name": "ampersand-todomvc", + "description": "TodoMVC Implementation in Ampersand.js", + "version": "1.0.0", + "author": "Henrik Joreteg ", "private": true, "dependencies": { "ampersand-collection": "^1.3.16", @@ -6,9 +10,7 @@ "ampersand-state": "^4.3.12", "ampersand-subcollection": "^1.4.5", "ampersand-view": "^7.1.4", - "debounce": "^1.0.0", - "todomvc-app-css": "^1.0.1", - "todomvc-common": "^1.0.1" + "debounce": "^1.0.0" }, "devDependencies": { "browserify": "5.10.1", diff --git a/examples/angularjs-perf/.gitignore b/examples/angularjs-perf/.gitignore deleted file mode 100644 index b2482351ba..0000000000 --- a/examples/angularjs-perf/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -node_modules/todomvc-common -!node_modules/todomvc-common/base.js -!node_modules/todomvc-common/base.css - -node_modules/todomvc-app-css -!node_modules/todomvc-app-css/index.css - -node_modules/angular -!node_modules/angular/angular.js diff --git a/examples/angularjs-perf/bower.json b/examples/angularjs-perf/bower.json new file mode 100644 index 0000000000..1b2e057470 --- /dev/null +++ b/examples/angularjs-perf/bower.json @@ -0,0 +1,8 @@ +{ + "name": "todomvc-angular-perf", + "version": "0.0.0", + "dependencies": { + "angular": "1.3.8", + "todomvc-common": "~0.3.0" + } +} diff --git a/examples/angularjs-perf/node_modules/angular/angular.js b/examples/angularjs-perf/bower_components/angular/angular.js similarity index 100% rename from examples/angularjs-perf/node_modules/angular/angular.js rename to examples/angularjs-perf/bower_components/angular/angular.js diff --git a/examples/angularjs-perf/bower_components/todomvc-common/base.css b/examples/angularjs-perf/bower_components/todomvc-common/base.css new file mode 100644 index 0000000000..285f531307 --- /dev/null +++ b/examples/angularjs-perf/bower_components/todomvc-common/base.css @@ -0,0 +1,554 @@ +html, +body { + margin: 0; + padding: 0; +} + +button { + margin: 0; + padding: 0; + border: 0; + background: none; + font-size: 100%; + vertical-align: baseline; + font-family: inherit; + color: inherit; + -webkit-appearance: none; + -ms-appearance: none; + -o-appearance: none; + appearance: none; +} + +body { + font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; + line-height: 1.4em; + background: #eaeaea url('bg.png'); + color: #4d4d4d; + width: 550px; + margin: 0 auto; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + -o-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +button, +input[type="checkbox"] { + outline: none; +} + +#todoapp { + background: #fff; + background: rgba(255, 255, 255, 0.9); + margin: 130px 0 40px 0; + border: 1px solid #ccc; + position: relative; + border-top-left-radius: 2px; + border-top-right-radius: 2px; + box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2), + 0 25px 50px 0 rgba(0, 0, 0, 0.15); +} + +#todoapp:before { + content: ''; + border-left: 1px solid #f5d6d6; + border-right: 1px solid #f5d6d6; + width: 2px; + position: absolute; + top: 0; + left: 40px; + height: 100%; +} + +#todoapp input::-webkit-input-placeholder { + font-style: italic; +} + +#todoapp input::-moz-placeholder { + font-style: italic; + color: #a9a9a9; +} + +#todoapp h1 { + position: absolute; + top: -120px; + width: 100%; + font-size: 70px; + font-weight: bold; + text-align: center; + color: #b3b3b3; + color: rgba(255, 255, 255, 0.3); + text-shadow: -1px -1px rgba(0, 0, 0, 0.2); + -webkit-text-rendering: optimizeLegibility; + -moz-text-rendering: optimizeLegibility; + -ms-text-rendering: optimizeLegibility; + -o-text-rendering: optimizeLegibility; + text-rendering: optimizeLegibility; +} + +#header { + padding-top: 15px; + border-radius: inherit; +} + +#header:before { + content: ''; + position: absolute; + top: 0; + right: 0; + left: 0; + height: 15px; + z-index: 2; + border-bottom: 1px solid #6c615c; + background: #8d7d77; + background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8))); + background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); + background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670'); + border-top-left-radius: 1px; + border-top-right-radius: 1px; +} + +#new-todo, +.edit { + position: relative; + margin: 0; + width: 100%; + font-size: 24px; + font-family: inherit; + line-height: 1.4em; + border: 0; + outline: none; + color: inherit; + padding: 6px; + border: 1px solid #999; + box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + -o-box-sizing: border-box; + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + -o-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +#new-todo { + padding: 16px 16px 16px 60px; + border: none; + background: rgba(0, 0, 0, 0.02); + z-index: 2; + box-shadow: none; +} + +#main { + position: relative; + z-index: 2; + border-top: 1px dotted #adadad; +} + +label[for='toggle-all'] { + display: none; +} + +#toggle-all { + position: absolute; + top: -42px; + left: -4px; + width: 40px; + text-align: center; + /* Mobile Safari */ + border: none; +} + +#toggle-all:before { + content: '»'; + font-size: 28px; + color: #d9d9d9; + padding: 0 25px 7px; +} + +#toggle-all:checked:before { + color: #737373; +} + +#todo-list { + margin: 0; + padding: 0; + list-style: none; +} + +#todo-list li { + position: relative; + font-size: 24px; + border-bottom: 1px dotted #ccc; +} + +#todo-list li:last-child { + border-bottom: none; +} + +#todo-list li.editing { + border-bottom: none; + padding: 0; +} + +#todo-list li.editing .edit { + display: block; + width: 506px; + padding: 13px 17px 12px 17px; + margin: 0 0 0 43px; +} + +#todo-list li.editing .view { + display: none; +} + +#todo-list li .toggle { + text-align: center; + width: 40px; + /* auto, since non-WebKit browsers doesn't support input styling */ + height: auto; + position: absolute; + top: 0; + bottom: 0; + margin: auto 0; + /* Mobile Safari */ + border: none; + -webkit-appearance: none; + -ms-appearance: none; + -o-appearance: none; + appearance: none; +} + +#todo-list li .toggle:after { + content: '✔'; + /* 40 + a couple of pixels visual adjustment */ + line-height: 43px; + font-size: 20px; + color: #d9d9d9; + text-shadow: 0 -1px 0 #bfbfbf; +} + +#todo-list li .toggle:checked:after { + color: #85ada7; + text-shadow: 0 1px 0 #669991; + bottom: 1px; + position: relative; +} + +#todo-list li label { + white-space: pre; + word-break: break-word; + padding: 15px 60px 15px 15px; + margin-left: 45px; + display: block; + line-height: 1.2; + -webkit-transition: color 0.4s; + transition: color 0.4s; +} + +#todo-list li.completed label { + color: #a9a9a9; + text-decoration: line-through; +} + +#todo-list li .destroy { + display: none; + position: absolute; + top: 0; + right: 10px; + bottom: 0; + width: 40px; + height: 40px; + margin: auto 0; + font-size: 22px; + color: #a88a8a; + -webkit-transition: all 0.2s; + transition: all 0.2s; +} + +#todo-list li .destroy:hover { + text-shadow: 0 0 1px #000, + 0 0 10px rgba(199, 107, 107, 0.8); + -webkit-transform: scale(1.3); + transform: scale(1.3); +} + +#todo-list li .destroy:after { + content: '✖'; +} + +#todo-list li:hover .destroy { + display: block; +} + +#todo-list li .edit { + display: none; +} + +#todo-list li.editing:last-child { + margin-bottom: -1px; +} + +#footer { + color: #777; + padding: 0 15px; + position: absolute; + right: 0; + bottom: -31px; + left: 0; + height: 20px; + z-index: 1; + text-align: center; +} + +#footer:before { + content: ''; + position: absolute; + right: 0; + bottom: 31px; + left: 0; + height: 50px; + z-index: -1; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), + 0 6px 0 -3px rgba(255, 255, 255, 0.8), + 0 7px 1px -3px rgba(0, 0, 0, 0.3), + 0 43px 0 -6px rgba(255, 255, 255, 0.8), + 0 44px 2px -6px rgba(0, 0, 0, 0.2); +} + +#todo-count { + float: left; + text-align: left; +} + +#filters { + margin: 0; + padding: 0; + list-style: none; + position: absolute; + right: 0; + left: 0; +} + +#filters li { + display: inline; +} + +#filters li a { + color: #83756f; + margin: 2px; + text-decoration: none; +} + +#filters li a.selected { + font-weight: bold; +} + +#clear-completed { + float: right; + position: relative; + line-height: 20px; + text-decoration: none; + background: rgba(0, 0, 0, 0.1); + font-size: 11px; + padding: 0 10px; + border-radius: 3px; + box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2); +} + +#clear-completed:hover { + background: rgba(0, 0, 0, 0.15); + box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3); +} + +#info { + margin: 65px auto 0; + color: #a6a6a6; + font-size: 12px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); + text-align: center; +} + +#info a { + color: inherit; +} + +/* + Hack to remove background from Mobile Safari. + Can't use it globally since it destroys checkboxes in Firefox and Opera +*/ + +@media screen and (-webkit-min-device-pixel-ratio:0) { + #toggle-all, + #todo-list li .toggle { + background: none; + } + + #todo-list li .toggle { + height: 40px; + } + + #toggle-all { + top: -56px; + left: -15px; + width: 65px; + height: 41px; + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + -webkit-appearance: none; + appearance: none; + } +} + +.hidden { + display: none; +} + +hr { + margin: 20px 0; + border: 0; + border-top: 1px dashed #C5C5C5; + border-bottom: 1px dashed #F7F7F7; +} + +.learn a { + font-weight: normal; + text-decoration: none; + color: #b83f45; +} + +.learn a:hover { + text-decoration: underline; + color: #787e7e; +} + +.learn h3, +.learn h4, +.learn h5 { + margin: 10px 0; + font-weight: 500; + line-height: 1.2; + color: #000; +} + +.learn h3 { + font-size: 24px; +} + +.learn h4 { + font-size: 18px; +} + +.learn h5 { + margin-bottom: 0; + font-size: 14px; +} + +.learn ul { + padding: 0; + margin: 0 0 30px 25px; +} + +.learn li { + line-height: 20px; +} + +.learn p { + font-size: 15px; + font-weight: 300; + line-height: 1.3; + margin-top: 0; + margin-bottom: 0; +} + +.quote { + border: none; + margin: 20px 0 60px 0; +} + +.quote p { + font-style: italic; +} + +.quote p:before { + content: '“'; + font-size: 50px; + opacity: .15; + position: absolute; + top: -20px; + left: 3px; +} + +.quote p:after { + content: '”'; + font-size: 50px; + opacity: .15; + position: absolute; + bottom: -42px; + right: 3px; +} + +.quote footer { + position: absolute; + bottom: -40px; + right: 0; +} + +.quote footer img { + border-radius: 3px; +} + +.quote footer a { + margin-left: 5px; + vertical-align: middle; +} + +.speech-bubble { + position: relative; + padding: 10px; + background: rgba(0, 0, 0, .04); + border-radius: 5px; +} + +.speech-bubble:after { + content: ''; + position: absolute; + top: 100%; + right: 30px; + border: 13px solid transparent; + border-top-color: rgba(0, 0, 0, .04); +} + +.learn-bar > .learn { + position: absolute; + width: 272px; + top: 8px; + left: -300px; + padding: 10px; + border-radius: 5px; + background-color: rgba(255, 255, 255, .6); + -webkit-transition-property: left; + transition-property: left; + -webkit-transition-duration: 500ms; + transition-duration: 500ms; +} + +@media (min-width: 899px) { + .learn-bar { + width: auto; + margin: 0 0 0 300px; + } + + .learn-bar > .learn { + left: 8px; + } + + .learn-bar #todoapp { + width: 550px; + margin: 130px auto 40px auto; + } +} diff --git a/examples/ariatemplates/node_modules/todomvc-common/base.js b/examples/angularjs-perf/bower_components/todomvc-common/base.js similarity index 87% rename from examples/ariatemplates/node_modules/todomvc-common/base.js rename to examples/angularjs-perf/bower_components/todomvc-common/base.js index 44fb50c613..d3a15127d8 100644 --- a/examples/ariatemplates/node_modules/todomvc-common/base.js +++ b/examples/angularjs-perf/bower_components/todomvc-common/base.js @@ -1,8 +1,6 @@ -/* global _ */ (function () { 'use strict'; - /* jshint ignore:start */ // Underscore's Template Module // Courtesy of underscorejs.org var _ = (function (_) { @@ -116,7 +114,6 @@ if (location.hostname === 'todomvc.com') { window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')); } - /* jshint ignore:end */ function redirect() { if (location.hostname === 'tastejs.github.io') { @@ -178,17 +175,13 @@ if (learnJSON.backend) { this.frameworkJSON = learnJSON.backend; - this.frameworkJSON.issueLabel = framework; this.append({ backend: true }); } else if (learnJSON[framework]) { this.frameworkJSON = learnJSON[framework]; - this.frameworkJSON.issueLabel = framework; this.append(); } - - this.fetchIssueCount(); } Learn.prototype.append = function (opts) { @@ -219,26 +212,6 @@ document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); }; - Learn.prototype.fetchIssueCount = function () { - var issueLink = document.getElementById('issue-count-link'); - if (issueLink) { - var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos'); - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.onload = function (e) { - var parsedResponse = JSON.parse(e.target.responseText); - if (parsedResponse instanceof Array) { - var count = parsedResponse.length - if (count !== 0) { - issueLink.innerHTML = 'This app has ' + count + ' open issues'; - document.getElementById('issue-count').style.display = 'inline'; - } - } - }; - xhr.send(); - } - }; - redirect(); getFile('learn.json', Learn); })(); diff --git a/examples/angularjs-perf/bower_components/todomvc-common/bg.png b/examples/angularjs-perf/bower_components/todomvc-common/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..b2a7600825ee11f21849ed475499c7d1ecbcc870 GIT binary patch literal 2126 zcmV-U2(kBxP)+9y`=HK7nt=~3t000O5Nklm=04hVa_lXl_bm=+M;=eI<%$rO2ta`suh*Sr#POw*?adq!O!CV z*0&b)rkCCEV)1nIuiLBntUH-MJI;&qYgz|d@Nhnn_abi2g`pu4NAMVid3hS%?quz~ zjlJf(x8cj{VS zUVEP1msg9`^OFUhSO5rtXHhRlyU2i>6$BT=glG`{JlctGHrwRIm)6Buu65jLQn^H8 zvl9lZqUBA+%qvM5XC+yY@mfA(YB~XP=e|6<{%$^eU8nrK?v2ccb@Jom5CG>rKAL7J zplHEIavVp|0p1&m!G`Ti?<7iZ;}@G7#Z)}Km%2!FHnQ0*&?PLR)G!HAaHgU#c|$dz zpj#0HmeadIe>`#)=Jjkb{B=FKnU3pc-&Y@j_j6(h4Y~+Uq!^J=LHunx1xi4QXK-Y{&MoT8ky6Vb9c|WhnOwF~c=3zOy8agktliS6# z`#8F`9H)D=bmk9B5MnW&_r#)f$c+;$LSr-@^An8dhc~Iquuv>jOK7pw7LJ;&X0i1C zGMsHdP1Os@ny$$j!>XAii%7bp5k>`pyNA!~epb)()p9zR4Yl6z=U}{CIdh1z(FpAo zQIW!;0zpCyC4*7YLkZCO@cul0R_&zghsA8Ek)z%jNpKa92{@NH+SstX%@}xB*Jk!l}PZ1cClIns~}5^!RncUk*rmA z%SIVgt58EQxLJ+OiFqKkBuwquyZLUp|gr zPUbUbFbBrPd1xO`^C*r-i3p9*F#(OBh!4Wy@aC&*!|O|GXcjDA&YhF{;cD7*o(GS^ z@pQSE7)x_81=Qyje6*LQ#TXu-7(o;S8u3tpDA0_ddj%hmCspeXdhYR}-i9A=C`EoChUYuH~^x!9+|&(Pgb*>Ck<=9j|)*@xyfT zpP#+Kt<}39b|3Wl4fxT(+G?aH>gG^d@MEaUOJRfy55TtFI7^Y)VuMU=7Pp0y55jS~ z+TJ)igMyrqkX;wU8j68iIDtqJYhS_D3_Oem-@g6me_RfiQ}uT_K-A-9qG%}gk3E9c z!8`KgsMNXm)beloPfMql*|&$M>1q`i!cY)RFYnjNYu*gSv@8XebV7%}xYL>6)GJPJ zJpA7K31lcJuIFKSw-{x4FX0K2Az)~Xg<`sOu$4|^-(^XJX4YzoiNRvL zyuY7~26w-P^Zw%6F}shBwV2$02c8RsSUy6dM92diCAxiX3IUqsq5ig6>U_!Vy*q5$ zjm}16tJr+QZ&T?HkkpeIwVX-r1EI=@%Zlt;6g*mj&E!A))%gXJ=x6u#l zcKEP>bx4rqV*k`BBrZ$Mqjt{TsHcxUH>;)iAK}B(Kila&VD%b?6m&^0WK4^&EZNAI z8AMYs_%$D%|M+@+cQqL-hi4yG>h*jwdii!W1{}p>ZhwFYt_4Su1kjY9<-5`!$VNOI1y(EDSH4WpCijE_>al!Nt=^eVER#uJ1=b z;BWF2IgyF5LexV+yec$Kin;Ai@myo;G>zJ&jrdW#ecXhIZph5OYqw_PEfcF56Z5Zu_ZZE#q3Mc+N&1O^7hoh9QR7`+L$cBP5pqG=fqA0uh zUBukaxTFmH)<|Xbvp2c=HSbNkpXw73a5lv7V$jb92#yZ141@$X?F}Jt8gIU7Wm6m9 z?e_;;zsnm6`LZeP>T=$)Kr>Z?kr*UmFqR7zx0C6^bmcsc@1AGtw_rNH>-Xm$d*|Q< zn&1Ln0u7=l&ILs>%CkJp`DiG9F18x4Ne+lg<#i?e7jL%x;4ZnRkN^Mx07*qoM6N<$ Ef(>0N!2kdN literal 0 HcmV?d00001 diff --git a/examples/angularjs-perf/bower_components/todomvc-common/bower.json b/examples/angularjs-perf/bower_components/todomvc-common/bower.json new file mode 100644 index 0000000000..f1665c5db0 --- /dev/null +++ b/examples/angularjs-perf/bower_components/todomvc-common/bower.json @@ -0,0 +1,22 @@ +{ + "name": "todomvc-common", + "description": "Common TodoMVC utilities used by our apps", + "license": "MIT", + "authors": [ + "TasteJS team" + ], + "main": [ + "base.js", + "base.css" + ], + "keywords": [ + "todomvc", + "tastejs", + "util", + "utilities" + ], + "ignore": [ + "package.json", + "readme.md" + ] +} diff --git a/examples/angularjs-perf/index.html b/examples/angularjs-perf/index.html index 36fc49c0b8..cb1511a354 100644 --- a/examples/angularjs-perf/index.html +++ b/examples/angularjs-perf/index.html @@ -3,8 +3,7 @@ AngularJS • TodoMVC - - + @@ -59,8 +58,8 @@

todos

Part of TodoMVC

- - + + diff --git a/examples/angularjs-perf/node_modules/todomvc-app-css/index.css b/examples/angularjs-perf/node_modules/todomvc-app-css/index.css deleted file mode 100644 index 4308848800..0000000000 --- a/examples/angularjs-perf/node_modules/todomvc-app-css/index.css +++ /dev/null @@ -1,394 +0,0 @@ -html, -body { - margin: 0; - padding: 0; -} - -button { - margin: 0; - padding: 0; - border: 0; - background: none; - font-size: 100%; - vertical-align: baseline; - font-family: inherit; - font-weight: inherit; - color: inherit; - -webkit-appearance: none; - -ms-appearance: none; - appearance: none; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; -} - -body { - font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; - line-height: 1.4em; - background: #f5f5f5; - color: #4d4d4d; - min-width: 230px; - max-width: 550px; - margin: 0 auto; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; - font-weight: 300; -} - -button, -input[type="checkbox"] { - outline: none; -} - -.hidden { - display: none; -} - -#todoapp { - background: #fff; - margin: 130px 0 40px 0; - position: relative; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), - 0 25px 50px 0 rgba(0, 0, 0, 0.1); -} - -#todoapp input::-webkit-input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp input::-moz-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp input::input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp h1 { - position: absolute; - top: -155px; - width: 100%; - font-size: 100px; - font-weight: 100; - text-align: center; - color: rgba(175, 47, 47, 0.15); - -webkit-text-rendering: optimizeLegibility; - -moz-text-rendering: optimizeLegibility; - -ms-text-rendering: optimizeLegibility; - text-rendering: optimizeLegibility; -} - -#new-todo, -.edit { - position: relative; - margin: 0; - width: 100%; - font-size: 24px; - font-family: inherit; - font-weight: inherit; - line-height: 1.4em; - border: 0; - outline: none; - color: inherit; - padding: 6px; - border: 1px solid #999; - box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); - -ms-box-sizing: border-box; - box-sizing: border-box; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; -} - -#new-todo { - padding: 16px 16px 16px 60px; - border: none; - background: rgba(0, 0, 0, 0.003); - box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); -} - -#main { - position: relative; - z-index: 2; - border-top: 1px solid #e6e6e6; -} - -label[for='toggle-all'] { - display: none; -} - -#toggle-all { - position: absolute; - top: -55px; - left: -12px; - width: 60px; - height: 34px; - text-align: center; - border: none; /* Mobile Safari */ -} - -#toggle-all:before { - content: '❯'; - font-size: 22px; - color: #e6e6e6; - padding: 10px 27px 10px 27px; -} - -#toggle-all:checked:before { - color: #737373; -} - -#todo-list { - margin: 0; - padding: 0; - list-style: none; -} - -#todo-list li { - position: relative; - font-size: 24px; - border-bottom: 1px solid #ededed; -} - -#todo-list li:last-child { - border-bottom: none; -} - -#todo-list li.editing { - border-bottom: none; - padding: 0; -} - -#todo-list li.editing .edit { - display: block; - width: 506px; - padding: 13px 17px 12px 17px; - margin: 0 0 0 43px; -} - -#todo-list li.editing .view { - display: none; -} - -#todo-list li .toggle { - text-align: center; - width: 40px; - /* auto, since non-WebKit browsers doesn't support input styling */ - height: auto; - position: absolute; - top: 0; - bottom: 0; - margin: auto 0; - border: none; /* Mobile Safari */ - -webkit-appearance: none; - -ms-appearance: none; - appearance: none; -} - -#todo-list li .toggle:after { - content: url('data:image/svg+xml;utf8,'); -} - -#todo-list li .toggle:checked:after { - content: url('data:image/svg+xml;utf8,'); -} - -#todo-list li label { - white-space: pre; - word-break: break-word; - padding: 15px 60px 15px 15px; - margin-left: 45px; - display: block; - line-height: 1.2; - transition: color 0.4s; -} - -#todo-list li.completed label { - color: #d9d9d9; - text-decoration: line-through; -} - -#todo-list li .destroy { - display: none; - position: absolute; - top: 0; - right: 10px; - bottom: 0; - width: 40px; - height: 40px; - margin: auto 0; - font-size: 30px; - color: #cc9a9a; - margin-bottom: 11px; - transition: color 0.2s ease-out; -} - -#todo-list li .destroy:hover { - color: #af5b5e; -} - -#todo-list li .destroy:after { - content: '×'; -} - -#todo-list li:hover .destroy { - display: block; -} - -#todo-list li .edit { - display: none; -} - -#todo-list li.editing:last-child { - margin-bottom: -1px; -} - -#footer { - color: #777; - padding: 10px 15px; - height: 20px; - text-align: center; - border-top: 1px solid #e6e6e6; -} - -#footer:before { - content: ''; - position: absolute; - right: 0; - bottom: 0; - left: 0; - height: 50px; - overflow: hidden; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), - 0 8px 0 -3px #f6f6f6, - 0 9px 1px -3px rgba(0, 0, 0, 0.2), - 0 16px 0 -6px #f6f6f6, - 0 17px 2px -6px rgba(0, 0, 0, 0.2); -} - -#todo-count { - float: left; - text-align: left; -} - -#todo-count strong { - font-weight: 300; -} - -#filters { - margin: 0; - padding: 0; - list-style: none; - position: absolute; - right: 0; - left: 0; -} - -#filters li { - display: inline; -} - -#filters li a { - color: inherit; - margin: 3px; - padding: 3px 7px; - text-decoration: none; - border: 1px solid transparent; - border-radius: 3px; -} - -#filters li a.selected, -#filters li a:hover { - border-color: rgba(175, 47, 47, 0.1); -} - -#filters li a.selected { - border-color: rgba(175, 47, 47, 0.2); -} - -#clear-completed, -html #clear-completed:active { - float: right; - position: relative; - line-height: 20px; - text-decoration: none; - cursor: pointer; - visibility: hidden; - position: relative; -} - -#clear-completed::after { - visibility: visible; - content: 'Clear completed'; - position: absolute; - right: 0; - white-space: nowrap; -} - -#clear-completed:hover::after { - text-decoration: underline; -} - -#info { - margin: 65px auto 0; - color: #bfbfbf; - font-size: 10px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - text-align: center; -} - -#info p { - line-height: 1; -} - -#info a { - color: inherit; - text-decoration: none; - font-weight: 400; -} - -#info a:hover { - text-decoration: underline; -} - -/* - Hack to remove background from Mobile Safari. - Can't use it globally since it destroys checkboxes in Firefox -*/ -@media screen and (-webkit-min-device-pixel-ratio:0) { - #toggle-all, - #todo-list li .toggle { - background: none; - } - - #todo-list li .toggle { - height: 40px; - } - - #toggle-all { - -webkit-transform: rotate(90deg); - transform: rotate(90deg); - -webkit-appearance: none; - appearance: none; - } -} - -@media (max-width: 430px) { - #footer { - height: 50px; - } - - #filters { - bottom: 10px; - } -} diff --git a/examples/angularjs-perf/node_modules/todomvc-common/base.css b/examples/angularjs-perf/node_modules/todomvc-common/base.css deleted file mode 100644 index da65968a73..0000000000 --- a/examples/angularjs-perf/node_modules/todomvc-common/base.css +++ /dev/null @@ -1,141 +0,0 @@ -hr { - margin: 20px 0; - border: 0; - border-top: 1px dashed #c5c5c5; - border-bottom: 1px dashed #f7f7f7; -} - -.learn a { - font-weight: normal; - text-decoration: none; - color: #b83f45; -} - -.learn a:hover { - text-decoration: underline; - color: #787e7e; -} - -.learn h3, -.learn h4, -.learn h5 { - margin: 10px 0; - font-weight: 500; - line-height: 1.2; - color: #000; -} - -.learn h3 { - font-size: 24px; -} - -.learn h4 { - font-size: 18px; -} - -.learn h5 { - margin-bottom: 0; - font-size: 14px; -} - -.learn ul { - padding: 0; - margin: 0 0 30px 25px; -} - -.learn li { - line-height: 20px; -} - -.learn p { - font-size: 15px; - font-weight: 300; - line-height: 1.3; - margin-top: 0; - margin-bottom: 0; -} - -#issue-count { - display: none; -} - -.quote { - border: none; - margin: 20px 0 60px 0; -} - -.quote p { - font-style: italic; -} - -.quote p:before { - content: '“'; - font-size: 50px; - opacity: .15; - position: absolute; - top: -20px; - left: 3px; -} - -.quote p:after { - content: '”'; - font-size: 50px; - opacity: .15; - position: absolute; - bottom: -42px; - right: 3px; -} - -.quote footer { - position: absolute; - bottom: -40px; - right: 0; -} - -.quote footer img { - border-radius: 3px; -} - -.quote footer a { - margin-left: 5px; - vertical-align: middle; -} - -.speech-bubble { - position: relative; - padding: 10px; - background: rgba(0, 0, 0, .04); - border-radius: 5px; -} - -.speech-bubble:after { - content: ''; - position: absolute; - top: 100%; - right: 30px; - border: 13px solid transparent; - border-top-color: rgba(0, 0, 0, .04); -} - -.learn-bar > .learn { - position: absolute; - width: 272px; - top: 8px; - left: -300px; - padding: 10px; - border-radius: 5px; - background-color: rgba(255, 255, 255, .6); - transition-property: left; - transition-duration: 500ms; -} - -@media (min-width: 899px) { - .learn-bar { - width: auto; - padding-left: 300px; - } - - .learn-bar > .learn { - left: 8px; - } -} diff --git a/examples/angularjs-perf/package.json b/examples/angularjs-perf/package.json deleted file mode 100644 index 21f5c3df5b..0000000000 --- a/examples/angularjs-perf/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "private": true, - "dependencies": { - "angular": "1.3.8", - "todomvc-common": "^1.0.1", - "todomvc-app-css": "^1.0.1" - } -} diff --git a/examples/ariatemplates/.gitignore b/examples/ariatemplates/.gitignore deleted file mode 100644 index c85575bcf7..0000000000 --- a/examples/ariatemplates/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules/todomvc-app-css -!node_modules/todomvc-app-css/index.css - -node_modules/todomvc-common -!node_modules/todomvc-common/base.js -!node_modules/todomvc-common/base.css diff --git a/examples/ariatemplates/bower.json b/examples/ariatemplates/bower.json new file mode 100644 index 0000000000..eb0398e051 --- /dev/null +++ b/examples/ariatemplates/bower.json @@ -0,0 +1,7 @@ +{ + "name": "todomvc-ariatemplates", + "version": "0.0.0", + "dependencies": { + "todomvc-common": "~0.3.0" + } +} diff --git a/examples/ariatemplates/bower_components/todomvc-common/base.css b/examples/ariatemplates/bower_components/todomvc-common/base.css new file mode 100644 index 0000000000..285f531307 --- /dev/null +++ b/examples/ariatemplates/bower_components/todomvc-common/base.css @@ -0,0 +1,554 @@ +html, +body { + margin: 0; + padding: 0; +} + +button { + margin: 0; + padding: 0; + border: 0; + background: none; + font-size: 100%; + vertical-align: baseline; + font-family: inherit; + color: inherit; + -webkit-appearance: none; + -ms-appearance: none; + -o-appearance: none; + appearance: none; +} + +body { + font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; + line-height: 1.4em; + background: #eaeaea url('bg.png'); + color: #4d4d4d; + width: 550px; + margin: 0 auto; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + -o-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +button, +input[type="checkbox"] { + outline: none; +} + +#todoapp { + background: #fff; + background: rgba(255, 255, 255, 0.9); + margin: 130px 0 40px 0; + border: 1px solid #ccc; + position: relative; + border-top-left-radius: 2px; + border-top-right-radius: 2px; + box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2), + 0 25px 50px 0 rgba(0, 0, 0, 0.15); +} + +#todoapp:before { + content: ''; + border-left: 1px solid #f5d6d6; + border-right: 1px solid #f5d6d6; + width: 2px; + position: absolute; + top: 0; + left: 40px; + height: 100%; +} + +#todoapp input::-webkit-input-placeholder { + font-style: italic; +} + +#todoapp input::-moz-placeholder { + font-style: italic; + color: #a9a9a9; +} + +#todoapp h1 { + position: absolute; + top: -120px; + width: 100%; + font-size: 70px; + font-weight: bold; + text-align: center; + color: #b3b3b3; + color: rgba(255, 255, 255, 0.3); + text-shadow: -1px -1px rgba(0, 0, 0, 0.2); + -webkit-text-rendering: optimizeLegibility; + -moz-text-rendering: optimizeLegibility; + -ms-text-rendering: optimizeLegibility; + -o-text-rendering: optimizeLegibility; + text-rendering: optimizeLegibility; +} + +#header { + padding-top: 15px; + border-radius: inherit; +} + +#header:before { + content: ''; + position: absolute; + top: 0; + right: 0; + left: 0; + height: 15px; + z-index: 2; + border-bottom: 1px solid #6c615c; + background: #8d7d77; + background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8))); + background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); + background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670'); + border-top-left-radius: 1px; + border-top-right-radius: 1px; +} + +#new-todo, +.edit { + position: relative; + margin: 0; + width: 100%; + font-size: 24px; + font-family: inherit; + line-height: 1.4em; + border: 0; + outline: none; + color: inherit; + padding: 6px; + border: 1px solid #999; + box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + -o-box-sizing: border-box; + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + -o-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +#new-todo { + padding: 16px 16px 16px 60px; + border: none; + background: rgba(0, 0, 0, 0.02); + z-index: 2; + box-shadow: none; +} + +#main { + position: relative; + z-index: 2; + border-top: 1px dotted #adadad; +} + +label[for='toggle-all'] { + display: none; +} + +#toggle-all { + position: absolute; + top: -42px; + left: -4px; + width: 40px; + text-align: center; + /* Mobile Safari */ + border: none; +} + +#toggle-all:before { + content: '»'; + font-size: 28px; + color: #d9d9d9; + padding: 0 25px 7px; +} + +#toggle-all:checked:before { + color: #737373; +} + +#todo-list { + margin: 0; + padding: 0; + list-style: none; +} + +#todo-list li { + position: relative; + font-size: 24px; + border-bottom: 1px dotted #ccc; +} + +#todo-list li:last-child { + border-bottom: none; +} + +#todo-list li.editing { + border-bottom: none; + padding: 0; +} + +#todo-list li.editing .edit { + display: block; + width: 506px; + padding: 13px 17px 12px 17px; + margin: 0 0 0 43px; +} + +#todo-list li.editing .view { + display: none; +} + +#todo-list li .toggle { + text-align: center; + width: 40px; + /* auto, since non-WebKit browsers doesn't support input styling */ + height: auto; + position: absolute; + top: 0; + bottom: 0; + margin: auto 0; + /* Mobile Safari */ + border: none; + -webkit-appearance: none; + -ms-appearance: none; + -o-appearance: none; + appearance: none; +} + +#todo-list li .toggle:after { + content: '✔'; + /* 40 + a couple of pixels visual adjustment */ + line-height: 43px; + font-size: 20px; + color: #d9d9d9; + text-shadow: 0 -1px 0 #bfbfbf; +} + +#todo-list li .toggle:checked:after { + color: #85ada7; + text-shadow: 0 1px 0 #669991; + bottom: 1px; + position: relative; +} + +#todo-list li label { + white-space: pre; + word-break: break-word; + padding: 15px 60px 15px 15px; + margin-left: 45px; + display: block; + line-height: 1.2; + -webkit-transition: color 0.4s; + transition: color 0.4s; +} + +#todo-list li.completed label { + color: #a9a9a9; + text-decoration: line-through; +} + +#todo-list li .destroy { + display: none; + position: absolute; + top: 0; + right: 10px; + bottom: 0; + width: 40px; + height: 40px; + margin: auto 0; + font-size: 22px; + color: #a88a8a; + -webkit-transition: all 0.2s; + transition: all 0.2s; +} + +#todo-list li .destroy:hover { + text-shadow: 0 0 1px #000, + 0 0 10px rgba(199, 107, 107, 0.8); + -webkit-transform: scale(1.3); + transform: scale(1.3); +} + +#todo-list li .destroy:after { + content: '✖'; +} + +#todo-list li:hover .destroy { + display: block; +} + +#todo-list li .edit { + display: none; +} + +#todo-list li.editing:last-child { + margin-bottom: -1px; +} + +#footer { + color: #777; + padding: 0 15px; + position: absolute; + right: 0; + bottom: -31px; + left: 0; + height: 20px; + z-index: 1; + text-align: center; +} + +#footer:before { + content: ''; + position: absolute; + right: 0; + bottom: 31px; + left: 0; + height: 50px; + z-index: -1; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), + 0 6px 0 -3px rgba(255, 255, 255, 0.8), + 0 7px 1px -3px rgba(0, 0, 0, 0.3), + 0 43px 0 -6px rgba(255, 255, 255, 0.8), + 0 44px 2px -6px rgba(0, 0, 0, 0.2); +} + +#todo-count { + float: left; + text-align: left; +} + +#filters { + margin: 0; + padding: 0; + list-style: none; + position: absolute; + right: 0; + left: 0; +} + +#filters li { + display: inline; +} + +#filters li a { + color: #83756f; + margin: 2px; + text-decoration: none; +} + +#filters li a.selected { + font-weight: bold; +} + +#clear-completed { + float: right; + position: relative; + line-height: 20px; + text-decoration: none; + background: rgba(0, 0, 0, 0.1); + font-size: 11px; + padding: 0 10px; + border-radius: 3px; + box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2); +} + +#clear-completed:hover { + background: rgba(0, 0, 0, 0.15); + box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3); +} + +#info { + margin: 65px auto 0; + color: #a6a6a6; + font-size: 12px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); + text-align: center; +} + +#info a { + color: inherit; +} + +/* + Hack to remove background from Mobile Safari. + Can't use it globally since it destroys checkboxes in Firefox and Opera +*/ + +@media screen and (-webkit-min-device-pixel-ratio:0) { + #toggle-all, + #todo-list li .toggle { + background: none; + } + + #todo-list li .toggle { + height: 40px; + } + + #toggle-all { + top: -56px; + left: -15px; + width: 65px; + height: 41px; + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + -webkit-appearance: none; + appearance: none; + } +} + +.hidden { + display: none; +} + +hr { + margin: 20px 0; + border: 0; + border-top: 1px dashed #C5C5C5; + border-bottom: 1px dashed #F7F7F7; +} + +.learn a { + font-weight: normal; + text-decoration: none; + color: #b83f45; +} + +.learn a:hover { + text-decoration: underline; + color: #787e7e; +} + +.learn h3, +.learn h4, +.learn h5 { + margin: 10px 0; + font-weight: 500; + line-height: 1.2; + color: #000; +} + +.learn h3 { + font-size: 24px; +} + +.learn h4 { + font-size: 18px; +} + +.learn h5 { + margin-bottom: 0; + font-size: 14px; +} + +.learn ul { + padding: 0; + margin: 0 0 30px 25px; +} + +.learn li { + line-height: 20px; +} + +.learn p { + font-size: 15px; + font-weight: 300; + line-height: 1.3; + margin-top: 0; + margin-bottom: 0; +} + +.quote { + border: none; + margin: 20px 0 60px 0; +} + +.quote p { + font-style: italic; +} + +.quote p:before { + content: '“'; + font-size: 50px; + opacity: .15; + position: absolute; + top: -20px; + left: 3px; +} + +.quote p:after { + content: '”'; + font-size: 50px; + opacity: .15; + position: absolute; + bottom: -42px; + right: 3px; +} + +.quote footer { + position: absolute; + bottom: -40px; + right: 0; +} + +.quote footer img { + border-radius: 3px; +} + +.quote footer a { + margin-left: 5px; + vertical-align: middle; +} + +.speech-bubble { + position: relative; + padding: 10px; + background: rgba(0, 0, 0, .04); + border-radius: 5px; +} + +.speech-bubble:after { + content: ''; + position: absolute; + top: 100%; + right: 30px; + border: 13px solid transparent; + border-top-color: rgba(0, 0, 0, .04); +} + +.learn-bar > .learn { + position: absolute; + width: 272px; + top: 8px; + left: -300px; + padding: 10px; + border-radius: 5px; + background-color: rgba(255, 255, 255, .6); + -webkit-transition-property: left; + transition-property: left; + -webkit-transition-duration: 500ms; + transition-duration: 500ms; +} + +@media (min-width: 899px) { + .learn-bar { + width: auto; + margin: 0 0 0 300px; + } + + .learn-bar > .learn { + left: 8px; + } + + .learn-bar #todoapp { + width: 550px; + margin: 130px auto 40px auto; + } +} diff --git a/examples/batman/node_modules/todomvc-common/base.js b/examples/ariatemplates/bower_components/todomvc-common/base.js similarity index 87% rename from examples/batman/node_modules/todomvc-common/base.js rename to examples/ariatemplates/bower_components/todomvc-common/base.js index 44fb50c613..d3a15127d8 100644 --- a/examples/batman/node_modules/todomvc-common/base.js +++ b/examples/ariatemplates/bower_components/todomvc-common/base.js @@ -1,8 +1,6 @@ -/* global _ */ (function () { 'use strict'; - /* jshint ignore:start */ // Underscore's Template Module // Courtesy of underscorejs.org var _ = (function (_) { @@ -116,7 +114,6 @@ if (location.hostname === 'todomvc.com') { window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')); } - /* jshint ignore:end */ function redirect() { if (location.hostname === 'tastejs.github.io') { @@ -178,17 +175,13 @@ if (learnJSON.backend) { this.frameworkJSON = learnJSON.backend; - this.frameworkJSON.issueLabel = framework; this.append({ backend: true }); } else if (learnJSON[framework]) { this.frameworkJSON = learnJSON[framework]; - this.frameworkJSON.issueLabel = framework; this.append(); } - - this.fetchIssueCount(); } Learn.prototype.append = function (opts) { @@ -219,26 +212,6 @@ document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); }; - Learn.prototype.fetchIssueCount = function () { - var issueLink = document.getElementById('issue-count-link'); - if (issueLink) { - var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos'); - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.onload = function (e) { - var parsedResponse = JSON.parse(e.target.responseText); - if (parsedResponse instanceof Array) { - var count = parsedResponse.length - if (count !== 0) { - issueLink.innerHTML = 'This app has ' + count + ' open issues'; - document.getElementById('issue-count').style.display = 'inline'; - } - } - }; - xhr.send(); - } - }; - redirect(); getFile('learn.json', Learn); })(); diff --git a/examples/ariatemplates/bower_components/todomvc-common/bg.png b/examples/ariatemplates/bower_components/todomvc-common/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..b2a7600825ee11f21849ed475499c7d1ecbcc870 GIT binary patch literal 2126 zcmV-U2(kBxP)+9y`=HK7nt=~3t000O5Nklm=04hVa_lXl_bm=+M;=eI<%$rO2ta`suh*Sr#POw*?adq!O!CV z*0&b)rkCCEV)1nIuiLBntUH-MJI;&qYgz|d@Nhnn_abi2g`pu4NAMVid3hS%?quz~ zjlJf(x8cj{VS zUVEP1msg9`^OFUhSO5rtXHhRlyU2i>6$BT=glG`{JlctGHrwRIm)6Buu65jLQn^H8 zvl9lZqUBA+%qvM5XC+yY@mfA(YB~XP=e|6<{%$^eU8nrK?v2ccb@Jom5CG>rKAL7J zplHEIavVp|0p1&m!G`Ti?<7iZ;}@G7#Z)}Km%2!FHnQ0*&?PLR)G!HAaHgU#c|$dz zpj#0HmeadIe>`#)=Jjkb{B=FKnU3pc-&Y@j_j6(h4Y~+Uq!^J=LHunx1xi4QXK-Y{&MoT8ky6Vb9c|WhnOwF~c=3zOy8agktliS6# z`#8F`9H)D=bmk9B5MnW&_r#)f$c+;$LSr-@^An8dhc~Iquuv>jOK7pw7LJ;&X0i1C zGMsHdP1Os@ny$$j!>XAii%7bp5k>`pyNA!~epb)()p9zR4Yl6z=U}{CIdh1z(FpAo zQIW!;0zpCyC4*7YLkZCO@cul0R_&zghsA8Ek)z%jNpKa92{@NH+SstX%@}xB*Jk!l}PZ1cClIns~}5^!RncUk*rmA z%SIVgt58EQxLJ+OiFqKkBuwquyZLUp|gr zPUbUbFbBrPd1xO`^C*r-i3p9*F#(OBh!4Wy@aC&*!|O|GXcjDA&YhF{;cD7*o(GS^ z@pQSE7)x_81=Qyje6*LQ#TXu-7(o;S8u3tpDA0_ddj%hmCspeXdhYR}-i9A=C`EoChUYuH~^x!9+|&(Pgb*>Ck<=9j|)*@xyfT zpP#+Kt<}39b|3Wl4fxT(+G?aH>gG^d@MEaUOJRfy55TtFI7^Y)VuMU=7Pp0y55jS~ z+TJ)igMyrqkX;wU8j68iIDtqJYhS_D3_Oem-@g6me_RfiQ}uT_K-A-9qG%}gk3E9c z!8`KgsMNXm)beloPfMql*|&$M>1q`i!cY)RFYnjNYu*gSv@8XebV7%}xYL>6)GJPJ zJpA7K31lcJuIFKSw-{x4FX0K2Az)~Xg<`sOu$4|^-(^XJX4YzoiNRvL zyuY7~26w-P^Zw%6F}shBwV2$02c8RsSUy6dM92diCAxiX3IUqsq5ig6>U_!Vy*q5$ zjm}16tJr+QZ&T?HkkpeIwVX-r1EI=@%Zlt;6g*mj&E!A))%gXJ=x6u#l zcKEP>bx4rqV*k`BBrZ$Mqjt{TsHcxUH>;)iAK}B(Kila&VD%b?6m&^0WK4^&EZNAI z8AMYs_%$D%|M+@+cQqL-hi4yG>h*jwdii!W1{}p>ZhwFYt_4Su1kjY9<-5`!$VNOI1y(EDSH4WpCijE_>al!Nt=^eVER#uJ1=b z;BWF2IgyF5LexV+yec$Kin;Ai@myo;G>zJ&jrdW#ecXhIZph5OYqw_PEfcF56Z5Zu_ZZE#q3Mc+N&1O^7hoh9QR7`+L$cBP5pqG=fqA0uh zUBukaxTFmH)<|Xbvp2c=HSbNkpXw73a5lv7V$jb92#yZ141@$X?F}Jt8gIU7Wm6m9 z?e_;;zsnm6`LZeP>T=$)Kr>Z?kr*UmFqR7zx0C6^bmcsc@1AGtw_rNH>-Xm$d*|Q< zn&1Ln0u7=l&ILs>%CkJp`DiG9F18x4Ne+lg<#i?e7jL%x;4ZnRkN^Mx07*qoM6N<$ Ef(>0N!2kdN literal 0 HcmV?d00001 diff --git a/examples/ariatemplates/index.html b/examples/ariatemplates/index.html index 0187155873..5352792d76 100644 --- a/examples/ariatemplates/index.html +++ b/examples/ariatemplates/index.html @@ -3,14 +3,14 @@ Aria Templates • TodoMVC - - + +
- + - + + "; + return testEl.firstChild.innerHTML === ''; + })(); - ## Removing Objects + // IE 8 (and likely earlier) likes to move whitespace preceeding + // a script tag to appear after it. This means that we can + // accidentally remove whitespace when updating a morph. + var movesWhitespace = typeof document !== 'undefined' && (function() { + var testEl = document.createElement('div'); + testEl.innerHTML = "Test: Value"; + return testEl.childNodes[0].nodeValue === 'Test:' && + testEl.childNodes[2].nodeValue === ' Value'; + })(); - To remove an object from an enumerable, use the `removeObject()` method. This - will only remove the object if it is present in the enumerable, otherwise - this method has no effect. + // Use this to find children by ID instead of using jQuery + var findChildById = function(element, id) { + if (element.getAttribute('id') === id) { return element; } - ```javascript - set.removeObject(contact); - ``` + var len = element.childNodes.length, idx, node, found; + for (idx=0; idx 0) { + var len = matches.length, idx; + for (idx=0; idxTest'); + canSet = el.options.length === 1; + } - Attempts to remove the passed object from the receiver collection if the - object is present in the collection. If the object is not present, - this method has no effect. + innerHTMLTags[tagName] = canSet; - If the passed object is of a type not supported by the receiver, - then this method should raise an exception. + return canSet; + }; - @method removeObject - @param {Object} object The object to remove from the enumerable. - @return {Object} the passed object - */ - removeObject: required(Function), + var setInnerHTML = function(element, html) { + var tagName = element.tagName; + if (canSetInnerHTML(tagName)) { + setInnerHTMLWithoutFix(element, html); + } else { + // Firefox versions < 11 do not have support for element.outerHTML. + var outerHTML = element.outerHTML || new XMLSerializer().serializeToString(element); + Ember.assert("Can't set innerHTML on "+element.tagName+" in this browser", outerHTML); - /** - Removes each object in the passed enumerable from the receiver. + var startTag = outerHTML.match(new RegExp("<"+tagName+"([^>]*)>", 'i'))[0], + endTag = ''; - @method removeObjects - @param {Ember.Enumerable} objects the objects to remove - @return {Object} receiver - */ - removeObjects: function(objects) { - beginPropertyChanges(this); - for (var i = objects.length - 1; i >= 0; i--) { - this.removeObject(objects[i]); + var wrapper = document.createElement('div'); + setInnerHTMLWithoutFix(wrapper, startTag + html + endTag); + element = wrapper.firstChild; + while (element.tagName !== tagName) { + element = element.nextSibling; } - endPropertyChanges(this); - return this; } - }); + + return element; + }; + + function isSimpleClick(event) { + var modifier = event.shiftKey || event.metaKey || event.altKey || event.ctrlKey, + secondaryClick = event.which > 1; // IE9 may return undefined + + return !modifier && !secondaryClick; + } + + __exports__.setInnerHTML = setInnerHTML; + __exports__.isSimpleClick = isSimpleClick; }); -enifed("ember-runtime/mixins/observable", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/get_properties","ember-metal/set_properties","ember-metal/mixin","ember-metal/events","ember-metal/property_events","ember-metal/observer","ember-metal/computed","ember-metal/is_none","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __exports__) { +define("ember-views/views/collection_view", + ["ember-metal/core","ember-metal/platform","ember-metal/binding","ember-metal/merge","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/string","ember-views/views/container_view","ember-views/views/view","ember-metal/mixin","ember-runtime/mixins/array","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { "use strict"; + /** @module ember - @submodule ember-runtime + @submodule ember-views */ + var Ember = __dependency1__["default"]; // Ember.assert + var create = __dependency2__.create; + var isGlobalPath = __dependency3__.isGlobalPath; + var merge = __dependency4__["default"]; + var get = __dependency5__.get; + var set = __dependency6__.set; + var fmt = __dependency7__.fmt; + var ContainerView = __dependency8__["default"]; + var CoreView = __dependency9__.CoreView; + var View = __dependency9__.View; + var observer = __dependency10__.observer; + var beforeObserver = __dependency10__.beforeObserver; + var EmberArray = __dependency11__["default"]; - var get = __dependency2__.get; - var getWithDefault = __dependency2__.getWithDefault; - var set = __dependency3__.set; - var apply = __dependency4__.apply; - var getProperties = __dependency5__["default"]; - var setProperties = __dependency6__["default"]; - var Mixin = __dependency7__.Mixin; - var hasListeners = __dependency8__.hasListeners; - var beginPropertyChanges = __dependency9__.beginPropertyChanges; - var propertyWillChange = __dependency9__.propertyWillChange; - var propertyDidChange = __dependency9__.propertyDidChange; - var endPropertyChanges = __dependency9__.endPropertyChanges; - var addObserver = __dependency10__.addObserver; - var addBeforeObserver = __dependency10__.addBeforeObserver; - var removeObserver = __dependency10__.removeObserver; - var observersFor = __dependency10__.observersFor; - var cacheFor = __dependency11__.cacheFor; - var isNone = __dependency12__["default"]; - - - var slice = Array.prototype.slice; /** - ## Overview - - This mixin provides properties and property observing functionality, core - features of the Ember object model. - - Properties and observers allow one object to observe changes to a - property on another object. This is one of the fundamental ways that - models, controllers and views communicate with each other in an Ember - application. + `Ember.CollectionView` is an `Ember.View` descendent responsible for managing + a collection (an array or array-like object) by maintaining a child view object + and associated DOM representation for each item in the array and ensuring + that child views and their associated rendered HTML are updated when items in + the array are added, removed, or replaced. - Any object that has this mixin applied can be used in observer - operations. That includes `Ember.Object` and most objects you will - interact with as you write your Ember application. + ## Setting content - Note that you will not generally apply this mixin to classes yourself, - but you will use the features provided by this module frequently, so it - is important to understand how to use it. + The managed collection of objects is referenced as the `Ember.CollectionView` + instance's `content` property. - ## Using `get()` and `set()` + ```javascript + someItemsView = Ember.CollectionView.create({ + content: ['A', 'B','C'] + }) + ``` - Because of Ember's support for bindings and observers, you will always - access properties using the get method, and set properties using the - set method. This allows the observing objects to be notified and - computed properties to be handled properly. + The view for each item in the collection will have its `content` property set + to the item. - More documentation about `get` and `set` are below. + ## Specifying itemViewClass - ## Observing Property Changes + By default the view class for each item in the managed collection will be an + instance of `Ember.View`. You can supply a different class by setting the + `CollectionView`'s `itemViewClass` property. - You typically observe property changes simply by adding the `observes` - call to the end of your method declarations in classes that you write. - For example: + Given an empty `` and the following code: ```javascript - Ember.Object.extend({ - valueObserver: function() { - // Executes whenever the "value" property changes - }.observes('value') + someItemsView = Ember.CollectionView.create({ + classNames: ['a-collection'], + content: ['A','B','C'], + itemViewClass: Ember.View.extend({ + template: Ember.Handlebars.compile("the letter: {{view.content}}") + }) }); - ``` - - Although this is the most common way to add an observer, this capability - is actually built into the `Ember.Object` class on top of two methods - defined in this mixin: `addObserver` and `removeObserver`. You can use - these two methods to add and remove observers yourself if you need to - do so at runtime. - - To add an observer for a property, call: - ```javascript - object.addObserver('propertyKey', targetObject, targetAction) + someItemsView.appendTo('body'); ``` - This will call the `targetAction` method on the `targetObject` whenever - the value of the `propertyKey` changes. - - Note that if `propertyKey` is a computed property, the observer will be - called when any of the property dependencies are changed, even if the - resulting value of the computed property is unchanged. This is necessary - because computed properties are not computed until `get` is called. - - @class Observable - @namespace Ember - */ - __exports__["default"] = Mixin.create({ - - /** - Retrieves the value of a property from the object. - - This method is usually similar to using `object[keyName]` or `object.keyName`, - however it supports both computed properties and the unknownProperty - handler. - - Because `get` unifies the syntax for accessing all these kinds - of properties, it can make many refactorings easier, such as replacing a - simple property with a computed property, or vice versa. - - ### Computed Properties - - Computed properties are methods defined with the `property` modifier - declared at the end, such as: + Will result in the following HTML structure - ```javascript - fullName: function() { - return this.get('firstName') + ' ' + this.get('lastName'); - }.property('firstName', 'lastName') - ``` + ```html +
+
the letter: A
+
the letter: B
+
the letter: C
+
+ ``` - When you call `get` on a computed property, the function will be - called and the return value will be returned instead of the function - itself. + ## Automatic matching of parent/child tagNames - ### Unknown Properties + Setting the `tagName` property of a `CollectionView` to any of + "ul", "ol", "table", "thead", "tbody", "tfoot", "tr", or "select" will result + in the item views receiving an appropriately matched `tagName` property. - Likewise, if you try to call `get` on a property whose value is - `undefined`, the `unknownProperty()` method will be called on the object. - If this method returns any value other than `undefined`, it will be returned - instead. This allows you to implement "virtual" properties that are - not defined upfront. + Given an empty `` and the following code: - @method get - @param {String} keyName The property to retrieve - @return {Object} The property value or undefined. - */ - get: function(keyName) { - return get(this, keyName); - }, + ```javascript + anUnorderedListView = Ember.CollectionView.create({ + tagName: 'ul', + content: ['A','B','C'], + itemViewClass: Ember.View.extend({ + template: Ember.Handlebars.compile("the letter: {{view.content}}") + }) + }); - /** - To get the values of multiple properties at once, call `getProperties` - with a list of strings or an array: + anUnorderedListView.appendTo('body'); + ``` - ```javascript - record.getProperties('firstName', 'lastName', 'zipCode'); - // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } - ``` + Will result in the following HTML structure - is equivalent to: + ```html +
    +
  • the letter: A
  • +
  • the letter: B
  • +
  • the letter: C
  • +
+ ``` - ```javascript - record.getProperties(['firstName', 'lastName', 'zipCode']); - // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } - ``` + Additional `tagName` pairs can be provided by adding to + `Ember.CollectionView.CONTAINER_MAP ` - @method getProperties - @param {String...|Array} list of keys to get - @return {Hash} - */ - getProperties: function() { - return apply(null, getProperties, [this].concat(slice.call(arguments))); - }, + ```javascript + Ember.CollectionView.CONTAINER_MAP['article'] = 'section' + ``` - /** - Sets the provided key or path to the value. + ## Programmatic creation of child views - This method is generally very similar to calling `object[key] = value` or - `object.key = value`, except that it provides support for computed - properties, the `setUnknownProperty()` method and property observers. + For cases where additional customization beyond the use of a single + `itemViewClass` or `tagName` matching is required CollectionView's + `createChildView` method can be overidden: - ### Computed Properties + ```javascript + CustomCollectionView = Ember.CollectionView.extend({ + createChildView: function(viewClass, attrs) { + if (attrs.content.kind == 'album') { + viewClass = App.AlbumView; + } else { + viewClass = App.SongView; + } + return this._super(viewClass, attrs); + } + }); + ``` - If you try to set a value on a key that has a computed property handler - defined (see the `get()` method for an example), then `set()` will call - that method, passing both the value and key instead of simply changing - the value itself. This is useful for those times when you need to - implement a property that is composed of one or more member - properties. + ## Empty View - ### Unknown Properties + You can provide an `Ember.View` subclass to the `Ember.CollectionView` + instance as its `emptyView` property. If the `content` property of a + `CollectionView` is set to `null` or an empty array, an instance of this view + will be the `CollectionView`s only child. - If you try to set a value on a key that is undefined in the target - object, then the `setUnknownProperty()` handler will be called instead. This - gives you an opportunity to implement complex "virtual" properties that - are not predefined on the object. If `setUnknownProperty()` returns - undefined, then `set()` will simply set the value on the object. + ```javascript + aListWithNothing = Ember.CollectionView.create({ + classNames: ['nothing'] + content: null, + emptyView: Ember.View.extend({ + template: Ember.Handlebars.compile("The collection is empty") + }) + }); - ### Property Observers + aListWithNothing.appendTo('body'); + ``` - In addition to changing the property, `set()` will also register a property - change with the object. Unless you have placed this call inside of a - `beginPropertyChanges()` and `endPropertyChanges(),` any "local" observers - (i.e. observer methods declared on the same object), will be called - immediately. Any "remote" observers (i.e. observer methods declared on - another object) will be placed in a queue and called at a later time in a - coalesced manner. + Will result in the following HTML structure - ### Chaining + ```html +
+
+ The collection is empty +
+
+ ``` - In addition to property changes, `set()` returns the value of the object - itself so you can do chaining like this: + ## Adding and Removing items - ```javascript - record.set('firstName', 'Charles').set('lastName', 'Jolley'); - ``` + The `childViews` property of a `CollectionView` should not be directly + manipulated. Instead, add, remove, replace items from its `content` property. + This will trigger appropriate changes to its rendered HTML. - @method set - @param {String} keyName The property to set - @param {Object} value The value to set or `null`. - @return {Ember.Observable} - */ - set: function(keyName, value) { - set(this, keyName, value); - return this; - }, + @class CollectionView + @namespace Ember + @extends Ember.ContainerView + @since Ember 0.9 + */ + var CollectionView = ContainerView.extend({ /** - Sets a list of properties at once. These properties are set inside - a single `beginPropertyChanges` and `endPropertyChanges` batch, so - observers will be buffered. - - ```javascript - record.setProperties({ firstName: 'Charles', lastName: 'Jolley' }); - ``` + A list of items to be displayed by the `Ember.CollectionView`. - @method setProperties - @param {Hash} hash the hash of keys and values to set - @return {Ember.Observable} + @property content + @type Ember.Array + @default null */ - setProperties: function(hash) { - return setProperties(this, hash); - }, + content: null, /** - Begins a grouping of property changes. + This provides metadata about what kind of empty view class this + collection would like if it is being instantiated from another + system (like Handlebars) - You can use this method to group property changes so that notifications - will not be sent until the changes are finished. If you plan to make a - large number of changes to an object at one time, you should call this - method at the beginning of the changes to begin deferring change - notifications. When you are done making changes, call - `endPropertyChanges()` to deliver the deferred change notifications and end - deferring. + @private + @property emptyViewClass + */ + emptyViewClass: View, - @method beginPropertyChanges - @return {Ember.Observable} + /** + An optional view to display if content is set to an empty array. + + @property emptyView + @type Ember.View + @default null */ - beginPropertyChanges: function() { - beginPropertyChanges(); - return this; - }, + emptyView: null, /** - Ends a grouping of property changes. + @property itemViewClass + @type Ember.View + @default Ember.View + */ + itemViewClass: View, - You can use this method to group property changes so that notifications - will not be sent until the changes are finished. If you plan to make a - large number of changes to an object at one time, you should call - `beginPropertyChanges()` at the beginning of the changes to defer change - notifications. When you are done making changes, call this method to - deliver the deferred change notifications and end deferring. + /** + Setup a CollectionView - @method endPropertyChanges - @return {Ember.Observable} + @method init */ - endPropertyChanges: function() { - endPropertyChanges(); - return this; + init: function() { + var ret = this._super(); + this._contentDidChange(); + return ret; }, /** - Notify the observer system that a property is about to change. + Invoked when the content property is about to change. Notifies observers that the + entire array content will change. - Sometimes you need to change a value directly or indirectly without - actually calling `get()` or `set()` on it. In this case, you can use this - method and `propertyDidChange()` instead. Calling these two methods - together will notify all observers that the property has potentially - changed value. + @private + @method _contentWillChange + */ + _contentWillChange: beforeObserver('content', function() { + var content = this.get('content'); - Note that you must always call `propertyWillChange` and `propertyDidChange` - as a pair. If you do not, it may get the property change groups out of - order and cause notifications to be delivered more often than you would - like. + if (content) { content.removeArrayObserver(this); } + var len = content ? get(content, 'length') : 0; + this.arrayWillChange(content, 0, len); + }), - @method propertyWillChange - @param {String} keyName The property key that is about to change. - @return {Ember.Observable} + /** + Check to make sure that the content has changed, and if so, + update the children directly. This is always scheduled + asynchronously, to allow the element to be created before + bindings have synchronized and vice versa. + + @private + @method _contentDidChange */ - propertyWillChange: function(keyName) { - propertyWillChange(this, keyName); - return this; - }, + _contentDidChange: observer('content', function() { + var content = get(this, 'content'); - /** - Notify the observer system that a property has just changed. + if (content) { + this._assertArrayLike(content); + content.addArrayObserver(this); + } - Sometimes you need to change a value directly or indirectly without - actually calling `get()` or `set()` on it. In this case, you can use this - method and `propertyWillChange()` instead. Calling these two methods - together will notify all observers that the property has potentially - changed value. + var len = content ? get(content, 'length') : 0; + this.arrayDidChange(content, 0, null, len); + }), - Note that you must always call `propertyWillChange` and `propertyDidChange` - as a pair. If you do not, it may get the property change groups out of - order and cause notifications to be delivered more often than you would - like. + /** + Ensure that the content implements Ember.Array - @method propertyDidChange - @param {String} keyName The property key that has just changed. - @return {Ember.Observable} + @private + @method _assertArrayLike */ - propertyDidChange: function(keyName) { - propertyDidChange(this, keyName); - return this; + _assertArrayLike: function(content) { + Ember.assert(fmt("an Ember.CollectionView's content must implement Ember.Array. You passed %@", [content]), EmberArray.detect(content)); }, /** - Convenience method to call `propertyWillChange` and `propertyDidChange` in - succession. + Removes the content and content observers. - @method notifyPropertyChange - @param {String} keyName The property key to be notified about. - @return {Ember.Observable} + @method destroy */ - notifyPropertyChange: function(keyName) { - this.propertyWillChange(keyName); - this.propertyDidChange(keyName); - return this; - }, - - addBeforeObserver: function(key, target, method) { - Ember.deprecate('Before observers are deprecated and will be removed in a future release. If you want to keep track of previous values you have to implement it yourself. See http://emberjs.com/guides/deprecations#toc_deprecate-beforeobservers'); - addBeforeObserver(this, key, target, method); - }, + destroy: function() { + if (!this._super()) { return; } - /** - Adds an observer on a property. + var content = get(this, 'content'); + if (content) { content.removeArrayObserver(this); } - This is the core method used to register an observer for a property. + if (this._createdEmptyView) { + this._createdEmptyView.destroy(); + } - Once you call this method, any time the key's value is set, your observer - will be notified. Note that the observers are triggered any time the - value is set, regardless of whether it has actually changed. Your - observer should be prepared to handle that. + return this; + }, - You can also pass an optional context parameter to this method. The - context will be passed to your observer method whenever it is triggered. - Note that if you add the same target/method pair on a key multiple times - with different context parameters, your observer will only be called once - with the last context you passed. + /** + Called when a mutation to the underlying content array will occur. - ### Observer Methods + This method will remove any views that are no longer in the underlying + content array. - Observer methods you pass should generally have the following signature if - you do not pass a `context` parameter: + Invokes whenever the content array itself will change. - ```javascript - fooDidChange: function(sender, key, value, rev) { }; - ``` + @method arrayWillChange + @param {Array} content the managed collection of objects + @param {Number} start the index at which the changes will occurr + @param {Number} removed number of object to be removed from content + */ + arrayWillChange: function(content, start, removedCount) { + // If the contents were empty before and this template collection has an + // empty view remove it now. + var emptyView = get(this, 'emptyView'); + if (emptyView && emptyView instanceof View) { + emptyView.removeFromParent(); + } - The sender is the object that changed. The key is the property that - changes. The value property is currently reserved and unused. The rev - is the last property revision of the object when it changed, which you can - use to detect if the key value has really changed or not. + // Loop through child views that correspond with the removed items. + // Note that we loop from the end of the array to the beginning because + // we are mutating it as we go. + var childViews = this._childViews, childView, idx, len; - If you pass a `context` parameter, the context will be passed before the - revision like so: + len = this._childViews.length; - ```javascript - fooDidChange: function(sender, key, value, context, rev) { }; - ``` + var removingAll = removedCount === len; - Usually you will not need the value, context or revision parameters at - the end. In this case, it is common to write observer methods that take - only a sender and key value as parameters or, if you aren't interested in - any of these values, to write an observer that has no parameters at all. + if (removingAll) { + this.currentState.empty(this); + this.invokeRecursively(function(view) { + view.removedFromDOM = true; + }, false); + } - @method addObserver - @param {String} key The key to observer - @param {Object} target The target object to invoke - @param {String|Function} method The method to invoke. - */ - addObserver: function(key, target, method) { - addObserver(this, key, target, method); + for (idx = start + removedCount - 1; idx >= start; idx--) { + childView = childViews[idx]; + childView.destroy(); + } }, /** - Remove an observer you have previously registered on this object. Pass - the same key, target, and method you passed to `addObserver()` and your - target will no longer receive notifications. + Called when a mutation to the underlying content array occurs. - @method removeObserver - @param {String} key The key to observer - @param {Object} target The target object to invoke - @param {String|Function} method The method to invoke. - */ - removeObserver: function(key, target, method) { - removeObserver(this, key, target, method); - }, + This method will replay that mutation against the views that compose the + `Ember.CollectionView`, ensuring that the view reflects the model. - /** - Returns `true` if the object currently has observers registered for a - particular key. You can use this method to potentially defer performing - an expensive action until someone begins observing a particular property - on the object. + This array observer is added in `contentDidChange`. - @method hasObserverFor - @param {String} key Key to check - @return {Boolean} + @method arrayDidChange + @param {Array} content the managed collection of objects + @param {Number} start the index at which the changes occurred + @param {Number} removed number of object removed from content + @param {Number} added number of object added to content */ - hasObserverFor: function(key) { - return hasListeners(this, key+':change'); - }, + arrayDidChange: function(content, start, removed, added) { + var addedViews = [], view, item, idx, len, itemViewClass, + emptyView; - /** - Retrieves the value of a property, or a default value in the case that the - property returns `undefined`. + len = content ? get(content, 'length') : 0; - ```javascript - person.getWithDefault('lastName', 'Doe'); - ``` + if (len) { + itemViewClass = get(this, 'itemViewClass'); - @method getWithDefault - @param {String} keyName The name of the property to retrieve - @param {Object} defaultValue The value to return if the property value is undefined - @return {Object} The property value or the defaultValue. - */ - getWithDefault: function(keyName, defaultValue) { - return getWithDefault(this, keyName, defaultValue); - }, + if ('string' === typeof itemViewClass && isGlobalPath(itemViewClass)) { + itemViewClass = get(itemViewClass) || itemViewClass; + } - /** - Set the value of a property to the current value plus some amount. + Ember.assert(fmt("itemViewClass must be a subclass of Ember.View, not %@", + [itemViewClass]), + 'string' === typeof itemViewClass || View.detect(itemViewClass)); - ```javascript - person.incrementProperty('age'); - team.incrementProperty('score', 2); - ``` + for (idx = start; idx < start+added; idx++) { + item = content.objectAt(idx); - @method incrementProperty - @param {String} keyName The name of the property to increment - @param {Number} increment The amount to increment by. Defaults to 1 - @return {Number} The new property value - */ - incrementProperty: function(keyName, increment) { - if (isNone(increment)) { increment = 1; } - Ember.assert("Must pass a numeric value to incrementProperty", (!isNaN(parseFloat(increment)) && isFinite(increment))); - set(this, keyName, (parseFloat(get(this, keyName)) || 0) + increment); - return get(this, keyName); - }, + view = this.createChildView(itemViewClass, { + content: item, + contentIndex: idx + }); - /** - Set the value of a property to the current value minus some amount. + addedViews.push(view); + } + } else { + emptyView = get(this, 'emptyView'); - ```javascript - player.decrementProperty('lives'); - orc.decrementProperty('health', 5); - ``` + if (!emptyView) { return; } - @method decrementProperty - @param {String} keyName The name of the property to decrement - @param {Number} decrement The amount to decrement by. Defaults to 1 - @return {Number} The new property value - */ - decrementProperty: function(keyName, decrement) { - if (isNone(decrement)) { decrement = 1; } - Ember.assert("Must pass a numeric value to decrementProperty", (!isNaN(parseFloat(decrement)) && isFinite(decrement))); - set(this, keyName, (get(this, keyName) || 0) - decrement); - return get(this, keyName); - }, + if ('string' === typeof emptyView && isGlobalPath(emptyView)) { + emptyView = get(emptyView) || emptyView; + } - /** - Set the value of a boolean property to the opposite of its - current value. + emptyView = this.createChildView(emptyView); + addedViews.push(emptyView); + set(this, 'emptyView', emptyView); - ```javascript - starship.toggleProperty('warpDriveEngaged'); - ``` + if (CoreView.detect(emptyView)) { + this._createdEmptyView = emptyView; + } + } - @method toggleProperty - @param {String} keyName The name of the property to toggle - @return {Object} The new property value - */ - toggleProperty: function(keyName) { - set(this, keyName, !get(this, keyName)); - return get(this, keyName); + this.replace(start, 0, addedViews); }, /** - Returns the cached value of a computed property, if it exists. - This allows you to inspect the value of a computed property - without accidentally invoking it if it is intended to be - generated lazily. + Instantiates a view to be added to the childViews array during view + initialization. You generally will not call this method directly unless + you are overriding `createChildViews()`. Note that this method will + automatically configure the correct settings on the new view instance to + act as a child of the parent. - @method cacheFor - @param {String} keyName - @return {Object} The cached value of the computed property, if any + The tag name for the view will be set to the tagName of the viewClass + passed in. + + @method createChildView + @param {Class} viewClass + @param {Hash} [attrs] Attributes to add + @return {Ember.View} new instance */ - cacheFor: function(keyName) { - return cacheFor(this, keyName); - }, + createChildView: function(view, attrs) { + view = this._super(view, attrs); - // intended for debugging purposes - observersForKey: function(keyName) { - return observersFor(this, keyName); + var itemTagName = get(view, 'tagName'); + + if (itemTagName === null || itemTagName === undefined) { + itemTagName = CollectionView.CONTAINER_MAP[get(this, 'tagName')]; + set(view, 'tagName', itemTagName); + } + + return view; } }); + + /** + A map of parent tags to their default child tags. You can add + additional parent tags if you want collection views that use + a particular parent tag to default to a child tag. + + @property CONTAINER_MAP + @type Hash + @static + @final + */ + CollectionView.CONTAINER_MAP = { + ul: 'li', + ol: 'li', + table: 'tr', + thead: 'tr', + tbody: 'tr', + tfoot: 'tr', + tr: 'td', + select: 'option' + }; + + __exports__["default"] = CollectionView; }); -enifed("ember-runtime/mixins/promise_proxy", - ["ember-metal/property_get","ember-metal/set_properties","ember-metal/computed","ember-metal/mixin","ember-metal/error","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { +define("ember-views/views/component", + ["ember-metal/core","ember-views/mixins/component_template_deprecation","ember-runtime/mixins/target_action_support","ember-views/views/view","ember-metal/property_get","ember-metal/property_set","ember-metal/is_none","ember-metal/computed","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { "use strict"; - var get = __dependency1__.get; - var setProperties = __dependency2__["default"]; - var computed = __dependency3__.computed; - var Mixin = __dependency4__.Mixin; - var EmberError = __dependency5__["default"]; + var Ember = __dependency1__["default"]; + // Ember.assert, Ember.Handlebars - var not = computed.not; - var or = computed.or; + var ComponentTemplateDeprecation = __dependency2__["default"]; + var TargetActionSupport = __dependency3__["default"]; + var View = __dependency4__.View;var get = __dependency5__.get; + var set = __dependency6__.set; + var isNone = __dependency7__.isNone; - /** - @module ember - @submodule ember-runtime - */ + var computed = __dependency8__.computed; - function tap(proxy, promise) { - setProperties(proxy, { - isFulfilled: false, - isRejected: false - }); + var a_slice = Array.prototype.slice; - return promise.then(function(value) { - setProperties(proxy, { - content: value, - isFulfilled: true - }); - return value; - }, function(reason) { - setProperties(proxy, { - reason: reason, - isRejected: true - }); - throw reason; - }, "Ember: PromiseProxy"); - } + /** + @module ember + @submodule ember-views + */ /** - A low level mixin making ObjectProxy, ObjectController or ArrayController's promise aware. + An `Ember.Component` is a view that is completely + isolated. Property access in its templates go + to the view object and actions are targeted at + the view object. There is no access to the + surrounding context or outer controller; all + contextual information must be passed in. - ```javascript - var ObjectPromiseController = Ember.ObjectController.extend(Ember.PromiseProxyMixin); + The easiest way to create an `Ember.Component` is via + a template. If you name a template + `components/my-foo`, you will be able to use + `{{my-foo}}` in other templates, which will make + an instance of the isolated component. - var controller = ObjectPromiseController.create({ - promise: $.getJSON('/some/remote/data.json') - }); + ```handlebars + {{app-profile person=currentUser}} + ``` - controller.then(function(json){ - // the json - }, function(reason) { - // the reason why you have no json + ```handlebars + +

{{person.title}}

+ +

{{person.signature}}

+ ``` + + You can use `yield` inside a template to + include the **contents** of any block attached to + the component. The block will be executed in the + context of the surrounding context or outer controller: + + ```handlebars + {{#app-profile person=currentUser}} +

Admin mode

+ {{! Executed in the controller's context. }} + {{/app-profile}} + ``` + + ```handlebars + +

{{person.title}}

+ {{! Executed in the components context. }} + {{yield}} {{! block contents }} + ``` + + If you want to customize the component, in order to + handle events or actions, you implement a subclass + of `Ember.Component` named after the name of the + component. Note that `Component` needs to be appended to the name of + your subclass like `AppProfileComponent`. + + For example, you could implement the action + `hello` for the `app-profile` component: + + ```javascript + App.AppProfileComponent = Ember.Component.extend({ + actions: { + hello: function(name) { + console.log("Hello", name); + } + } }); ``` - the controller has bindable attributes which - track the promises life cycle + And then use it in the component's template: + + ```handlebars + + +

{{person.title}}

+ {{yield}} + + + ``` + + Components must have a `-` in their name to avoid + conflicts with built-in controls that wrap HTML + elements. This is consistent with the same + requirement in web components. + + @class Component + @namespace Ember + @extends Ember.View + */ + var Component = View.extend(TargetActionSupport, ComponentTemplateDeprecation, { + instrumentName: 'component', + instrumentDisplay: computed(function() { + if (this._debugContainerKey) { + return '{{' + this._debugContainerKey.split(':')[1] + '}}'; + } + }), + + init: function() { + this._super(); + set(this, 'context', this); + set(this, 'controller', this); + }, + + defaultLayout: function(context, options){ + Ember.Handlebars.helpers['yield'].call(context, options); + }, - ```javascript - controller.get('isPending') //=> true - controller.get('isSettled') //=> false - controller.get('isRejected') //=> false - controller.get('isFulfilled') //=> false - ``` + /** + A components template property is set by passing a block + during its invocation. It is executed within the parent context. - When the the $.getJSON completes, and the promise is fulfilled - with json, the life cycle attributes will update accordingly. + Example: - ```javascript - controller.get('isPending') //=> false - controller.get('isSettled') //=> true - controller.get('isRejected') //=> false - controller.get('isFulfilled') //=> true + ```handlebars + {{#my-component}} + // something that is run in the context + // of the parent context + {{/my-component}} ``` - As the controller is an ObjectController, and the json now its content, - all the json properties will be available directly from the controller. - - ```javascript - // Assuming the following json: - { - firstName: 'Stefan', - lastName: 'Penner' - } + Specifying a template directly to a component is deprecated without + also specifying the layout property. - // both properties will accessible on the controller - controller.get('firstName') //=> 'Stefan' - controller.get('lastName') //=> 'Penner' - ``` + @deprecated + @property template + */ + template: computed(function(key, value) { + if (value !== undefined) { return value; } - If the controller is backing a template, the attributes are - bindable from within that template + var templateName = get(this, 'templateName'), + template = this.templateForName(templateName, 'template'); - ```handlebars - {{#if isPending}} - loading... - {{else}} - firstName: {{firstName}} - lastName: {{lastName}} - {{/if}} - ``` - @class Ember.PromiseProxyMixin - */ - __exports__["default"] = Mixin.create({ - /** - If the proxied promise is rejected this will contain the reason - provided. + Ember.assert("You specified the templateName " + templateName + " for " + this + ", but it did not exist.", !templateName || template); - @property reason - @default null - */ - reason: null, + return template || get(this, 'defaultTemplate'); + }).property('templateName'), /** - Once the proxied promise has settled this will become `false`. + Specifying a components `templateName` is deprecated without also + providing the `layout` or `layoutName` properties. - @property isPending - @default true + @deprecated + @property templateName */ - isPending: not('isSettled').readOnly(), + templateName: null, - /** - Once the proxied promise has settled this will become `true`. + // during render, isolate keywords + cloneKeywords: function() { + return { + view: this, + controller: this + }; + }, - @property isSettled - @default false - */ - isSettled: or('isRejected', 'isFulfilled').readOnly(), + _yield: function(context, options) { + var view = options.data.view, + parentView = this._parentView, + template = get(this, 'template'); - /** - Will become `true` if the proxied promise is rejected. + if (template) { + Ember.assert("A Component must have a parent view in order to yield.", parentView); - @property isRejected - @default false - */ - isRejected: false, + view.appendChild(View, { + isVirtual: true, + tagName: '', + _contextView: parentView, + template: template, + context: get(parentView, 'context'), + controller: get(parentView, 'controller'), + templateData: { keywords: parentView.cloneKeywords() } + }); + } + }, /** - Will become `true` if the proxied promise is fulfilled. + If the component is currently inserted into the DOM of a parent view, this + property will point to the controller of the parent view. - @property isFulfilled - @default false + @property targetObject + @type Ember.Controller + @default null */ - isFulfilled: false, + targetObject: computed(function(key) { + var parentView = get(this, '_parentView'); + return parentView ? get(parentView, 'controller') : null; + }).property('_parentView'), /** - The promise whose fulfillment value is being proxied by this object. + Triggers a named action on the controller context where the component is used if + this controller has registered for notifications of the action. - This property must be specified upon creation, and should not be - changed once created. + For example a component for playing or pausing music may translate click events + into action notifications of "play" or "stop" depending on some internal state + of the component: - Example: ```javascript - Ember.ObjectController.extend(Ember.PromiseProxyMixin).create({ - promise: + App.PlayButtonComponent = Ember.Component.extend({ + click: function(){ + if (this.get('isPlaying')) { + this.sendAction('play'); + } else { + this.sendAction('stop'); + } + } }); ``` - @property promise - */ - promise: computed(function(key, promise) { - if (arguments.length === 2) { - return tap(this, promise); - } else { - throw new EmberError("PromiseProxy's promise must be set"); - } - }), + When used inside a template these component actions are configured to + trigger actions in the outer application context: - /** - An alias to the proxied promise's `then`. + ```handlebars + {{! application.hbs }} + {{play-button play="musicStarted" stop="musicStopped"}} + ``` - See RSVP.Promise.then. + When the component receives a browser `click` event it translate this + interaction into application-specific semantics ("play" or "stop") and + triggers the specified action name on the controller for the template + where the component is used: - @method then - @param {Function} callback - @return {RSVP.Promise} - */ - then: promiseAlias('then'), - /** - An alias to the proxied promise's `catch`. + ```javascript + App.ApplicationController = Ember.Controller.extend({ + actions: { + musicStarted: function(){ + // called when the play button is clicked + // and the music started playing + }, + musicStopped: function(){ + // called when the play button is clicked + // and the music stopped playing + } + } + }); + ``` - See RSVP.Promise.catch. + If no action name is passed to `sendAction` a default name of "action" + is assumed. - @method catch - @param {Function} callback - @return {RSVP.Promise} - @since 1.3.0 - */ - 'catch': promiseAlias('catch'), + ```javascript + App.NextButtonComponent = Ember.Component.extend({ + click: function(){ + this.sendAction(); + } + }); + ``` - /** - An alias to the proxied promise's `finally`. + ```handlebars + {{! application.hbs }} + {{next-button action="playNextSongInAlbum"}} + ``` - See RSVP.Promise.finally. + ```javascript + App.ApplicationController = Ember.Controller.extend({ + actions: { + playNextSongInAlbum: function(){ + ... + } + } + }); + ``` - @method finally - @param {Function} callback - @return {RSVP.Promise} - @since 1.3.0 + @method sendAction + @param [action] {String} the action to trigger + @param [context] {*} a context to send with the action */ - 'finally': promiseAlias('finally') + sendAction: function(action) { + var actionName, + contexts = a_slice.call(arguments, 1); + + // Send the default action + if (action === undefined) { + actionName = get(this, 'action'); + Ember.assert("The default action was triggered on the component " + this.toString() + + ", but the action name (" + actionName + ") was not a string.", + isNone(actionName) || typeof actionName === 'string'); + } else { + actionName = get(this, action); + Ember.assert("The " + action + " action was triggered on the component " + + this.toString() + ", but the action name (" + actionName + + ") was not a string.", + isNone(actionName) || typeof actionName === 'string'); + } + + // If no action name for that action could be found, just abort. + if (actionName === undefined) { return; } + this.triggerAction({ + action: actionName, + actionContext: contexts + }); + } }); - function promiseAlias(name) { - return function () { - var promise = get(this, 'promise'); - return promise[name].apply(promise, arguments); - }; - } + __exports__["default"] = Component; }); -enifed("ember-runtime/mixins/sortable", - ["ember-metal/core","ember-metal/property_get","ember-metal/enumerable_utils","ember-metal/mixin","ember-runtime/mixins/mutable_enumerable","ember-runtime/compare","ember-metal/observer","ember-metal/computed","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { +define("ember-views/views/container_view", + ["ember-metal/core","ember-metal/merge","ember-runtime/mixins/mutable_array","ember-metal/property_get","ember-metal/property_set","ember-views/views/view","ember-views/views/states","ember-metal/error","ember-metal/enumerable_utils","ember-metal/computed","ember-metal/run_loop","ember-metal/properties","ember-views/system/render_buffer","ember-metal/mixin","ember-runtime/system/native_array","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __exports__) { "use strict"; + var Ember = __dependency1__["default"]; + // Ember.assert, Ember.K + + var merge = __dependency2__["default"]; + var MutableArray = __dependency3__["default"]; + var get = __dependency4__.get; + var set = __dependency5__.set; + + var View = __dependency6__.View; + var ViewCollection = __dependency6__.ViewCollection; + var cloneStates = __dependency7__.cloneStates; + var EmberViewStates = __dependency7__.states; + + var EmberError = __dependency8__["default"]; + + // ES6TODO: functions on EnumerableUtils should get their own export + var EnumerableUtils = __dependency9__["default"]; + var forEach = EnumerableUtils.forEach; + + var computed = __dependency10__.computed; + var run = __dependency11__["default"]; + var defineProperty = __dependency12__.defineProperty; + var RenderBuffer = __dependency13__["default"]; + var observer = __dependency14__.observer; + var beforeObserver = __dependency14__.beforeObserver; + var A = __dependency15__.A; + /** @module ember - @submodule ember-runtime + @submodule ember-views */ - var Ember = __dependency1__["default"]; - // Ember.assert, Ember.A - - var get = __dependency2__.get; - var forEach = __dependency3__.forEach; - var Mixin = __dependency4__.Mixin; - var MutableEnumerable = __dependency5__["default"]; - var compare = __dependency6__["default"]; - var addObserver = __dependency7__.addObserver; - var removeObserver = __dependency7__.removeObserver; - var computed = __dependency8__.computed; - var beforeObserver = __dependency4__.beforeObserver; - var observer = __dependency4__.observer; - //ES6TODO: should we access these directly from their package or from how their exposed in ember-metal? + var states = cloneStates(EmberViewStates); /** - `Ember.SortableMixin` provides a standard interface for array proxies - to specify a sort order and maintain this sorting when objects are added, - removed, or updated without changing the implicit order of their underlying - model array: + A `ContainerView` is an `Ember.View` subclass that implements `Ember.MutableArray` + allowing programmatic management of its child views. - ```javascript - songs = [ - {trackNumber: 4, title: 'Ob-La-Di, Ob-La-Da'}, - {trackNumber: 2, title: 'Back in the U.S.S.R.'}, - {trackNumber: 3, title: 'Glass Onion'}, - ]; + ## Setting Initial Child Views - songsController = Ember.ArrayController.create({ - model: songs, - sortProperties: ['trackNumber'], - sortAscending: true + The initial array of child views can be set in one of two ways. You can + provide a `childViews` property at creation time that contains instance of + `Ember.View`: + + ```javascript + aContainer = Ember.ContainerView.create({ + childViews: [Ember.View.create(), Ember.View.create()] }); + ``` - songsController.get('firstObject'); // {trackNumber: 2, title: 'Back in the U.S.S.R.'} + You can also provide a list of property names whose values are instances of + `Ember.View`: - songsController.addObject({trackNumber: 1, title: 'Dear Prudence'}); - songsController.get('firstObject'); // {trackNumber: 1, title: 'Dear Prudence'} + ```javascript + aContainer = Ember.ContainerView.create({ + childViews: ['aView', 'bView', 'cView'], + aView: Ember.View.create(), + bView: Ember.View.create(), + cView: Ember.View.create() + }); ``` - If you add or remove the properties to sort by or change the sort direction the model - sort order will be automatically updated. + The two strategies can be combined: ```javascript - songsController.set('sortProperties', ['title']); - songsController.get('firstObject'); // {trackNumber: 2, title: 'Back in the U.S.S.R.'} - - songsController.toggleProperty('sortAscending'); - songsController.get('firstObject'); // {trackNumber: 4, title: 'Ob-La-Di, Ob-La-Da'} + aContainer = Ember.ContainerView.create({ + childViews: ['aView', Ember.View.create()], + aView: Ember.View.create() + }); ``` - `SortableMixin` works by sorting the `arrangedContent` array, which is the array that - `ArrayProxy` displays. Due to the fact that the underlying 'content' array is not changed, that - array will not display the sorted list: + Each child view's rendering will be inserted into the container's rendered + HTML in the same order as its position in the `childViews` property. - ```javascript - songsController.get('content').get('firstObject'); // Returns the unsorted original content - songsController.get('firstObject'); // Returns the sorted content. - ``` + ## Adding and Removing Child Views - Although the sorted content can also be accessed through the `arrangedContent` property, - it is preferable to use the proxied class and not the `arrangedContent` array directly. + The container view implements `Ember.MutableArray` allowing programmatic management of its child views. - @class SortableMixin - @namespace Ember - @uses Ember.MutableEnumerable - */ - __exports__["default"] = Mixin.create(MutableEnumerable, { + To remove a view, pass that view into a `removeObject` call on the container view. - /** - Specifies which properties dictate the `arrangedContent`'s sort order. + Given an empty `` the following code - When specifying multiple properties the sorting will use properties - from the `sortProperties` array prioritized from first to last. + ```javascript + aContainer = Ember.ContainerView.create({ + classNames: ['the-container'], + childViews: ['aView', 'bView'], + aView: Ember.View.create({ + template: Ember.Handlebars.compile("A") + }), + bView: Ember.View.create({ + template: Ember.Handlebars.compile("B") + }) + }); - @property {Array} sortProperties - */ - sortProperties: null, + aContainer.appendTo('body'); + ``` - /** - Specifies the `arrangedContent`'s sort direction. - Sorts the content in ascending order by default. Set to `false` to - use descending order. + Results in the HTML - @property {Boolean} sortAscending - @default true - */ - sortAscending: true, + ```html +
+
A
+
B
+
+ ``` - /** - The function used to compare two values. You can override this if you - want to do custom comparisons. Functions must be of the type expected by - Array#sort, i.e., + Removing a view - * return 0 if the two parameters are equal, - * return a negative value if the first parameter is smaller than the second or - * return a positive value otherwise: + ```javascript + aContainer.toArray(); // [aContainer.aView, aContainer.bView] + aContainer.removeObject(aContainer.get('bView')); + aContainer.toArray(); // [aContainer.aView] + ``` - ```javascript - function(x, y) { // These are assumed to be integers - if (x === y) - return 0; - return x < y ? -1 : 1; - } - ``` + Will result in the following HTML - @property sortFunction - @type {Function} - @default Ember.compare - */ - sortFunction: compare, + ```html +
+
A
+
+ ``` - orderBy: function(item1, item2) { - var result = 0; - var sortProperties = get(this, 'sortProperties'); - var sortAscending = get(this, 'sortAscending'); - var sortFunction = get(this, 'sortFunction'); + Similarly, adding a child view is accomplished by adding `Ember.View` instances to the + container view. - Ember.assert("you need to define `sortProperties`", !!sortProperties); + Given an empty `` the following code - forEach(sortProperties, function(propertyName) { - if (result === 0) { - result = sortFunction.call(this, get(item1, propertyName), get(item2, propertyName)); - if ((result !== 0) && !sortAscending) { - result = (-1) * result; - } - } - }, this); + ```javascript + aContainer = Ember.ContainerView.create({ + classNames: ['the-container'], + childViews: ['aView', 'bView'], + aView: Ember.View.create({ + template: Ember.Handlebars.compile("A") + }), + bView: Ember.View.create({ + template: Ember.Handlebars.compile("B") + }) + }); - return result; - }, + aContainer.appendTo('body'); + ``` - destroy: function() { - var content = get(this, 'content'); - var sortProperties = get(this, 'sortProperties'); + Results in the HTML - if (content && sortProperties) { - forEach(content, function(item) { - forEach(sortProperties, function(sortProperty) { - removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); - }, this); - }, this); - } + ```html +
+
A
+
B
+
+ ``` - return this._super(); - }, + Adding a view - isSorted: computed.notEmpty('sortProperties'), + ```javascript + AnotherViewClass = Ember.View.extend({ + template: Ember.Handlebars.compile("Another view") + }); - /** - Overrides the default `arrangedContent` from `ArrayProxy` in order to sort by `sortFunction`. - Also sets up observers for each `sortProperty` on each item in the content Array. + aContainer.toArray(); // [aContainer.aView, aContainer.bView] + aContainer.pushObject(AnotherViewClass.create()); + aContainer.toArray(); // [aContainer.aView, aContainer.bView, ] + ``` - @property arrangedContent - */ - arrangedContent: computed('content', 'sortProperties.@each', function(key, value) { - var content = get(this, 'content'); - var isSorted = get(this, 'isSorted'); - var sortProperties = get(this, 'sortProperties'); - var self = this; + Will result in the following HTML - if (content && isSorted) { - content = content.slice(); - content.sort(function(item1, item2) { - return self.orderBy(item1, item2); - }); - forEach(content, function(item) { - forEach(sortProperties, function(sortProperty) { - addObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); - }, this); - }, this); - return Ember.A(content); - } + ```html +
+
A
+
B
+
Another view
+
+ ``` - return content; - }), + ## Templates and Layout - _contentWillChange: beforeObserver('content', function() { - var content = get(this, 'content'); - var sortProperties = get(this, 'sortProperties'); + A `template`, `templateName`, `defaultTemplate`, `layout`, `layoutName` or + `defaultLayout` property on a container view will not result in the template + or layout being rendered. The HTML contents of a `Ember.ContainerView`'s DOM + representation will only be the rendered HTML of its child views. - if (content && sortProperties) { - forEach(content, function(item) { - forEach(sortProperties, function(sortProperty) { - removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); - }, this); - }, this); - } + @class ContainerView + @namespace Ember + @extends Ember.View + */ + var ContainerView = View.extend(MutableArray, { + states: states, + init: function() { this._super(); - }), - sortPropertiesWillChange: beforeObserver('sortProperties', function() { - this._lastSortAscending = undefined; - }), - - sortPropertiesDidChange: observer('sortProperties', function() { - this._lastSortAscending = undefined; - }), + var childViews = get(this, 'childViews'); - sortAscendingWillChange: beforeObserver('sortAscending', function() { - this._lastSortAscending = get(this, 'sortAscending'); - }), + // redefine view's childViews property that was obliterated + defineProperty(this, 'childViews', View.childViewsProperty); - sortAscendingDidChange: observer('sortAscending', function() { - if (this._lastSortAscending !== undefined && get(this, 'sortAscending') !== this._lastSortAscending) { - var arrangedContent = get(this, 'arrangedContent'); - arrangedContent.reverseObjects(); - } - }), + var _childViews = this._childViews; - contentArrayWillChange: function(array, idx, removedCount, addedCount) { - var isSorted = get(this, 'isSorted'); + forEach(childViews, function(viewName, idx) { + var view; - if (isSorted) { - var arrangedContent = get(this, 'arrangedContent'); - var removedObjects = array.slice(idx, idx+removedCount); - var sortProperties = get(this, 'sortProperties'); + if ('string' === typeof viewName) { + view = get(this, viewName); + view = this.createChildView(view); + set(this, viewName, view); + } else { + view = this.createChildView(viewName); + } - forEach(removedObjects, function(item) { - arrangedContent.removeObject(item); + _childViews[idx] = view; + }, this); - forEach(sortProperties, function(sortProperty) { - removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); - }, this); - }, this); + var currentView = get(this, 'currentView'); + if (currentView) { + if (!_childViews.length) { _childViews = this._childViews = this._childViews.slice(); } + _childViews.push(this.createChildView(currentView)); } - - return this._super(array, idx, removedCount, addedCount); }, - contentArrayDidChange: function(array, idx, removedCount, addedCount) { - var isSorted = get(this, 'isSorted'); - var sortProperties = get(this, 'sortProperties'); - - if (isSorted) { - var addedObjects = array.slice(idx, idx+addedCount); + replace: function(idx, removedCount, addedViews) { + var addedCount = addedViews ? get(addedViews, 'length') : 0; + var self = this; + Ember.assert("You can't add a child to a container - the child is already a child of another view", A(addedViews).every(function(item) { return !get(item, '_parentView') || get(item, '_parentView') === self; })); - forEach(addedObjects, function(item) { - this.insertItemSorted(item); + this.arrayContentWillChange(idx, removedCount, addedCount); + this.childViewsWillChange(this._childViews, idx, removedCount); - forEach(sortProperties, function(sortProperty) { - addObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); - }, this); - }, this); + if (addedCount === 0) { + this._childViews.splice(idx, removedCount) ; + } else { + var args = [idx, removedCount].concat(addedViews); + if (addedViews.length && !this._childViews.length) { this._childViews = this._childViews.slice(); } + this._childViews.splice.apply(this._childViews, args); } - return this._super(array, idx, removedCount, addedCount); - }, + this.arrayContentDidChange(idx, removedCount, addedCount); + this.childViewsDidChange(this._childViews, idx, removedCount, addedCount); - insertItemSorted: function(item) { - var arrangedContent = get(this, 'arrangedContent'); - var length = get(arrangedContent, 'length'); + return this; + }, - var idx = this._binarySearch(item, 0, length); - arrangedContent.insertAt(idx, item); + objectAt: function(idx) { + return this._childViews[idx]; }, - contentItemSortPropertyDidChange: function(item) { - var arrangedContent = get(this, 'arrangedContent'); - var oldIndex = arrangedContent.indexOf(item); - var leftItem = arrangedContent.objectAt(oldIndex - 1); - var rightItem = arrangedContent.objectAt(oldIndex + 1); - var leftResult = leftItem && this.orderBy(item, leftItem); - var rightResult = rightItem && this.orderBy(item, rightItem); + length: computed(function () { + return this._childViews.length; + }).volatile(), - if (leftResult < 0 || rightResult > 0) { - arrangedContent.removeObject(item); - this.insertItemSorted(item); - } - }, + /** + Instructs each child view to render to the passed render buffer. - _binarySearch: function(item, low, high) { - var mid, midItem, res, arrangedContent; + @private + @method render + @param {Ember.RenderBuffer} buffer the buffer to render to + */ + render: function(buffer) { + this.forEachChildView(function(view) { + view.renderToBuffer(buffer); + }); + }, - if (low === high) { - return low; - } + instrumentName: 'container', - arrangedContent = get(this, 'arrangedContent'); + /** + When a child view is removed, destroy its element so that + it is removed from the DOM. - mid = low + Math.floor((high - low) / 2); - midItem = arrangedContent.objectAt(mid); + The array observer that triggers this action is set up in the + `renderToBuffer` method. - res = this.orderBy(midItem, item); + @private + @method childViewsWillChange + @param {Ember.Array} views the child views array before mutation + @param {Number} start the start position of the mutation + @param {Number} removed the number of child views removed + **/ + childViewsWillChange: function(views, start, removed) { + this.propertyWillChange('childViews'); - if (res < 0) { - return this._binarySearch(item, mid+1, high); - } else if (res > 0) { - return this._binarySearch(item, low, mid); + if (removed > 0) { + var changedViews = views.slice(start, start+removed); + // transition to preRender before clearing parentView + this.currentState.childViewsWillChange(this, views, start, removed); + this.initializeViews(changedViews, null, null); } + }, - return mid; - } - }); - }); -enifed("ember-runtime/mixins/target_action_support", - ["ember-metal/core","ember-metal/property_get","ember-metal/utils","ember-metal/mixin","ember-metal/computed","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-runtime - */ - var Ember = __dependency1__["default"]; - // Ember.lookup, Ember.assert + removeChild: function(child) { + this.removeObject(child); + return this; + }, - var get = __dependency2__.get; - var typeOf = __dependency3__.typeOf; - var Mixin = __dependency4__.Mixin; - var computed = __dependency5__.computed; + /** + When a child view is added, make sure the DOM gets updated appropriately. - /** - `Ember.TargetActionSupport` is a mixin that can be included in a class - to add a `triggerAction` method with semantics similar to the Handlebars - `{{action}}` helper. In normal Ember usage, the `{{action}}` helper is - usually the best choice. This mixin is most often useful when you are - doing more complex event handling in View objects. + If the view has already rendered an element, we tell the child view to + create an element and insert it into the DOM. If the enclosing container + view has already written to a buffer, but not yet converted that buffer + into an element, we insert the string representation of the child into the + appropriate place in the buffer. - See also `Ember.ViewTargetActionSupport`, which has - view-aware defaults for target and actionContext. + @private + @method childViewsDidChange + @param {Ember.Array} views the array of child views after the mutation has occurred + @param {Number} start the start position of the mutation + @param {Number} removed the number of child views removed + @param {Number} added the number of child views added + */ + childViewsDidChange: function(views, start, removed, added) { + if (added > 0) { + var changedViews = views.slice(start, start+added); + this.initializeViews(changedViews, this, get(this, 'templateData')); + this.currentState.childViewsDidChange(this, views, start, added); + } + this.propertyDidChange('childViews'); + }, - @class TargetActionSupport - @namespace Ember - @extends Ember.Mixin - */ - var TargetActionSupport = Mixin.create({ - target: null, - action: null, - actionContext: null, + initializeViews: function(views, parentView, templateData) { + forEach(views, function(view) { + set(view, '_parentView', parentView); - targetObject: computed(function() { - var target = get(this, 'target'); + if (!view.container && parentView) { + set(view, 'container', parentView.container); + } - if (typeOf(target) === "string") { - var value = get(this, target); - if (value === undefined) { value = get(Ember.lookup, target); } - return value; - } else { - return target; - } - }).property('target'), + if (!get(view, 'templateData')) { + set(view, 'templateData', templateData); + } + }); + }, - actionContextObject: computed(function() { - var actionContext = get(this, 'actionContext'); + currentView: null, - if (typeOf(actionContext) === "string") { - var value = get(this, actionContext); - if (value === undefined) { value = get(Ember.lookup, actionContext); } - return value; - } else { - return actionContext; + _currentViewWillChange: beforeObserver('currentView', function() { + var currentView = get(this, 'currentView'); + if (currentView) { + currentView.destroy(); } - }).property('actionContext'), - - /** - Send an `action` with an `actionContext` to a `target`. The action, actionContext - and target will be retrieved from properties of the object. For example: + }), - ```javascript - App.SaveButtonView = Ember.View.extend(Ember.TargetActionSupport, { - target: Ember.computed.alias('controller'), - action: 'save', - actionContext: Ember.computed.alias('context'), - click: function() { - this.triggerAction(); // Sends the `save` action, along with the current context - // to the current controller + _currentViewDidChange: observer('currentView', function() { + var currentView = get(this, 'currentView'); + if (currentView) { + Ember.assert("You tried to set a current view that already has a parent. Make sure you don't have multiple outlets in the same view.", !get(currentView, '_parentView')); + this.pushObject(currentView); } - }); - ``` + }), - The `target`, `action`, and `actionContext` can be provided as properties of - an optional object argument to `triggerAction` as well. + _ensureChildrenAreInDOM: function () { + this.currentState.ensureChildrenAreInDOM(this); + } + }); - ```javascript - App.SaveButtonView = Ember.View.extend(Ember.TargetActionSupport, { - click: function() { - this.triggerAction({ - action: 'save', - target: this.get('controller'), - actionContext: this.get('context') - }); // Sends the `save` action, along with the current context - // to the current controller - } - }); - ``` + merge(states._default, { + childViewsWillChange: Ember.K, + childViewsDidChange: Ember.K, + ensureChildrenAreInDOM: Ember.K + }); - The `actionContext` defaults to the object you are mixing `TargetActionSupport` into. - But `target` and `action` must be specified either as properties or with the argument - to `triggerAction`, or a combination: + merge(states.inBuffer, { + childViewsDidChange: function(parentView, views, start, added) { + throw new EmberError('You cannot modify child views while in the inBuffer state'); + } + }); - ```javascript - App.SaveButtonView = Ember.View.extend(Ember.TargetActionSupport, { - target: Ember.computed.alias('controller'), - click: function() { - this.triggerAction({ - action: 'save' - }); // Sends the `save` action, along with a reference to `this`, - // to the current controller + merge(states.hasElement, { + childViewsWillChange: function(view, views, start, removed) { + for (var i=start; i get(this, 'content.length')) throw new EmberError(OUT_OF_RANGE_EXCEPTION); - this._replace(idx, 0, [object]); - return this; + renderToBufferIfNeeded: function (view, buffer) { + return false; }, - insertAt: function(idx, object) { - if (get(this, 'arrangedContent') === get(this, 'content')) { - return this._insertAt(idx, object); + // It should be impossible for a rendered view to be scheduled for + // insertion. + insertElement: function() { + throw new EmberError("You can't insert an element that has already been rendered"); + }, + + setElement: function(view, value) { + if (value === null) { + view.transitionTo('preRender'); } else { - throw new EmberError("Using insertAt on an arranged ArrayProxy is not allowed."); + view.clearBuffer(); + view.transitionTo('hasElement'); } + + return value; }, - removeAt: function(start, len) { - if ('number' === typeof start) { - var content = get(this, 'content'); - var arrangedContent = get(this, 'arrangedContent'); - var indices = []; - var i; + invokeObserver: function(target, observer) { + observer.call(target); + } + }); - if ((start < 0) || (start >= get(this, 'length'))) { - throw new EmberError(OUT_OF_RANGE_EXCEPTION); - } + __exports__["default"] = inBuffer; + }); +define("ember-views/views/states/in_dom", + ["ember-metal/core","ember-metal/platform","ember-metal/merge","ember-metal/error","ember-views/views/states/has_element","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // Ember.assert + var create = __dependency2__.create; + var merge = __dependency3__["default"]; + var EmberError = __dependency4__["default"]; - if (len === undefined) len = 1; + var hasElement = __dependency5__["default"]; + /** + @module ember + @submodule ember-views + */ - // Get a list of indices in original content to remove - for (i=start; i 0 && - indexOf(concatenatedProperties, keyName) >= 0) { - var baseValue = this[keyName]; + /** + If the view is currently inserted into the DOM of a parent view, this + property will point to the parent of the view. - if (baseValue) { - if ('function' === typeof baseValue.concat) { - value = baseValue.concat(value); - } else { - value = makeArray(baseValue).concat(value); - } - } else { - value = makeArray(value); - } - } + @property parentView + @type Ember.View + @default null + */ + parentView: computed('_parentView', function() { + var parent = this._parentView; - if (desc) { - desc.set(this, keyName, value); - } else { - if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) { - this.setUnknownProperty(keyName, value); - } else { - - if (hasPropertyAccessors) { - defineProperty(this, keyName, null, value); // setup mandatory setter - } else { - this[keyName] = value; - } - } - } - } - } + if (parent && parent.isVirtual) { + return get(parent, 'parentView'); + } else { + return parent; } + }), - finishPartial(this, m); + state: null, + + _parentView: null, + + // return the current view, not including virtual views + concreteView: computed('parentView', function() { + if (!this.isVirtual) { return this; } + else { return get(this, 'parentView.concreteView'); } + }), + + instrumentName: 'core_view', + + instrumentDetails: function(hash) { + hash.object = this.toString(); + hash.containerKey = this._debugContainerKey; + hash.view = this; + }, - var length = arguments.length; + /** + Invoked by the view system when this view needs to produce an HTML + representation. This method will create a new render buffer, if needed, + then apply any default attributes, such as class names and visibility. + Finally, the `render()` method is invoked, which is responsible for + doing the bulk of the rendering. - if (length === 0) { - this.init(); - } else if (length === 1) { - this.init(arguments[0]); - } else { - // v8 bug potentially incorrectly deopts this function: https://code.google.com/p/v8/issues/detail?id=3709 - // we may want to keep this around till this ages out on mobile - var args = new Array(length); - for (var x = 0; x < length; x++) { - args[x] = arguments[x]; - } - this.init.apply(this, args); - } + You should not need to override this method; instead, implement the + `template` property, or if you need more control, override the `render` + method. - m.proto = proto; - finishChains(this); - sendEvent(this, 'init'); - }; + @method renderToBuffer + @param {Ember.RenderBuffer} buffer the render buffer. If no buffer is + passed, a default buffer, using the current view's `tagName`, will + be used. + @private + */ + renderToBuffer: function(parentBuffer, bufferOperation) { + var name = 'render.' + this.instrumentName, + details = {}; - Class.toString = Mixin.prototype.toString; - Class.willReopen = function() { - if (wasApplied) { - Class.PrototypeMixin = Mixin.create(Class.PrototypeMixin); - } + this.instrumentDetails(details); - wasApplied = false; - }; - Class._initMixins = function(args) { initMixins = args; }; - Class._initProperties = function(args) { initProperties = args; }; + return instrument(name, details, function instrumentRenderToBuffer() { + return this._renderToBuffer(parentBuffer, bufferOperation); + }, this); + }, - Class.proto = function() { - var superclass = Class.superclass; - if (superclass) { superclass.proto(); } + _renderToBuffer: function(parentBuffer, bufferOperation) { + // If this is the top-most view, start a new buffer. Otherwise, + // create a new buffer relative to the original using the + // provided buffer operation (for example, `insertAfter` will + // insert a new buffer after the "parent buffer"). + var tagName = this.tagName; - if (!wasApplied) { - wasApplied = true; - Class.PrototypeMixin.applyPartial(Class.prototype); + if (tagName === null || tagName === undefined) { + tagName = 'div'; } - return this.prototype; - }; - - return Class; + var buffer = this.buffer = parentBuffer && parentBuffer.begin(tagName) || RenderBuffer(tagName); + this.transitionTo('inBuffer', false); - } + this.beforeRender(buffer); + this.render(buffer); + this.afterRender(buffer); - /** - @class CoreObject - @namespace Ember - */ - var CoreObject = makeCtor(); - CoreObject.toString = function() { return "Ember.CoreObject"; }; - CoreObject.PrototypeMixin = Mixin.create({ - reopen: function() { - var length = arguments.length; - var args = new Array(length); - for (var i = 0; i < length; i++) { - args[i] = arguments[i]; - } - applyMixin(this, args, true); - return this; + return buffer; }, /** - An overridable method called when objects are instantiated. By default, - does nothing unless it is overridden during class definition. - - Example: + Override the default event firing from `Ember.Evented` to + also call methods with the given name. - ```javascript - App.Person = Ember.Object.extend({ - init: function() { - alert('Name is ' + this.get('name')); + @method trigger + @param name {String} + @private + */ + trigger: function(name) { + this._super.apply(this, arguments); + var method = this[name]; + if (method) { + var args = [], i, l; + for (i = 1, l = arguments.length; i < l; i++) { + args.push(arguments[i]); } - }); + return method.apply(this, args); + } + }, - var steve = App.Person.create({ - name: "Steve" - }); + deprecatedSendHandles: function(actionName) { + return !!this[actionName]; + }, - // alerts 'Name is Steve'. - ``` + deprecatedSend: function(actionName) { + var args = [].slice.call(arguments, 1); + Ember.assert('' + this + " has the action " + actionName + " but it is not a function", typeof this[actionName] === 'function'); + Ember.deprecate('Action handlers implemented directly on views are deprecated in favor of action handlers on an `actions` object ( action: `' + actionName + '` on ' + this + ')', false); + this[actionName].apply(this, args); + return; + }, - NOTE: If you do override `init` for a framework class like `Ember.View` or - `Ember.ArrayController`, be sure to call `this._super()` in your - `init` declaration! If you don't, Ember may not have an opportunity to - do important setup work, and you'll see strange behavior in your - application. + has: function(name) { + return typeOf(this[name]) === 'function' || this._super(name); + }, - @method init - */ - init: function() {}, + destroy: function() { + var parent = this._parentView; - /** - Defines the properties that will be concatenated from the superclass - (instead of overridden). + if (!this._super()) { return; } - By default, when you extend an Ember class a property defined in - the subclass overrides a property with the same name that is defined - in the superclass. However, there are some cases where it is preferable - to build up a property's value by combining the superclass' property - value with the subclass' value. An example of this in use within Ember - is the `classNames` property of `Ember.View`. + // destroy the element -- this will avoid each child view destroying + // the element over and over again... + if (!this.removedFromDOM) { this.destroyElement(); } - Here is some sample code showing the difference between a concatenated - property and a normal one: + // remove from parent if found. Don't call removeFromParent, + // as removeFromParent will try to remove the element from + // the DOM again. + if (parent) { parent.removeChild(this); } - ```javascript - App.BarView = Ember.View.extend({ - someNonConcatenatedProperty: ['bar'], - classNames: ['bar'] - }); + this.transitionTo('destroying', false); - App.FooBarView = App.BarView.extend({ - someNonConcatenatedProperty: ['foo'], - classNames: ['foo'] - }); + return this; + }, - var fooBarView = App.FooBarView.create(); - fooBarView.get('someNonConcatenatedProperty'); // ['foo'] - fooBarView.get('classNames'); // ['ember-view', 'bar', 'foo'] - ``` + clearRenderedChildren: Ember.K, + triggerRecursively: Ember.K, + invokeRecursively: Ember.K, + transitionTo: Ember.K, + destroyElement: Ember.K + }); - This behavior extends to object creation as well. Continuing the - above example: + var ViewCollection = function(initialViews) { + var views = this.views = initialViews || []; + this.length = views.length; + }; - ```javascript - var view = App.FooBarView.create({ - someNonConcatenatedProperty: ['baz'], - classNames: ['baz'] - }) - view.get('someNonConcatenatedProperty'); // ['baz'] - view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz'] - ``` - Adding a single property that is not an array will just add it in the array: + ViewCollection.prototype = { + length: 0, - ```javascript - var view = App.FooBarView.create({ - classNames: 'baz' - }) - view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz'] - ``` + trigger: function(eventName) { + var views = this.views, view; + for (var i = 0, l = views.length; i < l; i++) { + view = views[i]; + if (view.trigger) { view.trigger(eventName); } + } + }, - Using the `concatenatedProperties` property, we can tell to Ember that mix - the content of the properties. + triggerRecursively: function(eventName) { + var views = this.views; + for (var i = 0, l = views.length; i < l; i++) { + views[i].triggerRecursively(eventName); + } + }, - In `Ember.View` the `classNameBindings` and `attributeBindings` properties - are also concatenated, in addition to `classNames`. + invokeRecursively: function(fn) { + var views = this.views, view; - This feature is available for you to use throughout the Ember object model, - although typical app developers are likely to use it infrequently. Since - it changes expectations about behavior of properties, you should properly - document its usage in each individual concatenated property (to not - mislead your users to think they can override the property in a subclass). + for (var i = 0, l = views.length; i < l; i++) { + view = views[i]; + fn(view); + } + }, - @property concatenatedProperties - @type Array - @default null - */ - concatenatedProperties: null, + transitionTo: function(state, children) { + var views = this.views; + for (var i = 0, l = views.length; i < l; i++) { + views[i].transitionTo(state, children); + } + }, - /** - Destroyed object property flag. + push: function() { + this.length += arguments.length; + var views = this.views; + return views.push.apply(views, arguments); + }, - if this property is `true` the observers and bindings were already - removed by the effect of calling the `destroy()` method. + objectAt: function(idx) { + return this.views[idx]; + }, - @property isDestroyed - @default false - */ - isDestroyed: false, + forEach: function(callback) { + var views = this.views; + return a_forEach(views, callback); + }, - /** - Destruction scheduled flag. The `destroy()` method has been called. + clear: function() { + this.length = 0; + this.views.length = 0; + } + }; - The object stays intact until the end of the run loop at which point - the `isDestroyed` flag is set. + var EMPTY_ARRAY = []; - @property isDestroying - @default false - */ - isDestroying: false, + /** + `Ember.View` is the class in Ember responsible for encapsulating templates of + HTML content, combining templates with data to render as sections of a page's + DOM, and registering and responding to user-initiated events. - /** - Destroys an object by setting the `isDestroyed` flag and removing its - metadata, which effectively destroys observers and bindings. + ## HTML Tag - If you try to set a property on a destroyed object, an exception will be - raised. + The default HTML tag name used for a view's DOM representation is `div`. This + can be customized by setting the `tagName` property. The following view + class: - Note that destruction is scheduled for the end of the run loop and does not - happen immediately. It will set an isDestroying flag immediately. + ```javascript + ParagraphView = Ember.View.extend({ + tagName: 'em' + }); + ``` - @method destroy - @return {Ember.Object} receiver - */ - destroy: function() { - if (this.isDestroying) { return; } - this.isDestroying = true; + Would result in instances with the following HTML: - schedule('actions', this, this.willDestroy); - schedule('destroy', this, this._scheduledDestroy); - return this; - }, + ```html + + ``` - /** - Override to implement teardown. + ## HTML `class` Attribute - @method willDestroy - */ - willDestroy: K, + The HTML `class` attribute of a view's tag can be set by providing a + `classNames` property that is set to an array of strings: - /** - Invoked by the run loop to actually destroy the object. This is - scheduled for execution by the `destroy` method. + ```javascript + MyView = Ember.View.extend({ + classNames: ['my-class', 'my-other-class'] + }); + ``` - @private - @method _scheduledDestroy - */ - _scheduledDestroy: function() { - if (this.isDestroyed) { return; } - destroy(this); - this.isDestroyed = true; - }, + Will result in view instances with an HTML representation of: - bind: function(to, from) { - if (!(from instanceof Binding)) { from = Binding.from(from); } - from.to(to).connect(this); - return from; - }, + ```html +
+ ``` - /** - Returns a string representation which attempts to provide more information - than Javascript's `toString` typically does, in a generic way for all Ember - objects. + `class` attribute values can also be set by providing a `classNameBindings` + property set to an array of properties names for the view. The return value + of these properties will be added as part of the value for the view's `class` + attribute. These properties can be computed properties: - ```javascript - App.Person = Em.Object.extend() - person = App.Person.create() - person.toString() //=> "" - ``` + ```javascript + MyView = Ember.View.extend({ + classNameBindings: ['propertyA', 'propertyB'], + propertyA: 'from-a', + propertyB: function() { + if (someLogic) { return 'from-b'; } + }.property() + }); + ``` - If the object's class is not defined on an Ember namespace, it will - indicate it is a subclass of the registered superclass: + Will result in view instances with an HTML representation of: - ```javascript - Student = App.Person.extend() - student = Student.create() - student.toString() //=> "<(subclass of App.Person):ember1025>" - ``` + ```html +
+ ``` - If the method `toStringExtension` is defined, its return value will be - included in the output. + If the value of a class name binding returns a boolean the property name + itself will be used as the class name if the property is true. The class name + will not be added if the value is `false` or `undefined`. - ```javascript - App.Teacher = App.Person.extend({ - toStringExtension: function() { - return this.get('fullName'); - } - }); - teacher = App.Teacher.create() - teacher.toString(); //=> "" - ``` + ```javascript + MyView = Ember.View.extend({ + classNameBindings: ['hovered'], + hovered: true + }); + ``` - @method toString - @return {String} string representation - */ - toString: function toString() { - var hasToStringExtension = typeof this.toStringExtension === 'function'; - var extension = hasToStringExtension ? ":" + this.toStringExtension() : ''; - var ret = '<'+this.constructor.toString()+':'+guidFor(this)+extension+'>'; + Will result in view instances with an HTML representation of: - this.toString = makeToString(ret); - return ret; - } - }); + ```html +
+ ``` - CoreObject.PrototypeMixin.ownerConstructor = CoreObject; + When using boolean class name bindings you can supply a string value other + than the property name for use as the `class` HTML attribute by appending the + preferred value after a ":" character when defining the binding: - function makeToString(ret) { - return function() { return ret; }; - } + ```javascript + MyView = Ember.View.extend({ + classNameBindings: ['awesome:so-very-cool'], + awesome: true + }); + ``` - CoreObject.__super__ = null; + Will result in view instances with an HTML representation of: - var ClassMixinProps = { + ```html +
+ ``` - ClassMixin: required(), + Boolean value class name bindings whose property names are in a + camelCase-style format will be converted to a dasherized format: - PrototypeMixin: required(), + ```javascript + MyView = Ember.View.extend({ + classNameBindings: ['isUrgent'], + isUrgent: true + }); + ``` - isClass: true, + Will result in view instances with an HTML representation of: - isMethod: false, + ```html +
+ ``` - /** - Creates a new subclass. + Class name bindings can also refer to object values that are found by + traversing a path relative to the view itself: - ```javascript - App.Person = Ember.Object.extend({ - say: function(thing) { - alert(thing); - } - }); - ``` + ```javascript + MyView = Ember.View.extend({ + classNameBindings: ['messages.empty'] + messages: Ember.Object.create({ + empty: true + }) + }); + ``` - This defines a new subclass of Ember.Object: `App.Person`. It contains one method: `say()`. + Will result in view instances with an HTML representation of: - You can also create a subclass from any existing class by calling its `extend()` method. For example, you might want to create a subclass of Ember's built-in `Ember.View` class: + ```html +
+ ``` - ```javascript - App.PersonView = Ember.View.extend({ - tagName: 'li', - classNameBindings: ['isAdministrator'] - }); - ``` + If you want to add a class name for a property which evaluates to true and + and a different class name if it evaluates to false, you can pass a binding + like this: - When defining a subclass, you can override methods but still access the implementation of your parent class by calling the special `_super()` method: + ```javascript + // Applies 'enabled' class when isEnabled is true and 'disabled' when isEnabled is false + Ember.View.extend({ + classNameBindings: ['isEnabled:enabled:disabled'] + isEnabled: true + }); + ``` - ```javascript - App.Person = Ember.Object.extend({ - say: function(thing) { - var name = this.get('name'); - alert(name + ' says: ' + thing); - } - }); + Will result in view instances with an HTML representation of: - App.Soldier = App.Person.extend({ - say: function(thing) { - this._super(thing + ", sir!"); - }, - march: function(numberOfHours) { - alert(this.get('name') + ' marches for ' + numberOfHours + ' hours.') - } - }); + ```html +
+ ``` - var yehuda = App.Soldier.create({ - name: "Yehuda Katz" - }); + When isEnabled is `false`, the resulting HTML reprensentation looks like + this: - yehuda.say("Yes"); // alerts "Yehuda Katz says: Yes, sir!" - ``` + ```html +
+ ``` - The `create()` on line #17 creates an *instance* of the `App.Soldier` class. The `extend()` on line #8 creates a *subclass* of `App.Person`. Any instance of the `App.Person` class will *not* have the `march()` method. + This syntax offers the convenience to add a class if a property is `false`: - You can also pass `Mixin` classes to add additional properties to the subclass. + ```javascript + // Applies no class when isEnabled is true and class 'disabled' when isEnabled is false + Ember.View.extend({ + classNameBindings: ['isEnabled::disabled'] + isEnabled: true + }); + ``` - ```javascript - App.Person = Ember.Object.extend({ - say: function(thing) { - alert(this.get('name') + ' says: ' + thing); - } - }); + Will result in view instances with an HTML representation of: - App.SingingMixin = Mixin.create({ - sing: function(thing){ - alert(this.get('name') + ' sings: la la la ' + thing); - } - }); + ```html +
+ ``` - App.BroadwayStar = App.Person.extend(App.SingingMixin, { - dance: function() { - alert(this.get('name') + ' dances: tap tap tap tap '); - } - }); - ``` + When the `isEnabled` property on the view is set to `false`, it will result + in view instances with an HTML representation of: - The `App.BroadwayStar` class contains three methods: `say()`, `sing()`, and `dance()`. + ```html +
+ ``` - @method extend - @static + Updates to the the value of a class name binding will result in automatic + update of the HTML `class` attribute in the view's rendered HTML + representation. If the value becomes `false` or `undefined` the class name + will be removed. - @param {Mixin} [mixins]* One or more Mixin classes - @param {Object} [arguments]* Object containing values to use within the new class - */ - extend: function extend() { - var Class = makeCtor(); - var proto; - Class.ClassMixin = Mixin.create(this.ClassMixin); - Class.PrototypeMixin = Mixin.create(this.PrototypeMixin); + Both `classNames` and `classNameBindings` are concatenated properties. See + [Ember.Object](/api/classes/Ember.Object.html) documentation for more + information about concatenated properties. - Class.ClassMixin.ownerConstructor = Class; - Class.PrototypeMixin.ownerConstructor = Class; + ## HTML Attributes - reopen.apply(Class.PrototypeMixin, arguments); + The HTML attribute section of a view's tag can be set by providing an + `attributeBindings` property set to an array of property names on the view. + The return value of these properties will be used as the value of the view's + HTML associated attribute: - Class.superclass = this; - Class.__super__ = this.prototype; + ```javascript + AnchorView = Ember.View.extend({ + tagName: 'a', + attributeBindings: ['href'], + href: 'http://google.com' + }); + ``` + + Will result in view instances with an HTML representation of: + + ```html + + ``` + + One property can be mapped on to another by placing a ":" between + the source property and the destination property: + + ```javascript + AnchorView = Ember.View.extend({ + tagName: 'a', + attributeBindings: ['url:href'], + url: 'http://google.com' + }); + ``` + + Will result in view instances with an HTML representation of: - proto = Class.prototype = o_create(this.prototype); - proto.constructor = Class; - generateGuid(proto); - meta(proto).proto = proto; // this will disable observers on prototype + ```html + + ``` + + If the return value of an `attributeBindings` monitored property is a boolean + the property will follow HTML's pattern of repeating the attribute's name as + its value: - Class.ClassMixin.apply(Class); - return Class; - }, + ```javascript + MyTextInput = Ember.View.extend({ + tagName: 'input', + attributeBindings: ['disabled'], + disabled: true + }); + ``` - /** - Equivalent to doing `extend(arguments).create()`. - If possible use the normal `create` method instead. + Will result in view instances with an HTML representation of: - @method createWithMixins - @static - @param [arguments]* - */ - createWithMixins: function() { - var C = this; - var l= arguments.length; - if (l > 0) { - var args = new Array(l); - for (var i = 0; i < l; i++) { - args[i] = arguments[i]; - } - this._initMixins(args); - } - return new C(); - }, + ```html + + ``` - /** - Creates an instance of a class. Accepts either no arguments, or an object - containing values to initialize the newly instantiated object with. + `attributeBindings` can refer to computed properties: - ```javascript - App.Person = Ember.Object.extend({ - helloWorld: function() { - alert("Hi, my name is " + this.get('name')); + ```javascript + MyTextInput = Ember.View.extend({ + tagName: 'input', + attributeBindings: ['disabled'], + disabled: function() { + if (someLogic) { + return true; + } else { + return false; } - }); + }.property() + }); + ``` - var tom = App.Person.create({ - name: 'Tom Dale' - }); + Updates to the the property of an attribute binding will result in automatic + update of the HTML attribute in the view's rendered HTML representation. - tom.helloWorld(); // alerts "Hi, my name is Tom Dale". - ``` + `attributeBindings` is a concatenated property. See [Ember.Object](/api/classes/Ember.Object.html) + documentation for more information about concatenated properties. - `create` will call the `init` function if defined during - `Ember.AnyObject.extend` + ## Templates - If no arguments are passed to `create`, it will not set values to the new - instance during initialization: + The HTML contents of a view's rendered representation are determined by its + template. Templates can be any function that accepts an optional context + parameter and returns a string of HTML that will be inserted within the + view's tag. Most typically in Ember this function will be a compiled + `Ember.Handlebars` template. - ```javascript - var noName = App.Person.create(); - noName.helloWorld(); // alerts undefined - ``` + ```javascript + AView = Ember.View.extend({ + template: Ember.Handlebars.compile('I am the template') + }); + ``` - NOTE: For performance reasons, you cannot declare methods or computed - properties during `create`. You should instead declare methods and computed - properties when using `extend` or use the `createWithMixins` shorthand. + Will result in view instances with an HTML representation of: - @method create - @static - @param [arguments]* - */ - create: function() { - var C = this; - var l = arguments.length; - if (l > 0) { - var args = new Array(l); - for (var i = 0; i < l; i++) { - args[i] = arguments[i]; - } - this._initProperties(args); - } - return new C(); - }, + ```html +
I am the template
+ ``` - /** - Augments a constructor's prototype with additional - properties and functions: + Within an Ember application is more common to define a Handlebars templates as + part of a page: - ```javascript - MyObject = Ember.Object.extend({ - name: 'an object' - }); + ```html + + ``` - o = MyObject.create(); - o.get('name'); // 'an object' + And associate it by name using a view's `templateName` property: - MyObject.reopen({ - say: function(msg){ - console.log(msg); - } - }) + ```javascript + AView = Ember.View.extend({ + templateName: 'some-template' + }); + ``` - o2 = MyObject.create(); - o2.say("hello"); // logs "hello" + If you have nested resources, your Handlebars template will look like this: - o.say("goodbye"); // logs "goodbye" - ``` + ```html + + ``` - To add functions and properties to the constructor itself, - see `reopenClass` + And `templateName` property: - @method reopen - */ - reopen: function() { - this.willReopen(); + ```javascript + AView = Ember.View.extend({ + templateName: 'posts/new' + }); + ``` - var l = arguments.length; - var args = new Array(l); - if (l > 0) { - for (var i = 0; i < l; i++) { - args[i] = arguments[i]; - } - } + Using a value for `templateName` that does not have a Handlebars template + with a matching `data-template-name` attribute will throw an error. - apply(this.PrototypeMixin, reopen, args); - return this; - }, + For views classes that may have a template later defined (e.g. as the block + portion of a `{{view}}` Handlebars helper call in another template or in + a subclass), you can provide a `defaultTemplate` property set to compiled + template function. If a template is not later provided for the view instance + the `defaultTemplate` value will be used: - /** - Augments a constructor's own properties and functions: + ```javascript + AView = Ember.View.extend({ + defaultTemplate: Ember.Handlebars.compile('I was the default'), + template: null, + templateName: null + }); + ``` - ```javascript - MyObject = Ember.Object.extend({ - name: 'an object' - }); + Will result in instances with an HTML representation of: - MyObject.reopenClass({ - canBuild: false - }); + ```html +
I was the default
+ ``` - MyObject.canBuild; // false - o = MyObject.create(); - ``` + If a `template` or `templateName` is provided it will take precedence over + `defaultTemplate`: - In other words, this creates static properties and functions for the class. These are only available on the class - and not on any instance of that class. + ```javascript + AView = Ember.View.extend({ + defaultTemplate: Ember.Handlebars.compile('I was the default') + }); - ```javascript - App.Person = Ember.Object.extend({ - name : "", - sayHello : function(){ - alert("Hello. My name is " + this.get('name')); - } - }); + aView = AView.create({ + template: Ember.Handlebars.compile('I was the template, not default') + }); + ``` - App.Person.reopenClass({ - species : "Homo sapiens", - createPerson: function(newPersonsName){ - return App.Person.create({ - name:newPersonsName - }); - } - }); + Will result in the following HTML representation when rendered: - var tom = App.Person.create({ - name : "Tom Dale" - }); - var yehuda = App.Person.createPerson("Yehuda Katz"); + ```html +
I was the template, not default
+ ``` - tom.sayHello(); // "Hello. My name is Tom Dale" - yehuda.sayHello(); // "Hello. My name is Yehuda Katz" - alert(App.Person.species); // "Homo sapiens" - ``` + ## View Context - Note that `species` and `createPerson` are *not* valid on the `tom` and `yehuda` - variables. They are only valid on `App.Person`. + The default context of the compiled template is the view's controller: - To add functions and properties to instances of - a constructor by extending the constructor's prototype - see `reopen` + ```javascript + AView = Ember.View.extend({ + template: Ember.Handlebars.compile('Hello {{excitedGreeting}}') + }); - @method reopenClass - */ - reopenClass: function() { - var l = arguments.length; - var args = new Array(l); - if (l > 0) { - for (var i = 0; i < l; i++) { - args[i] = arguments[i]; - } - } + aController = Ember.Object.create({ + firstName: 'Barry', + excitedGreeting: function() { + return this.get("content.firstName") + "!!!" + }.property() + }); - apply(this.ClassMixin, reopen, args); - applyMixin(this, arguments, false); - return this; - }, + aView = AView.create({ + controller: aController, + }); + ``` - detect: function(obj) { - if ('function' !== typeof obj) { return false; } - while(obj) { - if (obj===this) { return true; } - obj = obj.superclass; - } - return false; - }, + Will result in an HTML representation of: - detectInstance: function(obj) { - return obj instanceof this; - }, + ```html +
Hello Barry!!!
+ ``` - /** - In some cases, you may want to annotate computed properties with additional - metadata about how they function or what values they operate on. For - example, computed property functions may close over variables that are then - no longer available for introspection. + A context can also be explicitly supplied through the view's `context` + property. If the view has neither `context` nor `controller` properties, the + `parentView`'s context will be used. - You can pass a hash of these values to a computed property like this: + ## Layouts - ```javascript - person: function() { - var personId = this.get('personId'); - return App.Person.create({ id: personId }); - }.property().meta({ type: App.Person }) - ``` + Views can have a secondary template that wraps their main template. Like + primary templates, layouts can be any function that accepts an optional + context parameter and returns a string of HTML that will be inserted inside + view's tag. Views whose HTML element is self closing (e.g. ``) + cannot have a layout and this property will be ignored. - Once you've done this, you can retrieve the values saved to the computed - property from your class like this: + Most typically in Ember a layout will be a compiled `Ember.Handlebars` + template. - ```javascript - MyClass.metaForProperty('person'); - ``` + A view's layout can be set directly with the `layout` property or reference + an existing Handlebars template by name with the `layoutName` property. - This will return the original hash that was passed to `meta()`. + A template used as a layout must contain a single use of the Handlebars + `{{yield}}` helper. The HTML contents of a view's rendered `template` will be + inserted at this location: - @method metaForProperty - @param key {String} property name - */ - metaForProperty: function(key) { - var meta = this.proto()['__ember_meta__']; - var desc = meta && meta.descs[key]; + ```javascript + AViewWithLayout = Ember.View.extend({ + layout: Ember.Handlebars.compile("
{{yield}}
") + template: Ember.Handlebars.compile("I got wrapped"), + }); + ``` - Ember.assert("metaForProperty() could not find a computed property with key '"+key+"'.", !!desc && desc instanceof ComputedProperty); - return desc._meta || {}; - }, + Will result in view instances with an HTML representation of: - _computedProperties: computed(function() { - hasCachedComputedProperties = true; - var proto = this.proto(); - var descs = meta(proto).descs; - var property; - var properties = []; + ```html +
+
+ I got wrapped +
+
+ ``` - for (var name in descs) { - property = descs[name]; + See [Ember.Handlebars.helpers.yield](/api/classes/Ember.Handlebars.helpers.html#method_yield) + for more information. - if (property instanceof ComputedProperty) { - properties.push({ - name: name, - meta: property._meta - }); - } - } - return properties; - }).readOnly(), + ## Responding to Browser Events - /** - Iterate over each computed property for the class, passing its name - and any associated metadata (see `metaForProperty`) to the callback. + Views can respond to user-initiated events in one of three ways: method + implementation, through an event manager, and through `{{action}}` helper use + in their template or layout. - @method eachComputedProperty - @param {Function} callback - @param {Object} binding - */ - eachComputedProperty: function(callback, binding) { - var property, name; - var empty = {}; + ### Method Implementation - var properties = get(this, '_computedProperties'); + Views can respond to user-initiated events by implementing a method that + matches the event name. A `jQuery.Event` object will be passed as the + argument to this method. - for (var i = 0, length = properties.length; i < length; i++) { - property = properties[i]; - name = property.name; - callback.call(binding || this, property.name, property.meta || empty); + ```javascript + AView = Ember.View.extend({ + click: function(event) { + // will be called when when an instance's + // rendered element is clicked } - } - }; + }); + ``` - function injectedPropertyAssertion() { - Ember.assert("Injected properties are invalid", validatePropertyInjections(this)); - } + ### Event Managers - function addOnLookupHandler() { - Ember.runInDebug(function() { - /** - Provides lookup-time type validation for injected properties. + Views can define an object as their `eventManager` property. This object can + then implement methods that match the desired event names. Matching events + that occur on the view's rendered HTML or the rendered HTML of any of its DOM + descendants will trigger this method. A `jQuery.Event` object will be passed + as the first argument to the method and an `Ember.View` object as the + second. The `Ember.View` will be the view whose rendered HTML was interacted + with. This may be the view with the `eventManager` property or one of its + descendent views. - @private - @method _onLookup - */ - ClassMixinProps._onLookup = injectedPropertyAssertion; + ```javascript + AView = Ember.View.extend({ + eventManager: Ember.Object.create({ + doubleClick: function(event, view) { + // will be called when when an instance's + // rendered element or any rendering + // of this views's descendent + // elements is clicked + } + }) }); - } - - - addOnLookupHandler(); - - /** - Returns a hash of property names and container names that injected - properties will lookup on the container lazily. + ``` - @method _lazyInjections - @return {Object} Hash of all lazy injected property keys to container names - */ - ClassMixinProps._lazyInjections = function() { - var injections = {}; - var proto = this.proto(); - var descs = meta(proto).descs; - var key, desc; + An event defined for an event manager takes precedence over events of the + same name handled through methods on the view. - for (key in descs) { - desc = descs[key]; - if (desc instanceof InjectedProperty) { - injections[key] = desc.type + ':' + (desc.name || key); + ```javascript + AView = Ember.View.extend({ + mouseEnter: function(event) { + // will never trigger. + }, + eventManager: Ember.Object.create({ + mouseEnter: function(event, view) { + // takes precedence over AView#mouseEnter } - } + }) + }); + ``` - return injections; - }; - + Similarly a view's event manager will take precedence for events of any views + rendered as a descendent. A method name that matches an event name will not + be called if the view instance was rendered inside the HTML representation of + a view that has an `eventManager` property defined that handles events of the + name. Events not handled by the event manager will still trigger method calls + on the descendent. - var ClassMixin = Mixin.create(ClassMixinProps); + ```javascript + OuterView = Ember.View.extend({ + template: Ember.Handlebars.compile("outer {{#view InnerView}}inner{{/view}} outer"), + eventManager: Ember.Object.create({ + mouseEnter: function(event, view) { + // view might be instance of either + // OuterView or InnerView depending on + // where on the page the user interaction occured + } + }) + }); - ClassMixin.ownerConstructor = CoreObject; + InnerView = Ember.View.extend({ + click: function(event) { + // will be called if rendered inside + // an OuterView because OuterView's + // eventManager doesn't handle click events + }, + mouseEnter: function(event) { + // will never be called if rendered inside + // an OuterView. + } + }); + ``` - CoreObject.ClassMixin = ClassMixin; + ### Handlebars `{{action}}` Helper - ClassMixin.apply(CoreObject); + See [Handlebars.helpers.action](/api/classes/Ember.Handlebars.helpers.html#method_action). - CoreObject.reopen({ - didDefineProperty: function(proto, key, value) { - if (hasCachedComputedProperties === false) { return; } - if (value instanceof Ember.ComputedProperty) { - var cache = Ember.meta(this.constructor).cache; + ### Event Names - if (cache._computedProperties !== undefined) { - cache._computedProperties = undefined; - } - } - } - }); + All of the event handling approaches described above respond to the same set + of events. The names of the built-in events are listed below. (The hash of + built-in events exists in `Ember.EventDispatcher`.) Additional, custom events + can be registered by using `Ember.Application.customEvents`. - __exports__["default"] = CoreObject; - }); -enifed("ember-runtime/system/deferred", - ["ember-metal/core","ember-runtime/mixins/deferred","ember-runtime/system/object","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - var DeferredMixin = __dependency2__["default"]; - var EmberObject = __dependency3__["default"]; + Touch events: - var Deferred = EmberObject.extend(DeferredMixin, { - init: function() { - Ember.deprecate('Usage of Ember.Deferred is deprecated.'); - this._super(); - } - }); + * `touchStart` + * `touchMove` + * `touchEnd` + * `touchCancel` - Deferred.reopenClass({ - promise: function(callback, binding) { - var deferred = Deferred.create(); - callback.call(binding, deferred); - return deferred; - } - }); + Keyboard events - __exports__["default"] = Deferred; - }); -enifed("ember-runtime/system/each_proxy", - ["ember-metal/core","ember-metal/property_get","ember-metal/utils","ember-metal/enumerable_utils","ember-metal/array","ember-runtime/mixins/array","ember-runtime/system/object","ember-metal/computed","ember-metal/observer","ember-metal/events","ember-metal/properties","ember-metal/property_events","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-runtime - */ + * `keyDown` + * `keyUp` + * `keyPress` - var Ember = __dependency1__["default"]; - // Ember.assert + Mouse events - var get = __dependency2__.get; - var guidFor = __dependency3__.guidFor; - var forEach = __dependency4__.forEach; - var indexOf = __dependency5__.indexOf; - var EmberArray = __dependency6__["default"]; - // ES6TODO: WAT? Circular dep? - var EmberObject = __dependency7__["default"]; - var computed = __dependency8__.computed; - var addObserver = __dependency9__.addObserver; - var addBeforeObserver = __dependency9__.addBeforeObserver; - var removeBeforeObserver = __dependency9__.removeBeforeObserver; - var removeObserver = __dependency9__.removeObserver; - var typeOf = __dependency3__.typeOf; - var watchedEvents = __dependency10__.watchedEvents; - var defineProperty = __dependency11__.defineProperty; - var beginPropertyChanges = __dependency12__.beginPropertyChanges; - var propertyDidChange = __dependency12__.propertyDidChange; - var propertyWillChange = __dependency12__.propertyWillChange; - var endPropertyChanges = __dependency12__.endPropertyChanges; - var changeProperties = __dependency12__.changeProperties; + * `mouseDown` + * `mouseUp` + * `contextMenu` + * `click` + * `doubleClick` + * `mouseMove` + * `focusIn` + * `focusOut` + * `mouseEnter` + * `mouseLeave` - var EachArray = EmberObject.extend(EmberArray, { + Form events: - init: function(content, keyName, owner) { - this._super(); - this._keyName = keyName; - this._owner = owner; - this._content = content; - }, + * `submit` + * `change` + * `focusIn` + * `focusOut` + * `input` - objectAt: function(idx) { - var item = this._content.objectAt(idx); - return item && get(item, this._keyName); - }, + HTML5 drag and drop events: - length: computed(function() { - var content = this._content; - return content ? get(content, 'length') : 0; - }) + * `dragStart` + * `drag` + * `dragEnter` + * `dragLeave` + * `dragOver` + * `dragEnd` + * `drop` - }); + ## Handlebars `{{view}}` Helper - var IS_OBSERVER = /^.+:(before|change)$/; + Other `Ember.View` instances can be included as part of a view's template by + using the `{{view}}` Handlebars helper. See [Ember.Handlebars.helpers.view](/api/classes/Ember.Handlebars.helpers.html#method_view) + for additional information. - function addObserverForContentKey(content, keyName, proxy, idx, loc) { - var objects = proxy._objects; - var guid; - if (!objects) objects = proxy._objects = {}; + @class View + @namespace Ember + @extends Ember.CoreView + */ + var View = CoreView.extend({ - while(--loc>=idx) { - var item = content.objectAt(loc); - if (item) { - Ember.assert('When using @each to observe the array ' + content + ', the array must return an object', typeOf(item) === 'instance' || typeOf(item) === 'object'); - addBeforeObserver(item, keyName, proxy, 'contentKeyWillChange'); - addObserver(item, keyName, proxy, 'contentKeyDidChange'); + concatenatedProperties: ['classNames', 'classNameBindings', 'attributeBindings'], - // keep track of the index each item was found at so we can map - // it back when the obj changes. - guid = guidFor(item); - if (!objects[guid]) objects[guid] = []; - objects[guid].push(loc); - } - } - } + /** + @property isView + @type Boolean + @default true + @static + */ + isView: true, - function removeObserverForContentKey(content, keyName, proxy, idx, loc) { - var objects = proxy._objects; - if (!objects) objects = proxy._objects = {}; - var indicies, guid; + // .......................................................... + // TEMPLATE SUPPORT + // - while(--loc>=idx) { - var item = content.objectAt(loc); - if (item) { - removeBeforeObserver(item, keyName, proxy, 'contentKeyWillChange'); - removeObserver(item, keyName, proxy, 'contentKeyDidChange'); + /** + The name of the template to lookup if no template is provided. - guid = guidFor(item); - indicies = objects[guid]; - indicies[indexOf.call(indicies, loc)] = null; - } - } - } + By default `Ember.View` will lookup a template with this name in + `Ember.TEMPLATES` (a shared global object). - /** - This is the object instance returned when you get the `@each` property on an - array. It uses the unknownProperty handler to automatically create - EachArray instances for property names. + @property templateName + @type String + @default null + */ + templateName: null, - @private - @class EachProxy - @namespace Ember - @extends Ember.Object - */ - var EachProxy = EmberObject.extend({ + /** + The name of the layout to lookup if no layout is provided. - init: function(content) { - this._super(); - this._content = content; - content.addArrayObserver(this); + By default `Ember.View` will lookup a template with this name in + `Ember.TEMPLATES` (a shared global object). - // in case someone is already observing some keys make sure they are - // added - forEach(watchedEvents(this), function(eventName) { - this.didAddListener(eventName); - }, this); - }, + @property layoutName + @type String + @default null + */ + layoutName: null, /** - You can directly access mapped properties by simply requesting them. - The `unknownProperty` handler will generate an EachArray of each item. + Used to identify this view during debugging - @method unknownProperty - @param keyName {String} - @param value {*} + @property instrumentDisplay + @type String */ - unknownProperty: function(keyName, value) { - var ret; - ret = new EachArray(this._content, keyName, this); - defineProperty(this, keyName, null, ret); - this.beginObservingContentKey(keyName); - return ret; - }, + instrumentDisplay: computed(function() { + if (this.helperName) { + return '{{' + this.helperName + '}}'; + } + }), - // .......................................................... - // ARRAY CHANGES - // Invokes whenever the content array itself changes. + /** + The template used to render the view. This should be a function that + accepts an optional context parameter and returns a string of HTML that + will be inserted into the DOM relative to its parent view. - arrayWillChange: function(content, idx, removedCnt, addedCnt) { - var keys = this._keys; - var key, lim; + In general, you should set the `templateName` property instead of setting + the template yourself. - lim = removedCnt>0 ? idx+removedCnt : -1; - beginPropertyChanges(this); + @property template + @type Function + */ + template: computed('templateName', function(key, value) { + if (value !== undefined) { return value; } - for(key in keys) { - if (!keys.hasOwnProperty(key)) { continue; } + var templateName = get(this, 'templateName'), + template = this.templateForName(templateName, 'template'); - if (lim>0) { removeObserverForContentKey(content, key, this, idx, lim); } + Ember.assert("You specified the templateName " + templateName + " for " + this + ", but it did not exist.", !templateName || template); - propertyWillChange(this, key); - } + return template || get(this, 'defaultTemplate'); + }), - propertyWillChange(this._content, '@each'); - endPropertyChanges(this); - }, + /** + The controller managing this view. If this property is set, it will be + made available for use by the template. - arrayDidChange: function(content, idx, removedCnt, addedCnt) { - var keys = this._keys; - var lim; + @property controller + @type Object + */ + controller: computed('_parentView', function(key) { + var parentView = get(this, '_parentView'); + return parentView ? get(parentView, 'controller') : null; + }), - lim = addedCnt>0 ? idx+addedCnt : -1; - changeProperties(function() { - for(var key in keys) { - if (!keys.hasOwnProperty(key)) { continue; } + /** + A view may contain a layout. A layout is a regular template but + supersedes the `template` property during rendering. It is the + responsibility of the layout template to retrieve the `template` + property from the view (or alternatively, call `Handlebars.helpers.yield`, + `{{yield}}`) to render it in the correct location. - if (lim>0) { addObserverForContentKey(content, key, this, idx, lim); } + This is useful for a view that has a shared wrapper, but which delegates + the rendering of the contents of the wrapper to the `template` property + on a subclass. - propertyDidChange(this, key); - } + @property layout + @type Function + */ + layout: computed(function(key) { + var layoutName = get(this, 'layoutName'), + layout = this.templateForName(layoutName, 'layout'); - propertyDidChange(this._content, '@each'); - }, this); - }, + Ember.assert("You specified the layoutName " + layoutName + " for " + this + ", but it did not exist.", !layoutName || layout); - // .......................................................... - // LISTEN FOR NEW OBSERVERS AND OTHER EVENT LISTENERS - // Start monitoring keys based on who is listening... + return layout || get(this, 'defaultLayout'); + }).property('layoutName'), - didAddListener: function(eventName) { - if (IS_OBSERVER.test(eventName)) { - this.beginObservingContentKey(eventName.slice(0, -7)); - } + _yield: function(context, options) { + var template = get(this, 'template'); + if (template) { template(context, options); } }, - didRemoveListener: function(eventName) { - if (IS_OBSERVER.test(eventName)) { - this.stopObservingContentKey(eventName.slice(0, -7)); - } + templateForName: function(name, type) { + if (!name) { return; } + Ember.assert("templateNames are not allowed to contain periods: "+name, name.indexOf('.') === -1); + + // the defaultContainer is deprecated + var container = this.container || (Container && Container.defaultContainer); + return container && container.lookup('template:' + name); }, - // .......................................................... - // CONTENT KEY OBSERVING - // Actual watch keys on the source content. + /** + The object from which templates should access properties. - beginObservingContentKey: function(keyName) { - var keys = this._keys; - if (!keys) keys = this._keys = {}; - if (!keys[keyName]) { - keys[keyName] = 1; - var content = this._content; - var len = get(content, 'length'); + This object will be passed to the template function each time the render + method is called, but it is up to the individual function to decide what + to do with it. - addObserverForContentKey(content, keyName, this, 0, len); + By default, this will be the view's controller. + + @property context + @type Object + */ + context: computed(function(key, value) { + if (arguments.length === 2) { + set(this, '_context', value); + return value; } else { - keys[keyName]++; + return get(this, '_context'); } - }, + }).volatile(), - stopObservingContentKey: function(keyName) { - var keys = this._keys; - if (keys && (keys[keyName]>0) && (--keys[keyName]<=0)) { - var content = this._content; - var len = get(content, 'length'); + /** + Private copy of the view's template context. This can be set directly + by Handlebars without triggering the observer that causes the view + to be re-rendered. - removeObserverForContentKey(content, keyName, this, 0, len); - } - }, + The context of a view is looked up as follows: - contentKeyWillChange: function(obj, keyName) { - propertyWillChange(this, keyName); - }, + 1. Supplied context (usually by Handlebars) + 2. Specified controller + 3. `parentView`'s context (for a child of a ContainerView) - contentKeyDidChange: function(obj, keyName) { - propertyDidChange(this, keyName); - } - }); + The code in Handlebars that overrides the `_context` property first + checks to see whether the view has a specified controller. This is + something of a hack and should be revisited. - __exports__.EachArray = EachArray; - __exports__.EachProxy = EachProxy; - }); -enifed("ember-runtime/system/lazy_load", - ["ember-metal/core","ember-metal/array","ember-runtime/system/native_array","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - /*globals CustomEvent */ + @property _context + @private + */ + _context: computed(function(key) { + var parentView, controller; - var Ember = __dependency1__["default"]; - // Ember.ENV.EMBER_LOAD_HOOKS - var forEach = __dependency2__.forEach; - // make sure Ember.A is setup. + if (controller = get(this, 'controller')) { + return controller; + } - /** - @module ember - @submodule ember-runtime - */ + parentView = this._parentView; + if (parentView) { + return get(parentView, '_context'); + } - var loadHooks = Ember.ENV.EMBER_LOAD_HOOKS || {}; - var loaded = {}; + return null; + }), - /** - Detects when a specific package of Ember (e.g. 'Ember.Handlebars') - has fully loaded and is available for extension. + /** + If a value that affects template rendering changes, the view should be + re-rendered to reflect the new value. - The provided `callback` will be called with the `name` passed - resolved from a string into the object: + @method _contextDidChange + @private + */ + _contextDidChange: observer('context', function() { + this.rerender(); + }), - ``` javascript - Ember.onLoad('Ember.Handlebars' function(hbars) { - hbars.registerHelper(...); - }); - ``` + /** + If `false`, the view will appear hidden in DOM. - @method onLoad - @for Ember - @param name {String} name of hook - @param callback {Function} callback to be called - */ - function onLoad(name, callback) { - var object; + @property isVisible + @type Boolean + @default null + */ + isVisible: true, - loadHooks[name] = loadHooks[name] || Ember.A(); - loadHooks[name].pushObject(callback); + /** + Array of child views. You should never edit this array directly. + Instead, use `appendChild` and `removeFromParent`. - if (object = loaded[name]) { - callback(object); - } - } + @property childViews + @type Array + @default [] + @private + */ + childViews: childViewsProperty, - __exports__.onLoad = onLoad;/** - Called when an Ember.js package (e.g Ember.Handlebars) has finished - loading. Triggers any callbacks registered for this event. + _childViews: EMPTY_ARRAY, - @method runLoadHooks - @for Ember - @param name {String} name of hook - @param object {Object} object to pass to callbacks - */ - function runLoadHooks(name, object) { - loaded[name] = object; + // When it's a virtual view, we need to notify the parent that their + // childViews will change. + _childViewsWillChange: beforeObserver('childViews', function() { + if (this.isVirtual) { + var parentView = get(this, 'parentView'); + if (parentView) { propertyWillChange(parentView, 'childViews'); } + } + }), - if (typeof window === 'object' && typeof window.dispatchEvent === 'function' && typeof CustomEvent === "function") { - var event = new CustomEvent(name, {detail: object, name: name}); - window.dispatchEvent(event); - } + // When it's a virtual view, we need to notify the parent that their + // childViews did change. + _childViewsDidChange: observer('childViews', function() { + if (this.isVirtual) { + var parentView = get(this, 'parentView'); + if (parentView) { propertyDidChange(parentView, 'childViews'); } + } + }), - if (loadHooks[name]) { - forEach.call(loadHooks[name], function(callback) { - callback(object); - }); - } - } + /** + Return the nearest ancestor that is an instance of the provided + class. - __exports__.runLoadHooks = runLoadHooks; - }); -enifed("ember-runtime/system/namespace", - ["ember-metal/core","ember-metal/property_get","ember-metal/array","ember-metal/utils","ember-metal/mixin","ember-runtime/system/object","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-runtime - */ + @method nearestInstanceOf + @param {Class} klass Subclass of Ember.View (or Ember.View itself) + @return Ember.View + @deprecated + */ + nearestInstanceOf: function(klass) { + Ember.deprecate("nearestInstanceOf is deprecated and will be removed from future releases. Use nearestOfType."); + var view = get(this, 'parentView'); - // Ember.lookup, Ember.BOOTED, Ember.deprecate, Ember.NAME_KEY, Ember.anyUnprocessedMixins - var Ember = __dependency1__["default"]; - var get = __dependency2__.get; - var indexOf = __dependency3__.indexOf; - var GUID_KEY = __dependency4__.GUID_KEY; - var guidFor = __dependency4__.guidFor; - var Mixin = __dependency5__.Mixin; + while (view) { + if (view instanceof klass) { return view; } + view = get(view, 'parentView'); + } + }, - var EmberObject = __dependency6__["default"]; + /** + Return the nearest ancestor that is an instance of the provided + class or mixin. - /** - A Namespace is an object usually used to contain other objects or methods - such as an application or framework. Create a namespace anytime you want - to define one of these new containers. + @method nearestOfType + @param {Class,Mixin} klass Subclass of Ember.View (or Ember.View itself), + or an instance of Ember.Mixin. + @return Ember.View + */ + nearestOfType: function(klass) { + var view = get(this, 'parentView'), + isOfType = klass instanceof Mixin ? + function(view) { return klass.detect(view); } : + function(view) { return klass.detect(view.constructor); }; - # Example Usage + while (view) { + if (isOfType(view)) { return view; } + view = get(view, 'parentView'); + } + }, - ```javascript - MyFramework = Ember.Namespace.create({ - VERSION: '1.0.0' - }); - ``` + /** + Return the nearest ancestor that has a given property. - @class Namespace - @namespace Ember - @extends Ember.Object - */ - var Namespace = EmberObject.extend({ - isNamespace: true, + @method nearestWithProperty + @param {String} property A property name + @return Ember.View + */ + nearestWithProperty: function(property) { + var view = get(this, 'parentView'); - init: function() { - Namespace.NAMESPACES.push(this); - Namespace.PROCESSED = false; + while (view) { + if (property in view) { return view; } + view = get(view, 'parentView'); + } }, - toString: function() { - var name = get(this, 'name') || get(this, 'modulePrefix'); - if (name) { return name; } + /** + Return the nearest ancestor whose parent is an instance of + `klass`. - findNamespaces(); - return this[NAME_KEY]; - }, + @method nearestChildOf + @param {Class} klass Subclass of Ember.View (or Ember.View itself) + @return Ember.View + */ + nearestChildOf: function(klass) { + var view = get(this, 'parentView'); - nameClasses: function() { - processNamespace([this.toString()], this, {}); + while (view) { + if (get(view, 'parentView') instanceof klass) { return view; } + view = get(view, 'parentView'); + } }, - destroy: function() { - var namespaces = Namespace.NAMESPACES; - var toString = this.toString(); + /** + When the parent view changes, recursively invalidate `controller` - if (toString) { - Ember.lookup[toString] = undefined; - delete Namespace.NAMESPACES_BY_ID[toString]; - } - namespaces.splice(indexOf.call(namespaces, this), 1); - this._super(); - } - }); + @method _parentViewDidChange + @private + */ + _parentViewDidChange: observer('_parentView', function() { + if (this.isDestroying) { return; } - Namespace.reopenClass({ - NAMESPACES: [Ember], - NAMESPACES_BY_ID: {}, - PROCESSED: false, - processAll: processAllNamespaces, - byName: function(name) { - if (!Ember.BOOTED) { - processAllNamespaces(); + this.trigger('parentViewDidChange'); + + if (get(this, 'parentView.controller') && !get(this, 'controller')) { + this.notifyPropertyChange('controller'); } + }), + + _controllerDidChange: observer('controller', function() { + if (this.isDestroying) { return; } + + this.rerender(); + + this.forEachChildView(function(view) { + view.propertyDidChange('controller'); + }); + }), - return NAMESPACES_BY_ID[name]; - } - }); + cloneKeywords: function() { + var templateData = get(this, 'templateData'); - var NAMESPACES_BY_ID = Namespace.NAMESPACES_BY_ID; + var keywords = templateData ? copy(templateData.keywords) : {}; + set(keywords, 'view', get(this, 'concreteView')); + set(keywords, '_view', this); + set(keywords, 'controller', get(this, 'controller')); - var hasOwnProp = ({}).hasOwnProperty; + return keywords; + }, - function processNamespace(paths, root, seen) { - var idx = paths.length; + /** + Called on your view when it should push strings of HTML into a + `Ember.RenderBuffer`. Most users will want to override the `template` + or `templateName` properties instead of this method. - NAMESPACES_BY_ID[paths.join('.')] = root; + By default, `Ember.View` will look for a function in the `template` + property and invoke it with the value of `context`. The value of + `context` will be the view's controller unless you override it. - // Loop over all of the keys in the namespace, looking for classes - for(var key in root) { - if (!hasOwnProp.call(root, key)) { continue; } - var obj = root[key]; + @method render + @param {Ember.RenderBuffer} buffer The render buffer + */ + render: function(buffer) { + // If this view has a layout, it is the responsibility of the + // the layout to render the view's template. Otherwise, render the template + // directly. + var template = get(this, 'layout') || get(this, 'template'); - // If we are processing the `Ember` namespace, for example, the - // `paths` will start with `["Ember"]`. Every iteration through - // the loop will update the **second** element of this list with - // the key, so processing `Ember.View` will make the Array - // `['Ember', 'View']`. - paths[idx] = key; + if (template) { + var context = get(this, 'context'); + var keywords = this.cloneKeywords(); + var output; - // If we have found an unprocessed class - if (obj && obj.toString === classToString) { - // Replace the class' `toString` with the dot-separated path - // and set its `NAME_KEY` - obj.toString = makeToString(paths.join('.')); - obj[NAME_KEY] = paths.join('.'); + var data = { + view: this, + buffer: buffer, + isRenderData: true, + keywords: keywords, + insideGroup: get(this, 'templateData.insideGroup') + }; - // Support nested namespaces - } else if (obj && obj.isNamespace) { - // Skip aliased namespaces - if (seen[guidFor(obj)]) { continue; } - seen[guidFor(obj)] = true; + // Invoke the template with the provided template context, which + // is the view's controller by default. A hash of data is also passed that provides + // the template with access to the view and render buffer. - // Process the child namespace - processNamespace(paths, obj, seen); - } - } + Ember.assert('template must be a function. Did you mean to call Ember.Handlebars.compile("...") or specify templateName instead?', typeof template === 'function'); + // The template should write directly to the render buffer instead + // of returning a string. + output = template(context, { data: data }); - paths.length = idx; // cut out last item - } + // If the template returned a string instead of writing to the buffer, + // push the string onto the buffer. + if (output !== undefined) { buffer.push(output); } + } + }, - var STARTS_WITH_UPPERCASE = /^[A-Z]/; + /** + Renders the view again. This will work regardless of whether the + view is already in the DOM or not. If the view is in the DOM, the + rendering process will be deferred to give bindings a chance + to synchronize. - function tryIsNamespace(lookup, prop) { - try { - var obj = lookup[prop]; - return obj && obj.isNamespace && obj; - } catch (e) { - // continue - } - } + If children were added during the rendering process using `appendChild`, + `rerender` will remove them, because they will be added again + if needed by the next `render`. - function findNamespaces() { - var lookup = Ember.lookup; - var obj; + In general, if the display of your view changes, you should modify + the DOM element directly instead of manually calling `rerender`, which can + be slow. - if (Namespace.PROCESSED) { return; } + @method rerender + */ + rerender: function() { + return this.currentState.rerender(this); + }, - for (var prop in lookup) { - // Only process entities that start with uppercase A-Z - if (!STARTS_WITH_UPPERCASE.test(prop)) { continue; } + clearRenderedChildren: function() { + var lengthBefore = this.lengthBeforeRender, + lengthAfter = this.lengthAfterRender; - // Unfortunately, some versions of IE don't support window.hasOwnProperty - if (lookup.hasOwnProperty && !lookup.hasOwnProperty(prop)) { continue; } + // If there were child views created during the last call to render(), + // remove them under the assumption that they will be re-created when + // we re-render. - // At times we are not allowed to access certain properties for security reasons. - // There are also times where even if we can access them, we are not allowed to access their properties. - obj = tryIsNamespace(lookup, prop); - if (obj) { - obj[NAME_KEY] = prop; + // VIEW-TODO: Unit test this path. + var childViews = this._childViews; + for (var i=lengthAfter-1; i>=lengthBefore; i--) { + if (childViews[i]) { childViews[i].destroy(); } } - } - } - - var NAME_KEY = Ember.NAME_KEY = GUID_KEY + '_name'; + }, - function superClassString(mixin) { - var superclass = mixin.superclass; - if (superclass) { - if (superclass[NAME_KEY]) { return superclass[NAME_KEY]; } - else { return superClassString(superclass); } - } else { - return; - } - } + /** + Iterates over the view's `classNameBindings` array, inserts the value + of the specified property into the `classNames` array, then creates an + observer to update the view's element if the bound property ever changes + in the future. - function classToString() { - if (!Ember.BOOTED && !this[NAME_KEY]) { - processAllNamespaces(); - } + @method _applyClassNameBindings + @private + */ + _applyClassNameBindings: function(classBindings) { + var classNames = this.classNames, + elem, newClass, dasherizedClass; - var ret; + // Loop through all of the configured bindings. These will be either + // property names ('isUrgent') or property paths relative to the view + // ('content.isUrgent') + a_forEach(classBindings, function(binding) { - if (this[NAME_KEY]) { - ret = this[NAME_KEY]; - } else if (this._toString) { - ret = this._toString; - } else { - var str = superClassString(this); - if (str) { - ret = "(subclass of " + str + ")"; - } else { - ret = "(unknown mixin)"; - } - this.toString = makeToString(ret); - } + Ember.assert("classNameBindings must not have spaces in them. Multiple class name bindings can be provided as elements of an array, e.g. ['foo', ':bar']", binding.indexOf(' ') === -1); - return ret; - } + // Variable in which the old class value is saved. The observer function + // closes over this variable, so it knows which string to remove when + // the property changes. + var oldClass; + // Extract just the property name from bindings like 'foo:bar' + var parsedPath = View._parsePropertyPath(binding); - function processAllNamespaces() { - var unprocessedNamespaces = !Namespace.PROCESSED; - var unprocessedMixins = Ember.anyUnprocessedMixins; + // Set up an observer on the context. If the property changes, toggle the + // class name. + var observer = function() { + // Get the current value of the property + newClass = this._classStringForProperty(binding); + elem = this.$(); - if (unprocessedNamespaces) { - findNamespaces(); - Namespace.PROCESSED = true; - } + // If we had previously added a class to the element, remove it. + if (oldClass) { + elem.removeClass(oldClass); + // Also remove from classNames so that if the view gets rerendered, + // the class doesn't get added back to the DOM. + classNames.removeObject(oldClass); + } - if (unprocessedNamespaces || unprocessedMixins) { - var namespaces = Namespace.NAMESPACES; - var namespace; + // If necessary, add a new class. Make sure we keep track of it so + // it can be removed in the future. + if (newClass) { + elem.addClass(newClass); + oldClass = newClass; + } else { + oldClass = null; + } + }; - for (var i=0, l=namespaces.length; i 0) { - NativeArray = NativeArray.without.apply(NativeArray, ignore); - } + defineProperty(this, key); + return set(this, key, value); + }, - /** - Creates an `Ember.NativeArray` from an Array like object. - Does not modify the original object. Ember.A is not needed if - `Ember.EXTEND_PROTOTYPES` is `true` (the default value). However, - it is recommended that you use Ember.A when creating addons for - ember or when you can not guarantee that `Ember.EXTEND_PROTOTYPES` - will be `true`. + /** + Given a property name, returns a dasherized version of that + property name if the property evaluates to a non-falsy value. - Example + For example, if the view has property `isUrgent` that evaluates to true, + passing `isUrgent` to this method will return `"is-urgent"`. - ```js - var Pagination = Ember.CollectionView.extend({ - tagName: 'ul', - classNames: ['pagination'], + @method _classStringForProperty + @param property + @private + */ + _classStringForProperty: function(property) { + var parsedPath = View._parsePropertyPath(property); + var path = parsedPath.path; - init: function() { - this._super(); - if (!this.get('content')) { - this.set('content', Ember.A()); - } + var val = get(this, path); + if (val === undefined && isGlobalPath(path)) { + val = get(Ember.lookup, path); } - }); - ``` - @method A - @for Ember - @return {Ember.NativeArray} - */ - var A = function(arr) { - if (arr === undefined) { arr = []; } - return EmberArray.detect(arr) ? arr : NativeArray.apply(arr); - }; + return View._classStringForValue(path, val, parsedPath.className, parsedPath.falsyClassName); + }, - /** - Activates the mixin on the Array.prototype if not already applied. Calling - this method more than once is safe. This will be called when ember is loaded - unless you have `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Array` - set to `false`. + // .......................................................... + // ELEMENT SUPPORT + // - Example + /** + Returns the current DOM element for the view. - ```js - if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Array) { - Ember.NativeArray.activate(); - } - ``` + @property element + @type DOMElement + */ + element: computed('_parentView', function(key, value) { + if (value !== undefined) { + return this.currentState.setElement(this, value); + } else { + return this.currentState.getElement(this); + } + }), - @method activate - @for Ember.NativeArray - @static - @return {void} - */ - NativeArray.activate = function() { - NativeArray.apply(Array.prototype); + /** + Returns a jQuery object for this view's element. If you pass in a selector + string, this method will return a jQuery object, using the current element + as its buffer. - A = function(arr) { return arr || []; }; - }; + For example, calling `view.$('li')` will return a jQuery object containing + all of the `li` elements inside the DOM element of this view. - if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Array) { - NativeArray.activate(); - } + @method $ + @param {String} [selector] a jQuery-compatible selector string + @return {jQuery} the jQuery object for the DOM node + */ + $: function(sel) { + return this.currentState.$(this, sel); + }, - Ember.A = A; // ES6TODO: Setting A onto the object returned by ember-metal/core to avoid circles - __exports__.A = A; - __exports__.NativeArray = NativeArray; - __exports__["default"] = NativeArray; - }); -enifed("ember-runtime/system/object", - ["ember-runtime/system/core_object","ember-runtime/mixins/observable","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-runtime - */ + mutateChildViews: function(callback) { + var childViews = this._childViews, + idx = childViews.length, + view; - var CoreObject = __dependency1__["default"]; - var Observable = __dependency2__["default"]; + while(--idx >= 0) { + view = childViews[idx]; + callback(this, view, idx); + } - /** - `Ember.Object` is the main base class for all Ember objects. It is a subclass - of `Ember.CoreObject` with the `Ember.Observable` mixin applied. For details, - see the documentation for each of these. + return this; + }, - @class Object - @namespace Ember - @extends Ember.CoreObject - @uses Ember.Observable - */ - var EmberObject = CoreObject.extend(Observable); - EmberObject.toString = function() { - return "Ember.Object"; - }; + forEachChildView: function(callback) { + var childViews = this._childViews; - __exports__["default"] = EmberObject; - }); -enifed("ember-runtime/system/object_proxy", - ["ember-runtime/system/object","ember-runtime/mixins/-proxy","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var EmberObject = __dependency1__["default"]; - var _ProxyMixin = __dependency2__["default"]; + if (!childViews) { return this; } - /** - `Ember.ObjectProxy` forwards all properties not defined by the proxy itself - to a proxied `content` object. + var len = childViews.length, + view, idx; - ```javascript - object = Ember.Object.create({ - name: 'Foo' - }); + for (idx = 0; idx < len; idx++) { + view = childViews[idx]; + callback(view); + } - proxy = Ember.ObjectProxy.create({ - content: object - }); + return this; + }, - // Access and change existing properties - proxy.get('name') // 'Foo' - proxy.set('name', 'Bar'); - object.get('name') // 'Bar' + /** + Appends the view's element to the specified parent element. - // Create new 'description' property on `object` - proxy.set('description', 'Foo is a whizboo baz'); - object.get('description') // 'Foo is a whizboo baz' - ``` + If the view does not have an HTML representation yet, `createElement()` + will be called automatically. - While `content` is unset, setting a property to be delegated will throw an - Error. + Note that this method just schedules the view to be appended; the DOM + element will not be appended to the given element until all bindings have + finished synchronizing. - ```javascript - proxy = Ember.ObjectProxy.create({ - content: null, - flag: null - }); - proxy.set('flag', true); - proxy.get('flag'); // true - proxy.get('foo'); // undefined - proxy.set('foo', 'data'); // throws Error - ``` + This is not typically a function that you will need to call directly when + building your application. You might consider using `Ember.ContainerView` + instead. If you do need to use `appendTo`, be sure that the target element + you are providing is associated with an `Ember.Application` and does not + have an ancestor element that is associated with an Ember view. + + @method appendTo + @param {String|DOMElement|jQuery} A selector, element, HTML string, or jQuery object + @return {Ember.View} receiver + */ + appendTo: function(target) { + // Schedule the DOM element to be created and appended to the given + // element after bindings have synchronized. + this._insertElementLater(function() { + Ember.assert("You tried to append to (" + target + ") but that isn't in the DOM", jQuery(target).length > 0); + Ember.assert("You cannot append to an existing Ember.View. Consider using Ember.ContainerView instead.", !jQuery(target).is('.ember-view') && !jQuery(target).parents().is('.ember-view')); + this.$().appendTo(target); + }); - Delegated properties can be bound to and will change when content is updated. + return this; + }, - Computed properties on the proxy itself can depend on delegated properties. + /** + Replaces the content of the specified parent element with this view's + element. If the view does not have an HTML representation yet, + `createElement()` will be called automatically. - ```javascript - ProxyWithComputedProperty = Ember.ObjectProxy.extend({ - fullName: function () { - var firstName = this.get('firstName'), - lastName = this.get('lastName'); - if (firstName && lastName) { - return firstName + ' ' + lastName; - } - return firstName || lastName; - }.property('firstName', 'lastName') - }); + Note that this method just schedules the view to be appended; the DOM + element will not be appended to the given element until all bindings have + finished synchronizing - proxy = ProxyWithComputedProperty.create(); + @method replaceIn + @param {String|DOMElement|jQuery} target A selector, element, HTML string, or jQuery object + @return {Ember.View} received + */ + replaceIn: function(target) { + Ember.assert("You tried to replace in (" + target + ") but that isn't in the DOM", jQuery(target).length > 0); + Ember.assert("You cannot replace an existing Ember.View. Consider using Ember.ContainerView instead.", !jQuery(target).is('.ember-view') && !jQuery(target).parents().is('.ember-view')); - proxy.get('fullName'); // undefined - proxy.set('content', { - firstName: 'Tom', lastName: 'Dale' - }); // triggers property change for fullName on proxy + this._insertElementLater(function() { + jQuery(target).empty(); + this.$().appendTo(target); + }); - proxy.get('fullName'); // 'Tom Dale' - ``` + return this; + }, - @class ObjectProxy - @namespace Ember - @extends Ember.Object - @extends Ember._ProxyMixin - */ + /** + Schedules a DOM operation to occur during the next render phase. This + ensures that all bindings have finished synchronizing before the view is + rendered. - __exports__["default"] = EmberObject.extend(_ProxyMixin); - }); -enifed("ember-runtime/system/service", - ["ember-runtime/system/object","ember-runtime/inject","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var Object = __dependency1__["default"]; - var createInjectionHelper = __dependency2__.createInjectionHelper; + To use, pass a function that performs a DOM operation. - var Service; + Before your function is called, this view and all child views will receive + the `willInsertElement` event. After your function is invoked, this view + and all of its child views will receive the `didInsertElement` event. - - /** - @class Service - @namespace Ember - @extends Ember.Object + ```javascript + view._insertElementLater(function() { + this.createElement(); + this.$().appendTo('body'); + }); + ``` + + @method _insertElementLater + @param {Function} fn the function that inserts the element into the DOM + @private */ - Service = Object.extend(); + _insertElementLater: function(fn) { + this._scheduledInsert = run.scheduleOnce('render', this, '_insertElement', fn); + }, + + _insertElement: function (fn) { + this._scheduledInsert = null; + this.currentState.insertElement(this, fn); + }, /** - Creates a property that lazily looks up a service in the container. There - are no restrictions as to what objects a service can be injected into. + Appends the view's element to the document body. If the view does + not have an HTML representation yet, `createElement()` will be called + automatically. - Example: + If your application uses the `rootElement` property, you must append + the view within that element. Rendering views outside of the `rootElement` + is not supported. - ```javascript - App.ApplicationRoute = Ember.Route.extend({ - authManager: Ember.inject.service('auth'), + Note that this method just schedules the view to be appended; the DOM + element will not be appended to the document body until all bindings have + finished synchronizing. - model: function() { - return this.get('authManager').findCurrentUser(); - } + @method append + @return {Ember.View} receiver + */ + append: function() { + return this.appendTo(document.body); + }, + + /** + Removes the view's element from the element to which it is attached. + + @method remove + @return {Ember.View} receiver + */ + remove: function() { + // What we should really do here is wait until the end of the run loop + // to determine if the element has been re-appended to a different + // element. + // In the interim, we will just re-render if that happens. It is more + // important than elements get garbage collected. + if (!this.removedFromDOM) { this.destroyElement(); } + this.invokeRecursively(function(view) { + if (view.clearRenderedChildren) { view.clearRenderedChildren(); } }); - ``` + }, - This example will create an `authManager` property on the application route - that looks up the `auth` service in the container, making it easily - accessible in the `model` hook. + elementId: null, + + /** + Attempts to discover the element in the parent element. The default + implementation looks for an element with an ID of `elementId` (or the + view's guid if `elementId` is null). You can override this method to + provide your own form of lookup. For example, if you want to discover your + element using a CSS class name instead of an ID. - @method inject.service - @for Ember - @param {String} name (optional) name of the service to inject, defaults to - the property's name - @return {Ember.InjectedProperty} injection descriptor instance + @method findElementInParentElement + @param {DOMElement} parentElement The parent's DOM element + @return {DOMElement} The discovered element */ - createInjectionHelper('service'); - + findElementInParentElement: function(parentElem) { + var id = "#" + this.elementId; + return jQuery(id)[0] || jQuery(id, parentElem)[0]; + }, - __exports__["default"] = Service; - }); -enifed("ember-runtime/system/set", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/is_none","ember-runtime/system/string","ember-runtime/system/core_object","ember-runtime/mixins/mutable_enumerable","ember-runtime/mixins/enumerable","ember-runtime/mixins/copyable","ember-runtime/mixins/freezable","ember-metal/error","ember-metal/property_events","ember-metal/mixin","ember-metal/computed","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-runtime - */ - var Ember = __dependency1__["default"]; - // Ember.isNone, Ember.A + /** + Creates a DOM representation of the view and all of its + child views by recursively calling the `render()` method. - var get = __dependency2__.get; - var set = __dependency3__.set; - var guidFor = __dependency4__.guidFor; - var isNone = __dependency5__["default"]; - var fmt = __dependency6__.fmt; - var CoreObject = __dependency7__["default"]; - var MutableEnumerable = __dependency8__["default"]; - var Enumerable = __dependency9__["default"]; - var Copyable = __dependency10__["default"]; - var Freezable = __dependency11__.Freezable; - var FROZEN_ERROR = __dependency11__.FROZEN_ERROR; - var EmberError = __dependency12__["default"]; - var propertyWillChange = __dependency13__.propertyWillChange; - var propertyDidChange = __dependency13__.propertyDidChange; - var aliasMethod = __dependency14__.aliasMethod; - var computed = __dependency15__.computed; + After the element has been created, `didInsertElement` will + be called on this view and all of its child views. - /** - An unordered collection of objects. + @method createElement + @return {Ember.View} receiver + */ + createElement: function() { + if (get(this, 'element')) { return this; } - A Set works a bit like an array except that its items are not ordered. You - can create a set to efficiently test for membership for an object. You can - also iterate through a set just like an array, even accessing objects by - index, however there is no guarantee as to their order. + var buffer = this.renderToBuffer(); + set(this, 'element', buffer.element()); - All Sets are observable via the Enumerable Observer API - which works - on any enumerable object including both Sets and Arrays. + return this; + }, - ## Creating a Set + /** + Called when a view is going to insert an element into the DOM. - You can create a set like you would most objects using - `new Ember.Set()`. Most new sets you create will be empty, but you can - also initialize the set with some content by passing an array or other - enumerable of objects to the constructor. + @event willInsertElement + */ + willInsertElement: Ember.K, - Finally, you can pass in an existing set and the set will be copied. You - can also create a copy of a set by calling `Ember.Set#copy()`. + /** + Called when the element of the view has been inserted into the DOM + or after the view was re-rendered. Override this function to do any + set up that requires an element in the document body. - ```javascript - // creates a new empty set - var foundNames = new Ember.Set(); + @event didInsertElement + */ + didInsertElement: Ember.K, - // creates a set with four names in it. - var names = new Ember.Set(["Charles", "Tom", "Juan", "Alex"]); // :P + /** + Called when the view is about to rerender, but before anything has + been torn down. This is a good opportunity to tear down any manual + observers you have installed based on the DOM state - // creates a copy of the names set. - var namesCopy = new Ember.Set(names); + @event willClearRender + */ + willClearRender: Ember.K, - // same as above. - var anotherNamesCopy = names.copy(); - ``` + /** + Run this callback on the current view (unless includeSelf is false) and recursively on child views. - ## Adding/Removing Objects + @method invokeRecursively + @param fn {Function} + @param includeSelf {Boolean} Includes itself if true. + @private + */ + invokeRecursively: function(fn, includeSelf) { + var childViews = (includeSelf === false) ? this._childViews : [this]; + var currentViews, view, currentChildViews; - You generally add or remove objects from a set using `add()` or - `remove()`. You can add any type of object including primitives such as - numbers, strings, and booleans. + while (childViews.length) { + currentViews = childViews.slice(); + childViews = []; - Unlike arrays, objects can only exist one time in a set. If you call `add()` - on a set with the same object multiple times, the object will only be added - once. Likewise, calling `remove()` with the same object multiple times will - remove the object the first time and have no effect on future calls until - you add the object to the set again. + for (var i=0, l=currentViews.length; i= 0) { - if (!obj.contains(this[loc])) return false; + applyAttributesToBuffer: function(buffer) { + // Creates observers for all registered class name and attribute bindings, + // then adds them to the element. + var classNameBindings = get(this, 'classNameBindings'); + if (classNameBindings.length) { + this._applyClassNameBindings(classNameBindings); } - return true; - }, + // Pass the render buffer so the method can apply attributes directly. + // This isn't needed for class name bindings because they use the + // existing classNames infrastructure. + var attributeBindings = get(this, 'attributeBindings'); + if (attributeBindings.length) { + this._applyAttributeBindings(buffer, attributeBindings); + } - /** - Adds an object to the set. Only non-`null` objects can be added to a set - and those can only be added once. If the object is already in the set or - the passed value is null this method will have no effect. + buffer.setClasses(this.classNames); + buffer.id(this.elementId); - This is an alias for `Ember.MutableEnumerable.addObject()`. + var role = get(this, 'ariaRole'); + if (role) { + buffer.attr('role', role); + } - ```javascript - var colors = new Ember.Set(); - colors.add("blue"); // ["blue"] - colors.add("blue"); // ["blue"] - colors.add("red"); // ["blue", "red"] - colors.add(null); // ["blue", "red"] - colors.add(undefined); // ["blue", "red"] - ``` + if (get(this, 'isVisible') === false) { + buffer.style('display', 'none'); + } + }, - @method add - @param {Object} obj The object to add. - @return {Ember.Set} The set itself. - */ - add: aliasMethod('addObject'), + // .......................................................... + // STANDARD RENDER PROPERTIES + // /** - Removes the object from the set if it is found. If you pass a `null` value - or an object that is already not in the set, this method will have no - effect. This is an alias for `Ember.MutableEnumerable.removeObject()`. + Tag name for the view's outer element. The tag name is only used when an + element is first created. If you change the `tagName` for an element, you + must destroy and recreate the view element. - ```javascript - var colors = new Ember.Set(["red", "green", "blue"]); - colors.remove("red"); // ["blue", "green"] - colors.remove("purple"); // ["blue", "green"] - colors.remove(null); // ["blue", "green"] - ``` + By default, the render buffer will use a `
` tag for views. - @method remove - @param {Object} obj The object to remove - @return {Ember.Set} The set itself. + @property tagName + @type String + @default null */ - remove: aliasMethod('removeObject'), + + // We leave this null by default so we can tell the difference between + // the default case and a user-specified tag. + tagName: null, /** - Removes the last element from the set and returns it, or `null` if it's empty. + The WAI-ARIA role of the control represented by this view. For example, a + button may have a role of type 'button', or a pane may have a role of + type 'alertdialog'. This property is used by assistive software to help + visually challenged users navigate rich web applications. - ```javascript - var colors = new Ember.Set(["green", "blue"]); - colors.pop(); // "blue" - colors.pop(); // "green" - colors.pop(); // null - ``` + The full list of valid WAI-ARIA roles is available at: + [http://www.w3.org/TR/wai-aria/roles#roles_categorization](http://www.w3.org/TR/wai-aria/roles#roles_categorization) - @method pop - @return {Object} The removed object from the set or null. + @property ariaRole + @type String + @default null */ - pop: function() { - if (get(this, 'isFrozen')) throw new EmberError(FROZEN_ERROR); - var obj = this.length > 0 ? this[this.length-1] : null; - this.remove(obj); - return obj; - }, + ariaRole: null, /** - Inserts the given object on to the end of the set. It returns - the set itself. - - This is an alias for `Ember.MutableEnumerable.addObject()`. - - ```javascript - var colors = new Ember.Set(); - colors.push("red"); // ["red"] - colors.push("green"); // ["red", "green"] - colors.push("blue"); // ["red", "green", "blue"] - ``` + Standard CSS class names to apply to the view's outer element. This + property automatically inherits any class names defined by the view's + superclasses as well. - @method push - @return {Ember.Set} The set itself. + @property classNames + @type Array + @default ['ember-view'] */ - push: aliasMethod('addObject'), + classNames: ['ember-view'], /** - Removes the last element from the set and returns it, or `null` if it's empty. - - This is an alias for `Ember.Set.pop()`. + A list of properties of the view to apply as class names. If the property + is a string value, the value of that string will be applied as a class + name. ```javascript - var colors = new Ember.Set(["green", "blue"]); - colors.shift(); // "blue" - colors.shift(); // "green" - colors.shift(); // null + // Applies the 'high' class to the view element + Ember.View.extend({ + classNameBindings: ['priority'] + priority: 'high' + }); ``` - @method shift - @return {Object} The removed object from the set or null. - */ - shift: aliasMethod('pop'), - - /** - Inserts the given object on to the end of the set. It returns - the set itself. - - This is an alias of `Ember.Set.push()` + If the value of the property is a Boolean, the name of that property is + added as a dasherized class name. ```javascript - var colors = new Ember.Set(); - colors.unshift("red"); // ["red"] - colors.unshift("green"); // ["red", "green"] - colors.unshift("blue"); // ["red", "green", "blue"] + // Applies the 'is-urgent' class to the view element + Ember.View.extend({ + classNameBindings: ['isUrgent'] + isUrgent: true + }); ``` - @method unshift - @return {Ember.Set} The set itself. - */ - unshift: aliasMethod('push'), - - /** - Adds each object in the passed enumerable to the set. - - This is an alias of `Ember.MutableEnumerable.addObjects()` + If you would prefer to use a custom value instead of the dasherized + property name, you can pass a binding like this: ```javascript - var colors = new Ember.Set(); - colors.addEach(["red", "green", "blue"]); // ["red", "green", "blue"] + // Applies the 'urgent' class to the view element + Ember.View.extend({ + classNameBindings: ['isUrgent:urgent'] + isUrgent: true + }); ``` - @method addEach - @param {Ember.Enumerable} objects the objects to add. - @return {Ember.Set} The set itself. + This list of properties is inherited from the view's superclasses as well. + + @property classNameBindings + @type Array + @default [] */ - addEach: aliasMethod('addObjects'), + classNameBindings: EMPTY_ARRAY, /** - Removes each object in the passed enumerable to the set. + A list of properties of the view to apply as attributes. If the property is + a string value, the value of that string will be applied as the attribute. - This is an alias of `Ember.MutableEnumerable.removeObjects()` + ```javascript + // Applies the type attribute to the element + // with the value "button", like
+ Ember.View.extend({ + attributeBindings: ['type'], + type: 'button' + }); + ``` + + If the value of the property is a Boolean, the name of that property is + added as an attribute. ```javascript - var colors = new Ember.Set(["red", "green", "blue"]); - colors.removeEach(["red", "blue"]); // ["green"] + // Renders something like
+ Ember.View.extend({ + attributeBindings: ['enabled'], + enabled: true + }); ``` - @method removeEach - @param {Ember.Enumerable} objects the objects to remove. - @return {Ember.Set} The set itself. + @property attributeBindings */ - removeEach: aliasMethod('removeObjects'), + attributeBindings: EMPTY_ARRAY, - // .......................................................... - // PRIVATE ENUMERABLE SUPPORT + // ....................................................... + // CORE DISPLAY METHODS // - init: function(items) { - Ember.deprecate('Ember.Set is deprecated and will be removed in a future release.'); + /** + Setup a view, but do not finish waking it up. + + * configure `childViews` + * register the view with the global views hash, which is used for event + dispatch + + @method init + @private + */ + init: function() { + this.elementId = this.elementId || guidFor(this); + this._super(); - if (items) this.addObjects(items); + + // setup child views. be sure to clone the child views array first + this._childViews = this._childViews.slice(); + + Ember.assert("Only arrays are allowed for 'classNameBindings'", typeOf(this.classNameBindings) === 'array'); + this.classNameBindings = A(this.classNameBindings.slice()); + + Ember.assert("Only arrays are allowed for 'classNames'", typeOf(this.classNames) === 'array'); + this.classNames = A(this.classNames.slice()); }, - // implement Ember.Enumerable - nextObject: function(idx) { - return this[idx]; + appendChild: function(view, options) { + return this.currentState.appendChild(this, view, options); }, - // more optimized version - firstObject: computed(function() { - return this.length > 0 ? this[0] : undefined; - }), + /** + Removes the child view from the parent view. - // more optimized version - lastObject: computed(function() { - return this.length > 0 ? this[this.length-1] : undefined; - }), + @method removeChild + @param {Ember.View} view + @return {Ember.View} receiver + */ + removeChild: function(view) { + // If we're destroying, the entire subtree will be + // freed, and the DOM will be handled separately, + // so no need to mess with childViews. + if (this.isDestroying) { return; } - // implements Ember.MutableEnumerable - addObject: function(obj) { - if (get(this, 'isFrozen')) throw new EmberError(FROZEN_ERROR); - if (isNone(obj)) return this; // nothing to do + // update parent node + set(view, '_parentView', null); - var guid = guidFor(obj); - var idx = this[guid]; - var len = get(this, 'length'); - var added; + // remove view from childViews array. + var childViews = this._childViews; - if (idx>=0 && idx=0 && idx=0; i--) { + childViews[i].removedFromDOM = true; + } + + // remove from non-virtual parent view if viewName was specified + if (viewName && nonVirtualParentView) { + nonVirtualParentView.set(viewName, null); + } + + childLen = childViews.length; + for (i=childLen-1; i>=0; i--) { + childViews[i].destroy(); } return this; }, - // optimized version - contains: function(obj) { - return this[guidFor(obj)]>=0; - }, + /** + Instantiates a view to be added to the childViews array during view + initialization. You generally will not call this method directly unless + you are overriding `createChildViews()`. Note that this method will + automatically configure the correct settings on the new view instance to + act as a child of the parent. - copy: function() { - var C = this.constructor, ret = new C(), loc = get(this, 'length'); - set(ret, 'length', loc); - while(--loc>=0) { - ret[loc] = this[loc]; - ret[guidFor(this[loc])] = loc; + @method createChildView + @param {Class|String} viewClass + @param {Hash} [attrs] Attributes to add + @return {Ember.View} new instance + */ + createChildView: function(view, attrs) { + if (!view) { + throw new TypeError("createChildViews first argument must exist"); } - return ret; - }, - toString: function() { - var len = this.length, idx, array = []; - for(idx = 0; idx < len; idx++) { - array[idx] = this[idx]; + if (view.isView && view._parentView === this && view.container === this.container) { + return view; } - return fmt("Ember.Set<%@>", [array.join(',')]); - } - }); - }); -enifed("ember-runtime/system/string", - ["ember-metal/core","ember-metal/utils","ember-metal/cache","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-runtime - */ - var Ember = __dependency1__["default"]; - // Ember.STRINGS, Ember.FEATURES - var isArray = __dependency2__.isArray; - var emberInspect = __dependency2__.inspect; - var Cache = __dependency3__["default"]; + attrs = attrs || {}; + attrs._parentView = this; - var STRING_DASHERIZE_REGEXP = (/[ _]/g); + if (CoreView.detect(view)) { + attrs.templateData = attrs.templateData || get(this, 'templateData'); - var STRING_DASHERIZE_CACHE = new Cache(1000, function(key) { - return decamelize(key).replace(STRING_DASHERIZE_REGEXP, '-'); - }); + attrs.container = this.container; + view = view.create(attrs); - var CAMELIZE_CACHE = new Cache(1000, function(key) { - return key.replace(STRING_CAMELIZE_REGEXP, function(match, separator, chr) { - return chr ? chr.toUpperCase() : ''; - }).replace(/^([A-Z])/, function(match, separator, chr) { - return match.toLowerCase(); - }); - }); + // don't set the property on a virtual view, as they are invisible to + // consumers of the view API + if (view.viewName) { + set(get(this, 'concreteView'), view.viewName, view); + } + } else if ('string' === typeof view) { + var fullName = 'view:' + view; + var ViewKlass = this.container.lookupFactory(fullName); - var CLASSIFY_CACHE = new Cache(1000, function(str) { - var parts = str.split("."); - var out = []; + Ember.assert("Could not find view: '" + fullName + "'", !!ViewKlass); - for (var i=0, l=parts.length; i 2) { - cachedFormats = new Array(arguments.length - 1); + @method _isVisibleDidChange + @private + */ + _isVisibleDidChange: observer('isVisible', function() { + if (this._isVisible === get(this, 'isVisible')) { return ; } + run.scheduleOnce('render', this, this._toggleVisibility); + }), - for (var i = 1, l = arguments.length; i < l; i++) { - cachedFormats[i - 1] = arguments[i]; - } - } + _toggleVisibility: function() { + var $el = this.$(); + if (!$el) { return; } - // first, replace any ORDERED replacements. - var idx = 0; // the current index for non-numerical replacements - return str.replace(/%@([0-9]+)?/g, function(s, argIndex) { - argIndex = (argIndex) ? parseInt(argIndex, 10) - 1 : idx++; - s = cachedFormats[argIndex]; - return (s === null) ? '(null)' : (s === undefined) ? '' : emberInspect(s); - }); - } + var isVisible = get(this, 'isVisible'); - function loc(str, formats) { - if (!isArray(formats) || arguments.length > 2) { - formats = Array.prototype.slice.call(arguments, 1); - } + if (this._isVisible === isVisible) { return ; } - str = Ember.STRINGS[str] || str; - return fmt(str, formats); - } + $el.toggle(isVisible); - function w(str) { - return str.split(/\s+/); - } + this._isVisible = isVisible; - function decamelize(str) { - return DECAMELIZE_CACHE.get(str); - } + if (this._isAncestorHidden()) { return; } - function dasherize(str) { - return STRING_DASHERIZE_CACHE.get(str); - } + if (isVisible) { + this._notifyBecameVisible(); + } else { + this._notifyBecameHidden(); + } + }, - function camelize(str) { - return CAMELIZE_CACHE.get(str); - } + _notifyBecameVisible: function() { + this.trigger('becameVisible'); - function classify(str) { - return CLASSIFY_CACHE.get(str); - } + this.forEachChildView(function(view) { + var isVisible = get(view, 'isVisible'); - function underscore(str) { - return UNDERSCORE_CACHE.get(str); - } + if (isVisible || isVisible === null) { + view._notifyBecameVisible(); + } + }); + }, - function capitalize(str) { - return CAPITALIZE_CACHE.get(str); - } + _notifyBecameHidden: function() { + this.trigger('becameHidden'); + this.forEachChildView(function(view) { + var isVisible = get(view, 'isVisible'); - /** - Defines the hash of localized strings for the current language. Used by - the `Ember.String.loc()` helper. To localize, add string values to this - hash. + if (isVisible || isVisible === null) { + view._notifyBecameHidden(); + } + }); + }, - @property STRINGS - @for Ember - @type Hash - */ - Ember.STRINGS = {}; + _isAncestorHidden: function() { + var parent = get(this, 'parentView'); - /** - Defines string helper methods including string formatting and localization. - Unless `Ember.EXTEND_PROTOTYPES.String` is `false` these methods will also be - added to the `String.prototype` as well. + while (parent) { + if (get(parent, 'isVisible') === false) { return true; } - @class String - @namespace Ember - @static - */ - __exports__["default"] = { - /** - Apply formatting options to the string. This will look for occurrences - of "%@" in your string and substitute them with the arguments you pass into - this method. If you want to control the specific order of replacement, - you can add a number after the key as well to indicate which argument - you want to insert. + parent = get(parent, 'parentView'); + } - Ordered insertions are most useful when building loc strings where values - you need to insert may appear in different orders. + return false; + }, - ```javascript - "Hello %@ %@".fmt('John', 'Doe'); // "Hello John Doe" - "Hello %@2, %@1".fmt('John', 'Doe'); // "Hello Doe, John" - ``` + clearBuffer: function() { + this.invokeRecursively(nullViewsBuffer); + }, - @method fmt - @param {String} str The string to format - @param {Array} formats An array of parameters to interpolate into string. - @return {String} formatted string - */ - fmt: fmt, + transitionTo: function(state, children) { + var priorState = this.currentState, + currentState = this.currentState = this.states[state]; + this.state = state; - /** - Formats the passed string, but first looks up the string in the localized - strings hash. This is a convenient way to localize text. See - `Ember.String.fmt()` for more information on formatting. + if (priorState && priorState.exit) { priorState.exit(this); } + if (currentState.enter) { currentState.enter(this); } + if (state === 'inDOM') { meta(this).cache.element = undefined; } - Note that it is traditional but not required to prefix localized string - keys with an underscore or other character so you can easily identify - localized strings. + if (children !== false) { + this.forEachChildView(function(view) { + view.transitionTo(state); + }); + } + }, - ```javascript - Ember.STRINGS = { - '_Hello World': 'Bonjour le monde', - '_Hello %@ %@': 'Bonjour %@ %@' - }; + // ....................................................... + // EVENT HANDLING + // - Ember.String.loc("_Hello World"); // 'Bonjour le monde'; - Ember.String.loc("_Hello %@ %@", ["John", "Smith"]); // "Bonjour John Smith"; - ``` + /** + Handle events from `Ember.EventDispatcher` - @method loc - @param {String} str The string to format - @param {Array} formats Optional array of parameters to interpolate into string. - @return {String} formatted string + @method handleEvent + @param eventName {String} + @param evt {Event} + @private */ - loc: loc, + handleEvent: function(eventName, evt) { + return this.currentState.handleEvent(this, eventName, evt); + }, - /** - Splits a string into separate units separated by spaces, eliminating any - empty strings in the process. This is a convenience method for split that - is mostly useful when applied to the `String.prototype`. + registerObserver: function(root, path, target, observer) { + if (!observer && 'function' === typeof target) { + observer = target; + target = null; + } - ```javascript - Ember.String.w("alpha beta gamma").forEach(function(key) { - console.log(key); + if (!root || typeof root !== 'object') { + return; + } + + var view = this, + stateCheckedObserver = function() { + view.currentState.invokeObserver(this, observer); + }, + scheduledObserver = function() { + run.scheduleOnce('render', this, stateCheckedObserver); + }; + + addObserver(root, path, target, scheduledObserver); + + this.one('willClearRender', function() { + removeObserver(root, path, target, scheduledObserver); }); + } - // > alpha - // > beta - // > gamma - ``` + }); - @method w - @param {String} str The string to split - @return {Array} array containing the split strings - */ - w: w, + /* + Describe how the specified actions should behave in the various + states that a view can exist in. Possible states: - /** - Converts a camelized string into all lower case separated by underscores. + * preRender: when a view is first instantiated, and after its + element was destroyed, it is in the preRender state + * inBuffer: once a view has been rendered, but before it has + been inserted into the DOM, it is in the inBuffer state + * hasElement: the DOM representation of the view is created, + and is ready to be inserted + * inDOM: once a view has been inserted into the DOM it is in + the inDOM state. A view spends the vast majority of its + existence in this state. + * destroyed: once a view has been destroyed (using the destroy + method), it is in this state. No further actions can be invoked + on a destroyed view. + */ - ```javascript - 'innerHTML'.decamelize(); // 'inner_html' - 'action_name'.decamelize(); // 'action_name' - 'css-class-name'.decamelize(); // 'css-class-name' - 'my favorite items'.decamelize(); // 'my favorite items' - ``` + // in the destroyed state, everything is illegal - @method decamelize - @param {String} str The string to decamelize. - @return {String} the decamelized string. - */ - decamelize: decamelize, + // before rendering has begun, all legal manipulations are noops. - /** - Replaces underscores, spaces, or camelCase with dashes. + // inside the buffer, legal manipulations are done on the buffer - ```javascript - 'innerHTML'.dasherize(); // 'inner-html' - 'action_name'.dasherize(); // 'action-name' - 'css-class-name'.dasherize(); // 'css-class-name' - 'my favorite items'.dasherize(); // 'my-favorite-items' - ``` + // once the view has been inserted into the DOM, legal manipulations + // are done on the DOM element. + + function notifyMutationListeners() { + run.once(View, 'notifyMutationListeners'); + } + + var DOMManager = { + prepend: function(view, html) { + view.$().prepend(html); + notifyMutationListeners(); + }, + + after: function(view, html) { + view.$().after(html); + notifyMutationListeners(); + }, + + html: function(view, html) { + view.$().html(html); + notifyMutationListeners(); + }, + + replace: function(view) { + var element = get(view, 'element'); + + set(view, 'element', null); + + view._insertElementLater(function() { + jQuery(element).replaceWith(get(view, 'element')); + notifyMutationListeners(); + }); + }, + + remove: function(view) { + view.$().remove(); + notifyMutationListeners(); + }, - @method dasherize - @param {String} str The string to dasherize. - @return {String} the dasherized string. - */ - dasherize: dasherize, + empty: function(view) { + view.$().empty(); + notifyMutationListeners(); + } + }; + + View.reopen({ + domManager: DOMManager + }); + + View.reopenClass({ /** - Returns the lowerCamelCase form of a string. + Parse a path and return an object which holds the parsed properties. + + For example a path like "content.isEnabled:enabled:disabled" will return the + following object: ```javascript - 'innerHTML'.camelize(); // 'innerHTML' - 'action_name'.camelize(); // 'actionName' - 'css-class-name'.camelize(); // 'cssClassName' - 'my favorite items'.camelize(); // 'myFavoriteItems' - 'My Favorite Items'.camelize(); // 'myFavoriteItems' + { + path: "content.isEnabled", + className: "enabled", + falsyClassName: "disabled", + classNames: ":enabled:disabled" + } ``` - @method camelize - @param {String} str The string to camelize. - @return {String} the camelized string. + @method _parsePropertyPath + @static + @private */ - camelize: camelize, + _parsePropertyPath: function(path) { + var split = path.split(':'), + propertyPath = split[0], + classNames = "", + className, + falsyClassName; - /** - Returns the UpperCamelCase form of a string. + // check if the property is defined as prop:class or prop:trueClass:falseClass + if (split.length > 1) { + className = split[1]; + if (split.length === 3) { falsyClassName = split[2]; } - ```javascript - 'innerHTML'.classify(); // 'InnerHTML' - 'action_name'.classify(); // 'ActionName' - 'css-class-name'.classify(); // 'CssClassName' - 'my favorite items'.classify(); // 'MyFavoriteItems' - ``` + classNames = ':' + className; + if (falsyClassName) { classNames += ":" + falsyClassName; } + } - @method classify - @param {String} str the string to classify - @return {String} the classified string - */ - classify: classify, + return { + path: propertyPath, + classNames: classNames, + className: (className === '') ? undefined : className, + falsyClassName: falsyClassName + }; + }, /** - More general than decamelize. Returns the lower\_case\_and\_underscored - form of a string. + Get the class name for a given value, based on the path, optional + `className` and optional `falsyClassName`. - ```javascript - 'innerHTML'.underscore(); // 'inner_html' - 'action_name'.underscore(); // 'action_name' - 'css-class-name'.underscore(); // 'css_class_name' - 'my favorite items'.underscore(); // 'my_favorite_items' - ``` + - if a `className` or `falsyClassName` has been specified: + - if the value is truthy and `className` has been specified, + `className` is returned + - if the value is falsy and `falsyClassName` has been specified, + `falsyClassName` is returned + - otherwise `null` is returned + - if the value is `true`, the dasherized last part of the supplied path + is returned + - if the value is not `false`, `undefined` or `null`, the `value` + is returned + - if none of the above rules apply, `null` is returned - @method underscore - @param {String} str The string to underscore. - @return {String} the underscored string. + @method _classStringForValue + @param path + @param val + @param className + @param falsyClassName + @static + @private */ - underscore: underscore, + _classStringForValue: function(path, val, className, falsyClassName) { + if(isArray(val)) { + val = get(val, 'length') !== 0; + } + + // When using the colon syntax, evaluate the truthiness or falsiness + // of the value to determine which className to return + if (className || falsyClassName) { + if (className && !!val) { + return className; - /** - Returns the Capitalized form of a string + } else if (falsyClassName && !val) { + return falsyClassName; - ```javascript - 'innerHTML'.capitalize() // 'InnerHTML' - 'action_name'.capitalize() // 'Action_name' - 'css-class-name'.capitalize() // 'Css-class-name' - 'my favorite items'.capitalize() // 'My favorite items' - ``` + } else { + return null; + } - @method capitalize - @param {String} str The string to capitalize. - @return {String} The capitalized string. - */ - capitalize: capitalize - }; + // If value is a Boolean and true, return the dasherized property + // name. + } else if (val === true) { + // Normalize property path to be suitable for use + // as a class name. For exaple, content.foo.barBaz + // becomes bar-baz. + var parts = path.split('.'); + return dasherize(parts[parts.length-1]); - __exports__.fmt = fmt; - __exports__.loc = loc; - __exports__.w = w; - __exports__.decamelize = decamelize; - __exports__.dasherize = dasherize; - __exports__.camelize = camelize; - __exports__.classify = classify; - __exports__.underscore = underscore; - __exports__.capitalize = capitalize; - }); -enifed("ember-runtime/system/subarray", - ["ember-metal/error","ember-metal/enumerable_utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var EmberError = __dependency1__["default"]; - var EnumerableUtils = __dependency2__["default"]; + // If the value is not false, undefined, or null, return the current + // value of the property. + } else if (val !== false && val != null) { + return val; - var RETAIN = 'r'; - var FILTER = 'f'; + // Nothing to display. Return null so that the old class is removed + // but no new class is added. + } else { + return null; + } + } + }); - function Operation(type, count) { - this.type = type; - this.count = count; - } + var mutation = EmberObject.extend(Evented).create(); - __exports__["default"] = SubArray; + View.addMutationListener = function(callback) { + mutation.on('change', callback); + }; + + View.removeMutationListener = function(callback) { + mutation.off('change', callback); + }; + + View.notifyMutationListeners = function() { + mutation.trigger('change'); + }; /** - An `Ember.SubArray` tracks an array in a way similar to, but more specialized - than, `Ember.TrackedArray`. It is useful for keeping track of the indexes of - items within a filtered array. + Global views hash - @class SubArray - @namespace Ember + @property views + @static + @type Hash */ - function SubArray (length) { - if (arguments.length < 1) { length = 0; } + View.views = {}; - if (length > 0) { - this._operations = [new Operation(RETAIN, length)]; + // If someone overrides the child views computed property when + // defining their class, we want to be able to process the user's + // supplied childViews and then restore the original computed property + // at view initialization time. This happens in Ember.ContainerView's init + // method. + View.childViewsProperty = childViewsProperty; + + View.applyAttributeBindings = function(elem, name, value) { + var type = typeOf(value); + + // if this changes, also change the logic in ember-handlebars/lib/helpers/binding.js + if (name !== 'value' && (type === 'string' || (type === 'number' && !isNaN(value)))) { + if (value !== elem.attr(name)) { + elem.attr(name, value); + } + } else if (name === 'value' || type === 'boolean') { + if (isNone(value) || value === false) { + // `null`, `undefined` or `false` should remove attribute + elem.removeAttr(name); + elem.prop(name, ''); + } else if (value !== elem.prop(name)) { + // value should always be properties + elem.prop(name, value); + } + } else if (!value) { + elem.removeAttr(name); + } + }; + + __exports__.CoreView = CoreView; + __exports__.View = View; + __exports__.ViewCollection = ViewCollection; + }); +})(); + +(function() { +define("metamorph", + [], + function() { + "use strict"; + // ========================================================================== + // Project: metamorph + // Copyright: ©2014 Tilde, Inc. All rights reserved. + // ========================================================================== + + var K = function() {}, + guid = 0, + disableRange = (function(){ + if ('undefined' !== typeof MetamorphENV) { + return MetamorphENV.DISABLE_RANGE_API; + } else if ('undefined' !== ENV) { + return ENV.DISABLE_RANGE_API; + } else { + return false; + } + })(), + + // Feature-detect the W3C range API, the extended check is for IE9 which only partially supports ranges + supportsRange = (!disableRange) && typeof document !== 'undefined' && ('createRange' in document) && (typeof Range !== 'undefined') && Range.prototype.createContextualFragment, + + // Internet Explorer prior to 9 does not allow setting innerHTML if the first element + // is a "zero-scope" element. This problem can be worked around by making + // the first node an invisible text node. We, like Modernizr, use ­ + needsShy = typeof document !== 'undefined' && (function() { + var testEl = document.createElement('div'); + testEl.innerHTML = "
"; + testEl.firstChild.innerHTML = ""; + return testEl.firstChild.innerHTML === ''; + })(), + + + // IE 8 (and likely earlier) likes to move whitespace preceeding + // a script tag to appear after it. This means that we can + // accidentally remove whitespace when updating a morph. + movesWhitespace = document && (function() { + var testEl = document.createElement('div'); + testEl.innerHTML = "Test: Value"; + return testEl.childNodes[0].nodeValue === 'Test:' && + testEl.childNodes[2].nodeValue === ' Value'; + })(); + + // Constructor that supports either Metamorph('foo') or new + // Metamorph('foo'); + // + // Takes a string of HTML as the argument. + + var Metamorph = function(html) { + var self; + + if (this instanceof Metamorph) { + self = this; } else { - this._operations = []; + self = new K(); } - } + self.innerHTML = html; + var myGuid = 'metamorph-'+(guid++); + self.start = myGuid + '-start'; + self.end = myGuid + '-end'; - SubArray.prototype = { - /** - Track that an item was added to the tracked array. + return self; + }; - @method addItem + K.prototype = Metamorph.prototype; - @param {Number} index The index of the item in the tracked array. - @param {Boolean} match `true` iff the item is included in the subarray. + var rangeFor, htmlFunc, removeFunc, outerHTMLFunc, appendToFunc, afterFunc, prependFunc, startTagFunc, endTagFunc; - @return {number} The index of the item in the subarray. + outerHTMLFunc = function() { + return this.startTag() + this.innerHTML + this.endTag(); + }; + + startTagFunc = function() { + /* + * We replace chevron by its hex code in order to prevent escaping problems. + * Check this thread for more explaination: + * http://stackoverflow.com/questions/8231048/why-use-x3c-instead-of-when-generating-html-from-javascript + */ + return "hi"; + * div.firstChild.firstChild.tagName //=> "" + * + * If our script markers are inside such a node, we need to find that + * node and use *it* as the marker. + */ + var realNode = function(start) { + while (start.parentNode.tagName === "") { + start = start.parentNode; + } - _findOperation: function (index, foundCallback, notFoundCallback) { - var seenInSubArray = 0; - var operationIndex, len, operation, rangeStart, rangeEnd; + return start; + }; - // OPTIMIZE: change to balanced tree - // find leftmost operation to the right of `index` - for (operationIndex = rangeStart = 0, len = this._operations.length; operationIndex < len; rangeStart = rangeEnd + 1, ++operationIndex) { - operation = this._operations[operationIndex]; - rangeEnd = rangeStart + operation.count - 1; + /* + * When automatically adding a tbody, Internet Explorer inserts the + * tbody immediately before the first . Other browsers create it + * before the first node, no matter what. + * + * This means the the following code: + * + * div = document.createElement("div"); + * div.innerHTML = "
hi
+ * + * Generates the following DOM in IE: + * + * + div + * + table + * - script id='first' + * + tbody + * + tr + * + td + * - "hi" + * - script id='last' + * + * Which means that the two script tags, even though they were + * inserted at the same point in the hierarchy in the original + * HTML, now have different parents. + * + * This code reparents the first script tag by making it the tbody's + * first child. + * + */ + var fixParentage = function(start, end) { + if (start.parentNode !== end.parentNode) { + end.parentNode.insertBefore(start, end.parentNode.firstChild); + } + }; - if (index >= rangeStart && index <= rangeEnd) { - foundCallback(operation, operationIndex, rangeStart, rangeEnd, seenInSubArray); - return; - } else if (operation.type === RETAIN) { - seenInSubArray += operation.count; + htmlFunc = function(html, outerToo) { + // get the real starting node. see realNode for details. + var start = realNode(document.getElementById(this.start)); + var end = document.getElementById(this.end); + var parentNode = end.parentNode; + var node, nextSibling, last; + + // make sure that the start and end nodes share the same + // parent. If not, fix it. + fixParentage(start, end); + + // remove all of the nodes after the starting placeholder and + // before the ending placeholder. + node = start.nextSibling; + while (node) { + nextSibling = node.nextSibling; + last = node === end; + + // if this is the last node, and we want to remove it as well, + // set the `end` node to the next sibling. This is because + // for the rest of the function, we insert the new nodes + // before the end (note that insertBefore(node, null) is + // the same as appendChild(node)). + // + // if we do not want to remove it, just break. + if (last) { + if (outerToo) { end = node.nextSibling; } else { break; } } + + node.parentNode.removeChild(node); + + // if this is the last node and we didn't break before + // (because we wanted to remove the outer nodes), break + // now. + if (last) { break; } + + node = nextSibling; } - notFoundCallback(seenInSubArray); - }, + // get the first node for the HTML string, even in cases like + // tables and lists where a simple innerHTML on a div would + // swallow some of the content. + node = firstNodeFor(start.parentNode, html); - _composeAt: function(index) { - var op = this._operations[index]; - var otherOp; + if (outerToo) { + start.parentNode.removeChild(start); + } - if (!op) { - // Composing out of bounds is a no-op, as when removing the last operation - // in the list. - return; + // copy the nodes for the HTML between the starting and ending + // placeholder. + while (node) { + nextSibling = node.nextSibling; + parentNode.insertBefore(node, end); + node = nextSibling; } + }; - if (index > 0) { - otherOp = this._operations[index-1]; - if (otherOp.type === op.type) { - op.count += otherOp.count; - this._operations.splice(index-1, 1); - --index; - } + // remove the nodes in the DOM representing this metamorph. + // + // this includes the starting and ending placeholders. + removeFunc = function() { + var start = realNode(document.getElementById(this.start)); + var end = document.getElementById(this.end); + + this.html(''); + start.parentNode.removeChild(start); + end.parentNode.removeChild(end); + }; + + appendToFunc = function(parentNode) { + var node = firstNodeFor(parentNode, this.outerHTML()); + var nextSibling; + + while (node) { + nextSibling = node.nextSibling; + parentNode.appendChild(node); + node = nextSibling; } + }; - if (index < this._operations.length-1) { - otherOp = this._operations[index+1]; - if (otherOp.type === op.type) { - op.count += otherOp.count; - this._operations.splice(index+1, 1); - } + afterFunc = function(html) { + // get the real starting node. see realNode for details. + var end = document.getElementById(this.end); + var insertBefore = end.nextSibling; + var parentNode = end.parentNode; + var nextSibling; + var node; + + // get the first node for the HTML string, even in cases like + // tables and lists where a simple innerHTML on a div would + // swallow some of the content. + node = firstNodeFor(parentNode, html); + + // copy the nodes for the HTML between the starting and ending + // placeholder. + while (node) { + nextSibling = node.nextSibling; + parentNode.insertBefore(node, insertBefore); + node = nextSibling; } - }, + }; - toString: function () { - var str = ""; - EnumerableUtils.forEach(this._operations, function (operation) { - str += " " + operation.type + ":" + operation.count; - }); - return str.substring(1); + prependFunc = function(html) { + var start = document.getElementById(this.start); + var parentNode = start.parentNode; + var nextSibling; + var node; + + node = firstNodeFor(parentNode, html); + var insertBefore = start.nextSibling; + + while (node) { + nextSibling = node.nextSibling; + parentNode.insertBefore(node, insertBefore); + node = nextSibling; + } + }; + } + + Metamorph.prototype.html = function(html) { + this.checkRemoved(); + if (html === undefined) { return this.innerHTML; } + + htmlFunc.call(this, html); + + this.innerHTML = html; + }; + + Metamorph.prototype.replaceWith = function(html) { + this.checkRemoved(); + htmlFunc.call(this, html, true); + }; + + Metamorph.prototype.remove = removeFunc; + Metamorph.prototype.outerHTML = outerHTMLFunc; + Metamorph.prototype.appendTo = appendToFunc; + Metamorph.prototype.after = afterFunc; + Metamorph.prototype.prepend = prependFunc; + Metamorph.prototype.startTag = startTagFunc; + Metamorph.prototype.endTag = endTagFunc; + + Metamorph.prototype.isRemoved = function() { + var before = document.getElementById(this.start); + var after = document.getElementById(this.end); + + return !before || !after; + }; + + Metamorph.prototype.checkRemoved = function() { + if (this.isRemoved()) { + throw new Error("Cannot perform operations on a Metamorph that is not in the DOM."); } }; + + return Metamorph; }); -enifed("ember-runtime/system/tracked_array", - ["ember-metal/property_get","ember-metal/enumerable_utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { + +})(); + +(function() { +define("ember-handlebars-compiler", + ["ember-metal/core","exports"], + function(__dependency1__, __exports__) { "use strict"; - var get = __dependency1__.get; - var forEach = __dependency2__.forEach; + /** + @module ember + @submodule ember-handlebars-compiler + */ + + var Ember = __dependency1__["default"]; - var RETAIN = 'r'; - var INSERT = 'i'; - var DELETE = 'd'; + // ES6Todo: you'll need to import debugger once debugger is es6'd. + if (typeof Ember.assert === 'undefined') { Ember.assert = function(){}; }; + if (typeof Ember.FEATURES === 'undefined') { Ember.FEATURES = { isEnabled: function(){} }; }; - __exports__["default"] = TrackedArray; + var objectCreate = Object.create || function(parent) { + function F() {} + F.prototype = parent; + return new F(); + }; + + // set up for circular references later + var View, Component; + + // ES6Todo: when ember-debug is es6'ed import this. + // var emberAssert = Ember.assert; + var Handlebars = (Ember.imports && Ember.imports.Handlebars) || (this && this.Handlebars); + if (!Handlebars && typeof require === 'function') { + Handlebars = require('handlebars'); + } + + Ember.assert("Ember Handlebars requires Handlebars version 1.0 or 1.1. Include " + + "a SCRIPT tag in the HTML HEAD linking to the Handlebars file " + + "before you link to Ember.", Handlebars); + + Ember.assert("Ember Handlebars requires Handlebars version 1.0 or 1.1, " + + "COMPILER_REVISION expected: 4, got: " + Handlebars.COMPILER_REVISION + + " - Please note: Builds of master may have other COMPILER_REVISION values.", + Handlebars.COMPILER_REVISION === 4); /** - An `Ember.TrackedArray` tracks array operations. It's useful when you want to - lazily compute the indexes of items in an array after they've been shifted by - subsequent operations. + Prepares the Handlebars templating library for use inside Ember's view + system. - @class TrackedArray + The `Ember.Handlebars` object is the standard Handlebars library, extended to + use Ember's `get()` method instead of direct property access, which allows + computed properties to be used inside templates. + + To create an `Ember.Handlebars` template, call `Ember.Handlebars.compile()`. + This will return a function that can be used by `Ember.View` for rendering. + + @class Handlebars @namespace Ember - @param {Array} [items=[]] The array to be tracked. This is used just to get - the initial items for the starting state of retain:n. */ - function TrackedArray(items) { - if (arguments.length < 1) { items = []; } + var EmberHandlebars = Ember.Handlebars = objectCreate(Handlebars); - var length = get(items, 'length'); + /** + Register a bound helper or custom view helper. - if (length) { - this._operations = [new ArrayOperation(RETAIN, length, items)]; - } else { - this._operations = []; - } - } + ## Simple bound helper example - TrackedArray.RETAIN = RETAIN; - TrackedArray.INSERT = INSERT; - TrackedArray.DELETE = DELETE; + ```javascript + Ember.Handlebars.helper('capitalize', function(value) { + return value.toUpperCase(); + }); + ``` - TrackedArray.prototype = { + The above bound helper can be used inside of templates as follows: - /** - Track that `newItems` were added to the tracked array at `index`. + ```handlebars + {{capitalize name}} + ``` - @method addItems - @param index - @param newItems - */ - addItems: function (index, newItems) { - var count = get(newItems, 'length'); - if (count < 1) { return; } + In this case, when the `name` property of the template's context changes, + the rendered value of the helper will update to reflect this change. - var match = this._findArrayOperation(index); - var arrayOperation = match.operation; - var arrayOperationIndex = match.index; - var arrayOperationRangeStart = match.rangeStart; - var composeIndex, newArrayOperation; + For more examples of bound helpers, see documentation for + `Ember.Handlebars.registerBoundHelper`. - newArrayOperation = new ArrayOperation(INSERT, count, newItems); + ## Custom view helper example - if (arrayOperation) { - if (!match.split) { - // insert left of arrayOperation - this._operations.splice(arrayOperationIndex, 0, newArrayOperation); - composeIndex = arrayOperationIndex; - } else { - this._split(arrayOperationIndex, index - arrayOperationRangeStart, newArrayOperation); - composeIndex = arrayOperationIndex + 1; - } - } else { - // insert at end - this._operations.push(newArrayOperation); - composeIndex = arrayOperationIndex; - } + Assuming a view subclass named `App.CalendarView` were defined, a helper + for rendering instances of this view could be registered as follows: - this._composeInsert(composeIndex); - }, + ```javascript + Ember.Handlebars.helper('calendar', App.CalendarView): + ``` - /** - Track that `count` items were removed at `index`. + The above bound helper can be used inside of templates as follows: - @method removeItems - @param index - @param count - */ - removeItems: function (index, count) { - if (count < 1) { return; } + ```handlebars + {{calendar}} + ``` - var match = this._findArrayOperation(index); - var arrayOperationIndex = match.index; - var arrayOperationRangeStart = match.rangeStart; - var newArrayOperation, composeIndex; + Which is functionally equivalent to: - newArrayOperation = new ArrayOperation(DELETE, count); - if (!match.split) { - // insert left of arrayOperation - this._operations.splice(arrayOperationIndex, 0, newArrayOperation); - composeIndex = arrayOperationIndex; - } else { - this._split(arrayOperationIndex, index - arrayOperationRangeStart, newArrayOperation); - composeIndex = arrayOperationIndex + 1; - } + ```handlebars + {{view App.CalendarView}} + ``` - return this._composeDelete(composeIndex); - }, + Options in the helper will be passed to the view in exactly the same + manner as with the `view` helper. - /** - Apply all operations, reducing them to retain:n, for `n`, the number of - items in the array. + @method helper + @for Ember.Handlebars + @param {String} name + @param {Function|Ember.View} function or view class constructor + @param {String} dependentKeys* + */ + EmberHandlebars.helper = function(name, value) { + if (!View) { View = requireModule('ember-views/views/view')['View']; } // ES6TODO: stupid circular dep + if (!Component) { Component = requireModule('ember-views/views/component')['default']; } // ES6TODO: stupid circular dep - `callback` will be called for each operation and will be passed the following arguments: + Ember.assert("You tried to register a component named '" + name + "', but component names must include a '-'", !Component.detect(value) || name.match(/-/)); - * {array} items The items for the given operation - * {number} offset The computed offset of the items, ie the index in the - array of the first item for this operation. - * {string} operation The type of the operation. One of - `Ember.TrackedArray.{RETAIN, DELETE, INSERT}` + if (View.detect(value)) { + EmberHandlebars.registerHelper(name, EmberHandlebars.makeViewHelper(value)); + } else { + EmberHandlebars.registerBoundHelper.apply(null, arguments); + } + }; - @method apply - @param {Function} callback - */ - apply: function (callback) { - var items = []; - var offset = 0; + /** + Returns a helper function that renders the provided ViewClass. + + Used internally by Ember.Handlebars.helper and other methods + involving helper/component registration. + + @private + @method makeViewHelper + @for Ember.Handlebars + @param {Function} ViewClass view class constructor + @since 1.2.0 + */ + EmberHandlebars.makeViewHelper = function(ViewClass) { + return function(options) { + Ember.assert("You can only pass attributes (such as name=value) not bare values to a helper for a View found in '" + ViewClass.toString() + "'", arguments.length < 2); + return EmberHandlebars.helpers.view.call(this, ViewClass, options); + }; + }; - forEach(this._operations, function (arrayOperation, operationIndex) { - callback(arrayOperation.items, offset, arrayOperation.type, operationIndex); + /** + @class helpers + @namespace Ember.Handlebars + */ + EmberHandlebars.helpers = objectCreate(Handlebars.helpers); - if (arrayOperation.type !== DELETE) { - offset += arrayOperation.count; - items = items.concat(arrayOperation.items); - } - }); + /** + Override the the opcode compiler and JavaScript compiler for Handlebars. - this._operations = [new ArrayOperation(RETAIN, items.length, items)]; - }, + @class Compiler + @namespace Ember.Handlebars + @private + @constructor + */ + EmberHandlebars.Compiler = function() {}; - /** - Return an `ArrayOperationMatch` for the operation that contains the item at `index`. + // Handlebars.Compiler doesn't exist in runtime-only + if (Handlebars.Compiler) { + EmberHandlebars.Compiler.prototype = objectCreate(Handlebars.Compiler.prototype); + } - @method _findArrayOperation + EmberHandlebars.Compiler.prototype.compiler = EmberHandlebars.Compiler; - @param {Number} index the index of the item whose operation information - should be returned. - @private - */ - _findArrayOperation: function (index) { - var split = false; - var arrayOperationIndex, arrayOperation, - arrayOperationRangeStart, arrayOperationRangeEnd, - len; + /** + @class JavaScriptCompiler + @namespace Ember.Handlebars + @private + @constructor + */ + EmberHandlebars.JavaScriptCompiler = function() {}; - // OPTIMIZE: we could search these faster if we kept a balanced tree. - // find leftmost arrayOperation to the right of `index` - for (arrayOperationIndex = arrayOperationRangeStart = 0, len = this._operations.length; arrayOperationIndex < len; ++arrayOperationIndex) { - arrayOperation = this._operations[arrayOperationIndex]; + // Handlebars.JavaScriptCompiler doesn't exist in runtime-only + if (Handlebars.JavaScriptCompiler) { + EmberHandlebars.JavaScriptCompiler.prototype = objectCreate(Handlebars.JavaScriptCompiler.prototype); + EmberHandlebars.JavaScriptCompiler.prototype.compiler = EmberHandlebars.JavaScriptCompiler; + } - if (arrayOperation.type === DELETE) { continue; } - arrayOperationRangeEnd = arrayOperationRangeStart + arrayOperation.count - 1; + EmberHandlebars.JavaScriptCompiler.prototype.namespace = "Ember.Handlebars"; - if (index === arrayOperationRangeStart) { - break; - } else if (index > arrayOperationRangeStart && index <= arrayOperationRangeEnd) { - split = true; - break; - } else { - arrayOperationRangeStart = arrayOperationRangeEnd + 1; - } - } + EmberHandlebars.JavaScriptCompiler.prototype.initializeBuffer = function() { + return "''"; + }; - return new ArrayOperationMatch(arrayOperation, arrayOperationIndex, split, arrayOperationRangeStart); - }, + /** + Override the default buffer for Ember Handlebars. By default, Handlebars + creates an empty String at the beginning of each invocation and appends to + it. Ember's Handlebars overrides this to append to a single shared buffer. - _split: function (arrayOperationIndex, splitIndex, newArrayOperation) { - var arrayOperation = this._operations[arrayOperationIndex]; - var splitItems = arrayOperation.items.slice(splitIndex); - var splitArrayOperation = new ArrayOperation(arrayOperation.type, splitItems.length, splitItems); + @private + @method appendToBuffer + @param string {String} + */ + EmberHandlebars.JavaScriptCompiler.prototype.appendToBuffer = function(string) { + return "data.buffer.push("+string+");"; + }; - // truncate LHS - arrayOperation.count = splitIndex; - arrayOperation.items = arrayOperation.items.slice(0, splitIndex); + // Hacks ahead: + // Handlebars presently has a bug where the `blockHelperMissing` hook + // doesn't get passed the name of the missing helper name, but rather + // gets passed the value of that missing helper evaluated on the current + // context, which is most likely `undefined` and totally useless. + // + // So we alter the compiled template function to pass the name of the helper + // instead, as expected. + // + // This can go away once the following is closed: + // https://github.com/wycats/handlebars.js/issues/634 - this._operations.splice(arrayOperationIndex + 1, 0, newArrayOperation, splitArrayOperation); - }, + var DOT_LOOKUP_REGEX = /helpers\.(.*?)\)/, + BRACKET_STRING_LOOKUP_REGEX = /helpers\['(.*?)'/, + INVOCATION_SPLITTING_REGEX = /(.*blockHelperMissing\.call\(.*)(stack[0-9]+)(,.*)/; - // see SubArray for a better implementation. - _composeInsert: function (index) { - var newArrayOperation = this._operations[index]; - var leftArrayOperation = this._operations[index-1]; // may be undefined - var rightArrayOperation = this._operations[index+1]; // may be undefined - var leftOp = leftArrayOperation && leftArrayOperation.type; - var rightOp = rightArrayOperation && rightArrayOperation.type; + EmberHandlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation = function(source) { + var helperInvocation = source[source.length - 1], + helperName = (DOT_LOOKUP_REGEX.exec(helperInvocation) || BRACKET_STRING_LOOKUP_REGEX.exec(helperInvocation))[1], + matches = INVOCATION_SPLITTING_REGEX.exec(helperInvocation); - if (leftOp === INSERT) { - // merge left - leftArrayOperation.count += newArrayOperation.count; - leftArrayOperation.items = leftArrayOperation.items.concat(newArrayOperation.items); + source[source.length - 1] = matches[1] + "'" + helperName + "'" + matches[3]; + }; - if (rightOp === INSERT) { - // also merge right (we have split an insert with an insert) - leftArrayOperation.count += rightArrayOperation.count; - leftArrayOperation.items = leftArrayOperation.items.concat(rightArrayOperation.items); - this._operations.splice(index, 2); - } else { - // only merge left - this._operations.splice(index, 1); - } - } else if (rightOp === INSERT) { - // merge right - newArrayOperation.count += rightArrayOperation.count; - newArrayOperation.items = newArrayOperation.items.concat(rightArrayOperation.items); - this._operations.splice(index + 1, 1); - } - }, + var stringifyBlockHelperMissing = EmberHandlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation; - _composeDelete: function (index) { - var arrayOperation = this._operations[index]; - var deletesToGo = arrayOperation.count; - var leftArrayOperation = this._operations[index-1]; // may be undefined - var leftOp = leftArrayOperation && leftArrayOperation.type; - var nextArrayOperation; - var nextOp; - var nextCount; - var removeNewAndNextOp = false; - var removedItems = []; + var originalBlockValue = EmberHandlebars.JavaScriptCompiler.prototype.blockValue; + EmberHandlebars.JavaScriptCompiler.prototype.blockValue = function() { + originalBlockValue.apply(this, arguments); + stringifyBlockHelperMissing(this.source); + }; - if (leftOp === DELETE) { - arrayOperation = leftArrayOperation; - index -= 1; - } + var originalAmbiguousBlockValue = EmberHandlebars.JavaScriptCompiler.prototype.ambiguousBlockValue; + EmberHandlebars.JavaScriptCompiler.prototype.ambiguousBlockValue = function() { + originalAmbiguousBlockValue.apply(this, arguments); + stringifyBlockHelperMissing(this.source); + }; - for (var i = index + 1; deletesToGo > 0; ++i) { - nextArrayOperation = this._operations[i]; - nextOp = nextArrayOperation.type; - nextCount = nextArrayOperation.count; + /** + Rewrite simple mustaches from `{{foo}}` to `{{bind "foo"}}`. This means that + all simple mustaches in Ember's Handlebars will also set up an observer to + keep the DOM up to date when the underlying property changes. - if (nextOp === DELETE) { - arrayOperation.count += nextCount; - continue; - } + @private + @method mustache + @for Ember.Handlebars.Compiler + @param mustache + */ + EmberHandlebars.Compiler.prototype.mustache = function(mustache) { + if (!(mustache.params.length || mustache.hash)) { + var id = new Handlebars.AST.IdNode([{ part: '_triageMustache' }]); - if (nextCount > deletesToGo) { - // d:2 {r,i}:5 we reduce the retain or insert, but it stays - removedItems = removedItems.concat(nextArrayOperation.items.splice(0, deletesToGo)); - nextArrayOperation.count -= deletesToGo; + // Update the mustache node to include a hash value indicating whether the original node + // was escaped. This will allow us to properly escape values when the underlying value + // changes and we need to re-render the value. + if (!mustache.escaped) { + mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]); + mustache.hash.pairs.push(["unescaped", new Handlebars.AST.StringNode("true")]); + } + mustache = new Handlebars.AST.MustacheNode([id].concat([mustache.id]), mustache.hash, !mustache.escaped); + } - // In the case where we truncate the last arrayOperation, we don't need to - // remove it; also the deletesToGo reduction is not the entirety of - // nextCount - i -= 1; - nextCount = deletesToGo; + return Handlebars.Compiler.prototype.mustache.call(this, mustache); + }; - deletesToGo = 0; - } else { - if (nextCount === deletesToGo) { - // Handle edge case of d:2 i:2 in which case both operations go away - // during composition. - removeNewAndNextOp = true; - } - removedItems = removedItems.concat(nextArrayOperation.items); - deletesToGo -= nextCount; - } + /** + Used for precompilation of Ember Handlebars templates. This will not be used + during normal app execution. - if (nextOp === INSERT) { - // d:2 i:3 will result in delete going away - arrayOperation.count -= nextCount; - } - } + @method precompile + @for Ember.Handlebars + @static + @param {String} string The template to precompile + @param {Boolean} asObject optional parameter, defaulting to true, of whether or not the + compiled template should be returned as an Object or a String + */ + EmberHandlebars.precompile = function(string, asObject) { + var ast = Handlebars.parse(string); - if (arrayOperation.count > 0) { - // compose our new delete with possibly several operations to the right of - // disparate types - this._operations.splice(index+1, i-1-index); - } else { - // The delete operation can go away; it has merely reduced some other - // operation, as in d:3 i:4; it may also have eliminated that operation, - // as in d:3 i:3. - this._operations.splice(index, removeNewAndNextOp ? 2 : 1); - } + var options = { + knownHelpers: { + action: true, + unbound: true, + 'bind-attr': true, + template: true, + view: true, + _triageMustache: true + }, + data: true, + stringParams: true + }; - return removedItems; - }, + asObject = asObject === undefined ? true : asObject; - toString: function () { - var str = ""; - forEach(this._operations, function (operation) { - str += " " + operation.type + ":" + operation.count; - }); - return str.substring(1); - } + var environment = new EmberHandlebars.Compiler().compile(ast, options); + return new EmberHandlebars.JavaScriptCompiler().compile(environment, options, undefined, asObject); }; - /** - Internal data structure to represent an array operation. + // We don't support this for Handlebars runtime-only + if (Handlebars.compile) { + /** + The entry point for Ember Handlebars. This replaces the default + `Handlebars.compile` and turns on template-local data and String + parameters. - @method ArrayOperation - @private - @param {String} type The type of the operation. One of - `Ember.TrackedArray.{RETAIN, INSERT, DELETE}` - @param {Number} count The number of items in this operation. - @param {Array} items The items of the operation, if included. RETAIN and - INSERT include their items, DELETE does not. - */ - function ArrayOperation (operation, count, items) { - this.type = operation; // RETAIN | INSERT | DELETE - this.count = count; - this.items = items; - } + @method compile + @for Ember.Handlebars + @static + @param {String} string The template to compile + @return {Function} + */ + EmberHandlebars.compile = function(string) { + var ast = Handlebars.parse(string); + var options = { data: true, stringParams: true }; + var environment = new EmberHandlebars.Compiler().compile(ast, options); + var templateSpec = new EmberHandlebars.JavaScriptCompiler().compile(environment, options, undefined, true); - /** - Internal data structure used to include information when looking up operations - by item index. + var template = EmberHandlebars.template(templateSpec); + template.isMethod = false; //Make sure we don't wrap templates with ._super - @method ArrayOperationMatch - @private - @param {ArrayOperation} operation - @param {Number} index The index of `operation` in the array of operations. - @param {Boolean} split Whether or not the item index searched for would - require a split for a new operation type. - @param {Number} rangeStart The index of the first item in the operation, - with respect to the tracked array. The index of the last item can be computed - from `rangeStart` and `operation.count`. - */ - function ArrayOperationMatch(operation, index, split, rangeStart) { - this.operation = operation; - this.index = index; - this.split = split; - this.rangeStart = rangeStart; + return template; + }; } + + __exports__["default"] = EmberHandlebars; }); -enifed("ember-template-compiler", - ["ember-metal/core","ember-template-compiler/system/precompile","ember-template-compiler/system/compile","ember-template-compiler/system/template","ember-template-compiler/plugins","ember-template-compiler/plugins/transform-each-in-to-hash","ember-template-compiler/plugins/transform-with-as-to-hash","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { +})(); + +(function() { +define("ember-handlebars/component_lookup", + ["ember-runtime/system/object","exports"], + function(__dependency1__, __exports__) { "use strict"; - var _Ember = __dependency1__["default"]; - var precompile = __dependency2__["default"]; - var compile = __dependency3__["default"]; - var template = __dependency4__["default"]; - var registerPlugin = __dependency5__.registerPlugin; + var EmberObject = __dependency1__["default"]; - var TransformEachInToHash = __dependency6__["default"]; - var TransformWithAsToHash = __dependency7__["default"]; + var ComponentLookup = EmberObject.extend({ + lookupFactory: function(name, container) { + + container = container || this.container; - registerPlugin('ast', TransformWithAsToHash); - registerPlugin('ast', TransformEachInToHash); + var fullName = 'component:' + name, + templateFullName = 'template:components/' + name, + templateRegistered = container && container.has(templateFullName); - __exports__._Ember = _Ember; - __exports__.precompile = precompile; - __exports__.compile = compile; - __exports__.template = template; - __exports__.registerPlugin = registerPlugin; + if (templateRegistered) { + container.injection(fullName, 'layout', templateFullName); + } + + var Component = container.lookupFactory(fullName); + + // Only treat as a component if either the component + // or a template has been registered. + if (templateRegistered || Component) { + if (!Component) { + container.register(fullName, Ember.Component); + Component = container.lookupFactory(fullName); + } + return Component; + } + } + }); + + __exports__["default"] = ComponentLookup; }); -enifed("ember-template-compiler/plugins", - ["exports"], - function(__exports__) { +define("ember-handlebars/controls", + ["ember-handlebars/controls/checkbox","ember-handlebars/controls/text_field","ember-handlebars/controls/text_area","ember-metal/core","ember-handlebars-compiler","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { "use strict"; + var Checkbox = __dependency1__["default"]; + var TextField = __dependency2__["default"]; + var TextArea = __dependency3__["default"]; + + var Ember = __dependency4__["default"]; + // Ember.assert + // var emberAssert = Ember.assert; + + var EmberHandlebars = __dependency5__["default"]; + var helpers = EmberHandlebars.helpers; /** @module ember - @submodule ember-template-compiler + @submodule ember-handlebars-compiler */ /** - @private - @property helpers - */ - var plugins = { - ast: [ ] - }; - /** - Adds an AST plugin to be used by Ember.HTMLBars.compile. + The `{{input}}` helper inserts an HTML `` tag into the template, + with a `type` value of either `text` or `checkbox`. If no `type` is provided, + `text` will be the default value applied. The attributes of `{{input}}` + match those of the native HTML tag as closely as possible for these two types. - @private - @method registerASTPlugin - */ - function registerPlugin(type, Plugin) { - if (!plugins[type]) { - throw new Error('Attempting to register "' + Plugin + '" as "' + type + '" which is not a valid HTMLBars plugin type.'); - } + ## Use as text field + An `{{input}}` with no `type` or a `type` of `text` will render an HTML text input. + The following HTML attributes can be set via the helper: - plugins[type].push(Plugin); - } + + + + + + + + + + + +
`readonly``required``autofocus`
`value``placeholder``disabled`
`size``tabindex``maxlength`
`name``min``max`
`pattern``accept``autocomplete`
`autosave``formaction``formenctype`
`formmethod``formnovalidate``formtarget`
`height``inputmode``multiple`
`step``width``form`
`selectionDirection``spellcheck` 
- __exports__.registerPlugin = registerPlugin;__exports__["default"] = plugins; - }); -enifed("ember-template-compiler/plugins/transform-each-in-to-hash", - ["exports"], - function(__exports__) { - "use strict"; - /** - @module ember - @submodule ember-htmlbars - */ + When set to a quoted string, these values will be directly applied to the HTML + element. When left unquoted, these values will be bound to a property on the + template's current rendering context (most typically a controller instance). - /** - An HTMLBars AST transformation that replaces all instances of + ## Unbound: ```handlebars - {{#each item in items}} - {{/each}} + {{input value="http://www.facebook.com"}} + ``` + + + ```html + + ``` + + ## Bound: + + ```javascript + App.ApplicationController = Ember.Controller.extend({ + firstName: "Stanley", + entryNotAllowed: true + }); ``` - with ```handlebars - {{#each items keyword="item"}} - {{/each}} + {{input type="text" value=firstName disabled=entryNotAllowed size="50"}} ``` - @class TransformEachInToHash - @private - */ - function TransformEachInToHash() { - // set later within HTMLBars to the syntax package - this.syntax = null; - } - /** - @private - @method transform - @param {AST} The AST to be transformed. - */ - TransformEachInToHash.prototype.transform = function TransformEachInToHash_transform(ast) { - var pluginContext = this; - var walker = new pluginContext.syntax.Walker(); - var b = pluginContext.syntax.builders; + ```html + + ``` - walker.visit(ast, function(node) { - if (pluginContext.validate(node)) { - var removedParams = node.sexpr.params.splice(0, 2); - var keyword = removedParams[0].original; + ## Extension - // TODO: This may not be necessary. - if (!node.sexpr.hash) { - node.sexpr.hash = b.hash(); - } + Internally, `{{input type="text"}}` creates an instance of `Ember.TextField`, passing + arguments from the helper to `Ember.TextField`'s `create` method. You can extend the + capabilities of text inputs in your applications by reopening this class. For example, + if you are building a Bootstrap project where `data-*` attributes are used, you + can add one to the `TextField`'s `attributeBindings` property: - node.sexpr.hash.pairs.push(b.pair( - 'keyword', - b.string(keyword) - )); - } + + ```javascript + Ember.TextField.reopen({ + attributeBindings: ['data-error'] }); + ``` - return ast; - }; + Keep in mind when writing `Ember.TextField` subclasses that `Ember.TextField` + itself extends `Ember.Component`, meaning that it does NOT inherit + the `controller` of the parent view. - TransformEachInToHash.prototype.validate = function TransformEachInToHash_validate(node) { - return (node.type === 'BlockStatement' || node.type === 'MustacheStatement') && - node.sexpr.path.original === 'each' && - node.sexpr.params.length === 3 && - node.sexpr.params[1].type === 'PathExpression' && - node.sexpr.params[1].original === 'in'; - }; + See more about [Ember components](api/classes/Ember.Component.html) - __exports__["default"] = TransformEachInToHash; - }); -enifed("ember-template-compiler/plugins/transform-with-as-to-hash", - ["exports"], - function(__exports__) { - "use strict"; - /** - @module ember - @submodule ember-htmlbars - */ - /** - An HTMLBars AST transformation that replaces all instances of + ## Use as checkbox + + An `{{input}}` with a `type` of `checkbox` will render an HTML checkbox input. + The following HTML attributes can be set via the helper: + + * `checked` + * `disabled` + * `tabindex` + * `indeterminate` + * `name` + * `autofocus` + * `form` + + + When set to a quoted string, these values will be directly applied to the HTML + element. When left unquoted, these values will be bound to a property on the + template's current rendering context (most typically a controller instance). + + ## Unbound: ```handlebars - {{#with foo.bar as bar}} - {{/with}} + {{input type="checkbox" name="isAdmin"}} + ``` + + ```html + + ``` + + ## Bound: + + ```javascript + App.ApplicationController = Ember.Controller.extend({ + isAdmin: true + }); ``` - with ```handlebars - {{#with foo.bar as |bar|}} - {{/with}} + {{input type="checkbox" checked=isAdmin }} ``` - @private - @class TransformWithAsToHash + + ```html + + ``` + + ## Extension + + Internally, `{{input type="checkbox"}}` creates an instance of `Ember.Checkbox`, passing + arguments from the helper to `Ember.Checkbox`'s `create` method. You can extend the + capablilties of checkbox inputs in your applications by reopening this class. For example, + if you wanted to add a css class to all checkboxes in your application: + + + ```javascript + Ember.Checkbox.reopen({ + classNames: ['my-app-checkbox'] + }); + ``` + + + @method input + @for Ember.Handlebars.helpers + @param {Hash} options */ - function TransformWithAsToHash() { - // set later within HTMLBars to the syntax package - this.syntax = null; + function inputHelper(options) { + Ember.assert('You can only pass attributes to the `input` helper, not arguments', arguments.length < 2); + + var hash = options.hash, + types = options.hashTypes, + inputType = hash.type, + onEvent = hash.on; + + delete hash.type; + delete hash.on; + + if (inputType === 'checkbox') { + Ember.assert("{{input type='checkbox'}} does not support setting `value=someBooleanValue`; you must use `checked=someBooleanValue` instead.", options.hashTypes.value !== 'ID'); + return helpers.view.call(this, Checkbox, options); + } else { + if (inputType) { hash.type = inputType; } + hash.onEvent = onEvent || 'enter'; + return helpers.view.call(this, TextField, options); + } } /** - @private - @method transform - @param {AST} The AST to be transformed. - */ - TransformWithAsToHash.prototype.transform = function TransformWithAsToHash_transform(ast) { - var pluginContext = this; - var walker = new pluginContext.syntax.Walker(); + `{{textarea}}` inserts a new instance of ` + ``` + + Bound: + + In the following example, the `writtenWords` property on `App.ApplicationController` + will be updated live as the user types 'Lots of text that IS bound' into + the text area of their browser's window. + + ```javascript + App.ApplicationController = Ember.Controller.extend({ + writtenWords: "Lots of text that IS bound" }); + ``` - return ast; - }; + ```handlebars + {{textarea value=writtenWords}} + ``` - TransformWithAsToHash.prototype.validate = function TransformWithAsToHash_validate(node) { - return node.type === 'BlockStatement' && - node.sexpr.path.original === 'with' && - node.sexpr.params.length === 3 && - node.sexpr.params[1].type === 'PathExpression' && - node.sexpr.params[1].original === 'as'; - }; + Would result in the following HTML: - __exports__["default"] = TransformWithAsToHash; - }); -enifed("ember-template-compiler/system/compile", - ["htmlbars-compiler/compiler","ember-template-compiler/system/compile_options","ember-template-compiler/system/template","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-template-compiler - */ + ```html + + ``` - var compile = __dependency1__.compile; - var compileOptions = __dependency2__["default"]; - var template = __dependency3__["default"]; + If you wanted a one way binding between the text area and a div tag + somewhere else on your screen, you could use `Ember.computed.oneWay`: - /** - Uses HTMLBars `compile` function to process a string into a compiled template. + ```javascript + App.ApplicationController = Ember.Controller.extend({ + writtenWords: "Lots of text that IS bound", + outputWrittenWords: Ember.computed.oneWay("writtenWords") + }); + ``` - This is not present in production builds. + ```handlebars + {{textarea value=writtenWords}} - @private - @method compile - @param {String} templateString This is the string to be compiled by HTMLBars. - */ - __exports__["default"] = function(templateString) { - var templateSpec = compile(templateString, compileOptions()); +
+ {{outputWrittenWords}} +
+ ``` - return template(templateSpec); - } - }); -enifed("ember-template-compiler/system/compile_options", - ["ember-metal/core","ember-template-compiler/plugins","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-template-compiler - */ + Would result in the following HTML: - var Ember = __dependency1__["default"]; - var plugins = __dependency2__["default"]; + ```html + - /** - @private - @property compileOptions - */ - __exports__["default"] = function() { - var disableComponentGeneration = true; - - return { - disableComponentGeneration: disableComponentGeneration, + <-- the following div will be updated in real time as you type --> - plugins: plugins - }; - } - }); -enifed("ember-template-compiler/system/precompile", - ["htmlbars-compiler/compiler","ember-template-compiler/system/compile_options","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-template-compiler - */ +
+ Lots of text that IS bound +
+ ``` + + Finally, this example really shows the power and ease of Ember when two + properties are bound to eachother via `Ember.computed.alias`. Type into + either text area box and they'll both stay in sync. Note that + `Ember.computed.alias` costs more in terms of performance, so only use it when + your really binding in both directions: + + ```javascript + App.ApplicationController = Ember.Controller.extend({ + writtenWords: "Lots of text that IS bound", + twoWayWrittenWords: Ember.computed.alias("writtenWords") + }); + ``` - var compileSpec = __dependency1__.compileSpec; - var compileOptions = __dependency2__["default"]; + ```handlebars + {{textarea value=writtenWords}} + {{textarea value=twoWayWrittenWords}} + ``` - /** - Uses HTMLBars `compile` function to process a string into a compiled template string. - The returned string must be passed through `Ember.HTMLBars.template`. + ```html + - This is not present in production builds. + <-- both updated in real time --> - @private - @method precompile - @param {String} templateString This is the string to be compiled by HTMLBars. - */ - __exports__["default"] = function(templateString) { - return compileSpec(templateString, compileOptions()); - } - }); -enifed("ember-template-compiler/system/template", - ["exports"], - function(__exports__) { - "use strict"; - /** - @module ember - @submodule ember-template-compiler - */ + + ``` - /** - Augments the detault precompiled output of an HTMLBars template with - additional information needed by Ember. + ## Extension - @private - @method template - @param {Function} templateSpec This is the compiled HTMLBars template spec. - */ + Internally, `{{textarea}}` creates an instance of `Ember.TextArea`, passing + arguments from the helper to `Ember.TextArea`'s `create` method. You can + extend the capabilities of text areas in your application by reopening this + class. For example, if you are building a Bootstrap project where `data-*` + attributes are used, you can globally add support for a `data-*` attribute + on all `{{textarea}}`s' in your app by reopening `Ember.TextArea` or + `Ember.TextSupport` and adding it to the `attributeBindings` concatenated + property: - __exports__["default"] = function(templateSpec) { - templateSpec.isTop = true; - templateSpec.isMethod = false; + ```javascript + Ember.TextArea.reopen({ + attributeBindings: ['data-error'] + }); + ``` - return templateSpec; - } - }); -enifed("ember-testing", - ["ember-metal/core","ember-testing/initializers","ember-testing/support","ember-testing/setup_for_testing","ember-testing/test","ember-testing/adapters/adapter","ember-testing/adapters/qunit","ember-testing/helpers"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { - "use strict"; - var Ember = __dependency1__["default"]; + Keep in mind when writing `Ember.TextArea` subclasses that `Ember.TextArea` + itself extends `Ember.Component`, meaning that it does NOT inherit + the `controller` of the parent view. - // to setup initializer - // to handle various edge cases + See more about [Ember components](api/classes/Ember.Component.html) - var setupForTesting = __dependency4__["default"]; - var Test = __dependency5__["default"]; - var Adapter = __dependency6__["default"]; - var QUnitAdapter = __dependency7__["default"]; - // adds helpers to helpers object in Test + @method textarea + @for Ember.Handlebars.helpers + @param {Hash} options + */ + function textareaHelper(options) { + Ember.assert('You can only pass attributes to the `textarea` helper, not arguments', arguments.length < 2); - /** - Ember Testing + var hash = options.hash, + types = options.hashTypes; - @module ember - @submodule ember-testing - @requires ember-application - */ + return helpers.view.call(this, TextArea, options); + } - Ember.Test = Test; - Ember.Test.Adapter = Adapter; - Ember.Test.QUnitAdapter = QUnitAdapter; - Ember.setupForTesting = setupForTesting; + __exports__.inputHelper = inputHelper; + __exports__.textareaHelper = textareaHelper; }); -enifed("ember-testing/adapters/adapter", - ["ember-runtime/system/object","exports"], - function(__dependency1__, __exports__) { +define("ember-handlebars/controls/checkbox", + ["ember-metal/property_get","ember-metal/property_set","ember-views/views/view","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; - var EmberObject = __dependency1__["default"]; - - function K() { return this; } + var get = __dependency1__.get; + var set = __dependency2__.set; + var View = __dependency3__.View; /** - @module ember - @submodule ember-testing + @module ember + @submodule ember-handlebars */ /** - The primary purpose of this class is to create hooks that can be implemented - by an adapter for various test frameworks. - - @class Adapter - @namespace Ember.Test - */ - var Adapter = EmberObject.extend({ - /** - This callback will be called whenever an async operation is about to start. + The internal class used to create text inputs when the `{{input}}` + helper is used with `type` of `checkbox`. - Override this to call your framework's methods that handle async - operations. + See [handlebars.helpers.input](/api/classes/Ember.Handlebars.helpers.html#method_input) for usage details. - @public - @method asyncStart - */ - asyncStart: K, + ## Direct manipulation of `checked` - /** - This callback will be called whenever an async operation has completed. + The `checked` attribute of an `Ember.Checkbox` object should always be set + through the Ember object or by interacting with its rendered element + representation via the mouse, keyboard, or touch. Updating the value of the + checkbox via jQuery will result in the checked value of the object and its + element losing synchronization. - @public - @method asyncEnd - */ - asyncEnd: K, + ## Layout and LayoutName properties - /** - Override this method with your testing framework's false assertion. - This function is called whenever an exception occurs causing the testing - promise to fail. + Because HTML `input` elements are self closing `layout` and `layoutName` + properties will not be applied. See [Ember.View](/api/classes/Ember.View.html)'s + layout section for more information. - QUnit example: + @class Checkbox + @namespace Ember + @extends Ember.View + */ + var Checkbox = View.extend({ + instrumentDisplay: '{{input type="checkbox"}}', - ```javascript - exception: function(error) { - ok(false, error); - }; - ``` + classNames: ['ember-checkbox'], - @public - @method exception - @param {String} error The exception to be raised. - */ - exception: function(error) { - throw error; - } - }); + tagName: 'input', - __exports__["default"] = Adapter; - }); -enifed("ember-testing/adapters/qunit", - ["ember-testing/adapters/adapter","ember-metal/utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var Adapter = __dependency1__["default"]; - var inspect = __dependency2__.inspect; + attributeBindings: ['type', 'checked', 'indeterminate', 'disabled', 'tabindex', 'name', + 'autofocus', 'required', 'form'], - /** - This class implements the methods defined by Ember.Test.Adapter for the - QUnit testing framework. + type: "checkbox", + checked: false, + disabled: false, + indeterminate: false, - @class QUnitAdapter - @namespace Ember.Test - @extends Ember.Test.Adapter - */ - __exports__["default"] = Adapter.extend({ - asyncStart: function() { - QUnit.stop(); + init: function() { + this._super(); + this.on("change", this, this._updateElementValue); }, - asyncEnd: function() { - QUnit.start(); + + didInsertElement: function() { + this._super(); + get(this, 'element').indeterminate = !!get(this, 'indeterminate'); }, - exception: function(error) { - ok(false, inspect(error)); + + _updateElementValue: function() { + set(this, 'checked', this.$().prop('checked')); } }); + + __exports__["default"] = Checkbox; }); -enifed("ember-testing/helpers", - ["ember-metal/property_get","ember-metal/error","ember-metal/run_loop","ember-views/system/jquery","ember-testing/test"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { +define("ember-handlebars/controls/select", + ["ember-handlebars-compiler","ember-metal/enumerable_utils","ember-metal/property_get","ember-metal/property_set","ember-views/views/view","ember-views/views/collection_view","ember-metal/utils","ember-metal/is_none","ember-metal/computed","ember-runtime/system/native_array","ember-metal/mixin","ember-metal/properties","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __exports__) { "use strict"; - var get = __dependency1__.get; - var EmberError = __dependency2__["default"]; - var run = __dependency3__["default"]; - var jQuery = __dependency4__["default"]; - var Test = __dependency5__["default"]; + /*jshint eqeqeq:false newcap:false */ /** - * @module ember - * @submodule ember-testing + @module ember + @submodule ember-handlebars */ - var helper = Test.registerHelper; - var asyncHelper = Test.registerAsyncHelper; - var countAsync = 0; - - function currentRouteName(app){ - var appController = app.__container__.lookup('controller:application'); + var EmberHandlebars = __dependency1__["default"]; + var EnumerableUtils = __dependency2__["default"]; + var get = __dependency3__.get; + var set = __dependency4__.set; + var View = __dependency5__.View; + var CollectionView = __dependency6__["default"]; + var isArray = __dependency7__.isArray; + var isNone = __dependency8__["default"]; + var computed = __dependency9__.computed; + var A = __dependency10__.A; + var observer = __dependency11__.observer; + var defineProperty = __dependency12__.defineProperty; - return get(appController, 'currentRouteName'); - } + var indexOf = EnumerableUtils.indexOf, + indexesOf = EnumerableUtils.indexesOf, + forEach = EnumerableUtils.forEach, + replace = EnumerableUtils.replace, + precompileTemplate = EmberHandlebars.compile; - function currentPath(app){ - var appController = app.__container__.lookup('controller:application'); + var SelectOption = View.extend({ + instrumentDisplay: 'Ember.SelectOption', - return get(appController, 'currentPath'); - } + tagName: 'option', + attributeBindings: ['value', 'selected'], - function currentURL(app){ - var router = app.__container__.lookup('router:main'); + defaultTemplate: function(context, options) { + options = { data: options.data, hash: {} }; + EmberHandlebars.helpers.bind.call(context, "view.label", options); + }, - return get(router, 'location').getURL(); - } + init: function() { + this.labelPathDidChange(); + this.valuePathDidChange(); - function pauseTest(){ - Test.adapter.asyncStart(); - return new Ember.RSVP.Promise(function(){ }, 'TestAdapter paused promise'); - } + this._super(); + }, - function visit(app, url) { - var router = app.__container__.lookup('router:main'); - router.location.setURL(url); + selected: computed(function() { + var content = get(this, 'content'), + selection = get(this, 'parentView.selection'); + if (get(this, 'parentView.multiple')) { + return selection && indexOf(selection, content.valueOf()) > -1; + } else { + // Primitives get passed through bindings as objects... since + // `new Number(4) !== 4`, we use `==` below + return content == selection; + } + }).property('content', 'parentView.selection'), - if (app._readinessDeferrals > 0) { - router['initialURL'] = url; - run(app, 'advanceReadiness'); - delete router['initialURL']; - } else { - run(app, app.handleURL, url); - } + labelPathDidChange: observer('parentView.optionLabelPath', function() { + var labelPath = get(this, 'parentView.optionLabelPath'); - return app.testHelpers.wait(); - } + if (!labelPath) { return; } - function click(app, selector, context) { - var $el = app.testHelpers.findWithAssert(selector, context); - run($el, 'mousedown'); + defineProperty(this, 'label', computed(function() { + return get(this, labelPath); + }).property(labelPath)); + }), - if ($el.is(':input')) { - var type = $el.prop('type'); - if (type !== 'checkbox' && type !== 'radio' && type !== 'hidden') { - run($el, function(){ - // Firefox does not trigger the `focusin` event if the window - // does not have focus. If the document doesn't have focus just - // use trigger('focusin') instead. - if (!document.hasFocus || document.hasFocus()) { - this.focus(); - } else { - this.trigger('focusin'); - } - }); - } - } + valuePathDidChange: observer('parentView.optionValuePath', function() { + var valuePath = get(this, 'parentView.optionValuePath'); - run($el, 'mouseup'); - run($el, 'click'); + if (!valuePath) { return; } - return app.testHelpers.wait(); - } + defineProperty(this, 'value', computed(function() { + return get(this, valuePath); + }).property(valuePath)); + }) + }); - function triggerEvent(app, selector, contextOrType, typeOrOptions, possibleOptions){ - var arity = arguments.length; - var context, type, options; + var SelectOptgroup = CollectionView.extend({ + instrumentDisplay: 'Ember.SelectOptgroup', - if (arity === 3) { - // context and options are optional, so this is - // app, selector, type - context = null; - type = contextOrType; - options = {}; - } else if (arity === 4) { - // context and options are optional, so this is - if (typeof typeOrOptions === "object") { // either - // app, selector, type, options - context = null; - type = contextOrType; - options = typeOrOptions; - } else { // or - // app, selector, context, type - context = contextOrType; - type = typeOrOptions; - options = {}; - } - } else { - context = contextOrType; - type = typeOrOptions; - options = possibleOptions; - } + tagName: 'optgroup', + attributeBindings: ['label'], - var $el = app.testHelpers.findWithAssert(selector, context); + selectionBinding: 'parentView.selection', + multipleBinding: 'parentView.multiple', + optionLabelPathBinding: 'parentView.optionLabelPath', + optionValuePathBinding: 'parentView.optionValuePath', - var event = jQuery.Event(type, options); + itemViewClassBinding: 'parentView.optionView' + }); - run($el, 'trigger', event); + /** + The `Ember.Select` view class renders a + [select](https://developer.mozilla.org/en/HTML/Element/select) HTML element, + allowing the user to choose from a list of options. - return app.testHelpers.wait(); - } + The text and `value` property of each ` + + + ``` + + The `value` attribute of the selected `"); + return buffer; + } - Example (The test will pause before clicking the button): +function program3(depth0,data) { + + var stack1; + stack1 = helpers.each.call(depth0, "view.groupedContent", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(4, program4, data),contexts:[depth0],types:["ID"],data:data}); + if(stack1 || stack1 === 0) { data.buffer.push(stack1); } + else { data.buffer.push(''); } + } +function program4(depth0,data) { + + + data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.groupView", {hash:{ + 'content': ("content"), + 'label': ("label") + },hashTypes:{'content': "ID",'label': "ID"},hashContexts:{'content': depth0,'label': depth0},contexts:[depth0],types:["ID"],data:data}))); + } - ```javascript - visit('/') - return pauseTest(); +function program6(depth0,data) { + + var stack1; + stack1 = helpers.each.call(depth0, "view.content", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(7, program7, data),contexts:[depth0],types:["ID"],data:data}); + if(stack1 || stack1 === 0) { data.buffer.push(stack1); } + else { data.buffer.push(''); } + } +function program7(depth0,data) { + + + data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.optionView", {hash:{ + 'content': ("") + },hashTypes:{'content': "ID"},hashContexts:{'content': depth0},contexts:[depth0],types:["ID"],data:data}))); + } - click('.btn'); - ``` + stack1 = helpers['if'].call(depth0, "view.prompt", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],data:data}); + if(stack1 || stack1 === 0) { data.buffer.push(stack1); } + stack1 = helpers['if'].call(depth0, "view.optionGroupPath", {hash:{},hashTypes:{},hashContexts:{},inverse:self.program(6, program6, data),fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],data:data}); + if(stack1 || stack1 === 0) { data.buffer.push(stack1); } + return buffer; + +}), + attributeBindings: ['multiple', 'disabled', 'tabindex', 'name', 'required', 'autofocus', + 'form', 'size'], - @method pauseTest - @return {Object} A promise that will never resolve - */ - helper('pauseTest', pauseTest); - + /** + The `multiple` attribute of the select element. Indicates whether multiple + options can be selected. - /** - Triggers the given DOM event on the element identified by the provided selector. + @property multiple + @type Boolean + @default false + */ + multiple: false, - Example: + /** + The `disabled` attribute of the select element. Indicates whether + the element is disabled from interactions. - ```javascript - triggerEvent('#some-elem-id', 'blur'); - ``` + @property disabled + @type Boolean + @default false + */ + disabled: false, - This is actually used internally by the `keyEvent` helper like so: + /** + The `required` attribute of the select element. Indicates whether + a selected option is required for form validation. - ```javascript - triggerEvent('#some-elem-id', 'keypress', { keyCode: 13 }); - ``` + @property required + @type Boolean + @default false + @since 1.5.0 + */ + required: false, - @method triggerEvent - @param {String} selector jQuery selector for finding element on the DOM - @param {String} [context] jQuery selector that will limit the selector - argument to find only within the context's children - @param {String} type The event type to be triggered. - @param {Object} [options] The options to be passed to jQuery.Event. - @return {RSVP.Promise} - @since 1.5.0 - */ - asyncHelper('triggerEvent', triggerEvent); - }); -enifed("ember-testing/initializers", - ["ember-runtime/system/lazy_load"], - function(__dependency1__) { - "use strict"; - var onLoad = __dependency1__.onLoad; + /** + The list of options. - var name = 'deferReadiness in `testing` mode'; + If `optionLabelPath` and `optionValuePath` are not overridden, this should + be a list of strings, which will serve simultaneously as labels and values. - onLoad('Ember.Application', function(Application) { - if (!Application.initializers[name]) { - Application.initializer({ - name: name, + Otherwise, this should be a list of objects. For instance: - initialize: function(container, application){ - if (application.testing) { - application.deferReadiness(); - } - } + ```javascript + Ember.Select.create({ + content: A([ + { id: 1, firstName: 'Yehuda' }, + { id: 2, firstName: 'Tom' } + ]), + optionLabelPath: 'content.firstName', + optionValuePath: 'content.id' }); - } - }); - }); -enifed("ember-testing/setup_for_testing", - ["ember-metal/core","ember-testing/adapters/qunit","ember-views/system/jquery","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // import Test from "ember-testing/test"; // ES6TODO: fix when cycles are supported - var QUnitAdapter = __dependency2__["default"]; - var jQuery = __dependency3__["default"]; + ``` - var Test, requests; + @property content + @type Array + @default null + */ + content: null, - function incrementAjaxPendingRequests(_, xhr){ - requests.push(xhr); - Test.pendingAjaxRequests = requests.length; - } + /** + When `multiple` is `false`, the element of `content` that is currently + selected, if any. - function decrementAjaxPendingRequests(_, xhr){ - for (var i=0;i') - .css({ position: 'absolute', left: '-1000px', top: '-1000px' }) - .appendTo('body') - .on('click', handler) - .trigger('click') - .remove(); - } + /** + The path of the option group. + When this property is used, `content` should be sorted by `optionGroupPath`. - $(function() { - /* - Determine whether a checkbox checked using jQuery's "click" method will have - the correct value for its checked property. + @property optionGroupPath + @type String + @default null + */ + optionGroupPath: null, - If we determine that the current jQuery version exhibits this behavior, - patch it to work correctly as in the commit for the actual fix: - https://github.com/jquery/jquery/commit/1fb2f92. + /** + The view class for optgroup. + + @property groupView + @type Ember.View + @default Ember.SelectOptgroup */ - testCheckboxClick(function() { - if (!this.checked && !$.event.special.click) { - $.event.special.click = { - // For checkbox, fire native event so checked state will be right - trigger: function() { - if ($.nodeName( this, "input" ) && this.type === "checkbox" && this.click) { - this.click(); - return false; - } - } - }; - } - }); + groupView: SelectOptgroup, - // Try again to verify that the patch took effect or blow up. - testCheckboxClick(function() { - Ember.warn("clicked checkboxes should be checked! the jQuery patch didn't work", this.checked); - }); - }); - }); -enifed("ember-testing/test", - ["ember-metal/core","ember-metal/run_loop","ember-metal/platform","ember-runtime/compare","ember-runtime/ext/rsvp","ember-testing/setup_for_testing","ember-application/system/application","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - var emberRun = __dependency2__["default"]; - var create = __dependency3__.create; - var compare = __dependency4__["default"]; - var RSVP = __dependency5__["default"]; - var setupForTesting = __dependency6__["default"]; - var EmberApplication = __dependency7__["default"]; + groupedContent: computed(function() { + var groupPath = get(this, 'optionGroupPath'); + var groupedContent = A(); + var content = get(this, 'content') || []; - /** - @module ember - @submodule ember-testing - */ - var slice = [].slice; - var helpers = {}; - var injectHelpersCallbacks = []; + forEach(content, function(item) { + var label = get(item, groupPath); - /** - This is a container for an assortment of testing related functionality: + if (get(groupedContent, 'lastObject.label') !== label) { + groupedContent.pushObject({ + label: label, + content: A() + }); + } - * Choose your default test adapter (for your framework of choice). - * Register/Unregister additional test helpers. - * Setup callbacks to be fired when the test helpers are injected into - your application. + get(groupedContent, 'lastObject.content').push(item); + }); + + return groupedContent; + }).property('optionGroupPath', 'content.@each'), - @class Test - @namespace Ember - */ - var Test = { /** - Hash containing all known test helpers. + The view class for option. - @property _helpers - @private - @since 1.7.0 + @property optionView + @type Ember.View + @default Ember.SelectOption */ - _helpers: helpers, + optionView: SelectOption, - /** - `registerHelper` is used to register a test helper that will be injected - when `App.injectTestHelpers` is called. + _change: function() { + if (get(this, 'multiple')) { + this._changeMultiple(); + } else { + this._changeSingle(); + } + }, + + selectionDidChange: observer('selection.@each', function() { + var selection = get(this, 'selection'); + if (get(this, 'multiple')) { + if (!isArray(selection)) { + set(this, 'selection', A([selection])); + return; + } + this._selectionDidChangeMultiple(); + } else { + this._selectionDidChangeSingle(); + } + }), + + valueDidChange: observer('value', function() { + var content = get(this, 'content'), + value = get(this, 'value'), + valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''), + selectedValue = (valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection')), + selection; + + if (value !== selectedValue) { + selection = content ? content.find(function(obj) { + return value === (valuePath ? get(obj, valuePath) : obj); + }) : null; + + this.set('selection', selection); + } + }), + + + _triggerChange: function() { + var selection = get(this, 'selection'); + var value = get(this, 'value'); + + if (!isNone(selection)) { this.selectionDidChange(); } + if (!isNone(value)) { this.valueDidChange(); } + + this._change(); + }, - The helper method will always be called with the current Application as - the first parameter. + _changeSingle: function() { + var selectedIndex = this.$()[0].selectedIndex, + content = get(this, 'content'), + prompt = get(this, 'prompt'); - For example: + if (!content || !get(content, 'length')) { return; } + if (prompt && selectedIndex === 0) { set(this, 'selection', null); return; } - ```javascript - Ember.Test.registerHelper('boot', function(app) { - Ember.run(app, app.advanceReadiness); - }); - ``` + if (prompt) { selectedIndex -= 1; } + set(this, 'selection', content.objectAt(selectedIndex)); + }, - This helper can later be called without arguments because it will be - called with `app` as the first parameter. - ```javascript - App = Ember.Application.create(); - App.injectTestHelpers(); - boot(); - ``` + _changeMultiple: function() { + var options = this.$('option:selected'), + prompt = get(this, 'prompt'), + offset = prompt ? 1 : 0, + content = get(this, 'content'), + selection = get(this, 'selection'); - @public - @method registerHelper - @param {String} name The name of the helper method to add. - @param {Function} helperMethod - @param options {Object} - */ - registerHelper: function(name, helperMethod) { - helpers[name] = { - method: helperMethod, - meta: { wait: false } - }; + if (!content) { return; } + if (options) { + var selectedIndexes = options.map(function() { + return this.index - offset; + }).toArray(); + var newSelection = content.objectsAt(selectedIndexes); + + if (isArray(selection)) { + replace(selection, 0, get(selection, 'length'), newSelection); + } else { + set(this, 'selection', newSelection); + } + } }, - /** - `registerAsyncHelper` is used to register an async test helper that will be injected - when `App.injectTestHelpers` is called. + _selectionDidChangeSingle: function() { + var el = this.get('element'); + if (!el) { return; } - The helper method will always be called with the current Application as - the first parameter. + var content = get(this, 'content'), + selection = get(this, 'selection'), + selectionIndex = content ? indexOf(content, selection) : -1, + prompt = get(this, 'prompt'); - For example: + if (prompt) { selectionIndex += 1; } + if (el) { el.selectedIndex = selectionIndex; } + }, - ```javascript - Ember.Test.registerAsyncHelper('boot', function(app) { - Ember.run(app, app.advanceReadiness); - }); - ``` + _selectionDidChangeMultiple: function() { + var content = get(this, 'content'), + selection = get(this, 'selection'), + selectedIndexes = content ? indexesOf(content, selection) : [-1], + prompt = get(this, 'prompt'), + offset = prompt ? 1 : 0, + options = this.$('option'), + adjusted; - The advantage of an async helper is that it will not run - until the last async helper has completed. All async helpers - after it will wait for it complete before running. + if (options) { + options.each(function() { + adjusted = this.index > -1 ? this.index - offset : -1; + this.selected = indexOf(selectedIndexes, adjusted) > -1; + }); + } + }, + init: function() { + this._super(); + this.on("didInsertElement", this, this._triggerChange); + this.on("change", this, this._change); + } + }); - For example: + __exports__["default"] = Select + __exports__.Select = Select; + __exports__.SelectOption = SelectOption; + __exports__.SelectOptgroup = SelectOptgroup; + }); +define("ember-handlebars/controls/text_area", + ["ember-metal/property_get","ember-views/views/component","ember-handlebars/controls/text_support","ember-metal/mixin","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; - ```javascript - Ember.Test.registerAsyncHelper('deletePost', function(app, postId) { - click('.delete-' + postId); - }); + /** + @module ember + @submodule ember-handlebars + */ + var get = __dependency1__.get; + var Component = __dependency2__["default"]; + var TextSupport = __dependency3__["default"]; + var observer = __dependency4__.observer; - // ... in your test - visit('/post/2'); - deletePost(2); - visit('/post/3'); - deletePost(3); - ``` + /** + The internal class used to create textarea element when the `{{textarea}}` + helper is used. - @public - @method registerAsyncHelper - @param {String} name The name of the helper method to add. - @param {Function} helperMethod - @since 1.2.0 - */ - registerAsyncHelper: function(name, helperMethod) { - helpers[name] = { - method: helperMethod, - meta: { wait: true } - }; - }, + See [handlebars.helpers.textarea](/api/classes/Ember.Handlebars.helpers.html#method_textarea) for usage details. - /** - Remove a previously added helper method. + ## Layout and LayoutName properties - Example: + Because HTML `textarea` elements do not contain inner HTML the `layout` and + `layoutName` properties will not be applied. See [Ember.View](/api/classes/Ember.View.html)'s + layout section for more information. - ```javascript - Ember.Test.unregisterHelper('wait'); - ``` + @class TextArea + @namespace Ember + @extends Ember.Component + @uses Ember.TextSupport + */ + var TextArea = Component.extend(TextSupport, { + instrumentDisplay: '{{textarea}}', - @public - @method unregisterHelper - @param {String} name The helper to remove. - */ - unregisterHelper: function(name) { - delete helpers[name]; - delete Test.Promise.prototype[name]; - }, + classNames: ['ember-text-area'], - /** - Used to register callbacks to be fired whenever `App.injectTestHelpers` - is called. + tagName: "textarea", + attributeBindings: ['rows', 'cols', 'name', 'selectionEnd', 'selectionStart', 'wrap'], + rows: null, + cols: null, - The callback will receive the current application as an argument. + _updateElementValue: observer('value', function() { + // We do this check so cursor position doesn't get affected in IE + var value = get(this, 'value'), + $el = this.$(); + if ($el && value !== $el.val()) { + $el.val(value); + } + }), - Example: + init: function() { + this._super(); + this.on("didInsertElement", this, this._updateElementValue); + } - ```javascript - Ember.Test.onInjectHelpers(function() { - Ember.$(document).ajaxSend(function() { - Test.pendingAjaxRequests++; - }); + }); - Ember.$(document).ajaxComplete(function() { - Test.pendingAjaxRequests--; - }); - }); - ``` + __exports__["default"] = TextArea; + }); +define("ember-handlebars/controls/text_field", + ["ember-metal/property_get","ember-metal/property_set","ember-views/views/component","ember-handlebars/controls/text_support","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-handlebars + */ - @public - @method onInjectHelpers - @param {Function} callback The function to be called. - */ - onInjectHelpers: function(callback) { - injectHelpersCallbacks.push(callback); - }, + var get = __dependency1__.get; + var set = __dependency2__.set; + var Component = __dependency3__["default"]; + var TextSupport = __dependency4__["default"]; - /** - This returns a thenable tailored for testing. It catches failed - `onSuccess` callbacks and invokes the `Ember.Test.adapter.exception` - callback in the last chained then. + /** - This method should be returned by async helpers such as `wait`. + The internal class used to create text inputs when the `{{input}}` + helper is used with `type` of `text`. - @public - @method promise - @param {Function} resolver The function used to resolve the promise. - */ - promise: function(resolver) { - return new Test.Promise(resolver); - }, + See [Handlebars.helpers.input](/api/classes/Ember.Handlebars.helpers.html#method_input) for usage details. - /** - Used to allow ember-testing to communicate with a specific testing - framework. + ## Layout and LayoutName properties - You can manually set it before calling `App.setupForTesting()`. + Because HTML `input` elements are self closing `layout` and `layoutName` + properties will not be applied. See [Ember.View](/api/classes/Ember.View.html)'s + layout section for more information. - Example: + @class TextField + @namespace Ember + @extends Ember.Component + @uses Ember.TextSupport + */ + var TextField = Component.extend(TextSupport, { + instrumentDisplay: '{{input type="text"}}', - ```javascript - Ember.Test.adapter = MyCustomAdapter.create() - ``` + classNames: ['ember-text-field'], + tagName: "input", + attributeBindings: ['type', 'value', 'size', 'pattern', 'name', 'min', 'max', + 'accept', 'autocomplete', 'autosave', 'formaction', + 'formenctype', 'formmethod', 'formnovalidate', 'formtarget', + 'height', 'inputmode', 'list', 'multiple', 'pattern', 'step', + 'width'], - If you do not set it, ember-testing will default to `Ember.Test.QUnitAdapter`. + /** + The `value` attribute of the input element. As the user inputs text, this + property is updated live. - @public - @property adapter - @type {Class} The adapter to be used. - @default Ember.Test.QUnitAdapter + @property value + @type String + @default "" */ - adapter: null, + value: "", /** - Replacement for `Ember.RSVP.resolve` - The only difference is this uses - an instance of `Ember.Test.Promise` + The `type` attribute of the input element. - @public - @method resolve - @param {Mixed} The value to resolve - @since 1.2.0 + @property type + @type String + @default "text" */ - resolve: function(val) { - return Test.promise(function(resolve) { - return resolve(val); - }); - }, + type: "text", /** - This allows ember-testing to play nicely with other asynchronous - events, such as an application that is waiting for a CSS3 - transition or an IndexDB transaction. + The `size` of the text field in characters. - For example: + @property size + @type String + @default null + */ + size: null, - ```javascript - Ember.Test.registerWaiter(function() { - return myPendingTransactions() == 0; - }); - ``` - The `context` argument allows you to optionally specify the `this` - with which your callback will be invoked. + /** + The `pattern` attribute of input element. - For example: + @property pattern + @type String + @default null + */ + pattern: null, - ```javascript - Ember.Test.registerWaiter(MyDB, MyDB.hasPendingTransactions); - ``` + /** + The `min` attribute of input element used with `type="number"` or `type="range"`. - @public - @method registerWaiter - @param {Object} context (optional) - @param {Function} callback - @since 1.2.0 + @property min + @type String + @default null + @since 1.4.0 */ - registerWaiter: function(context, callback) { - if (arguments.length === 1) { - callback = context; - context = null; - } - if (!this.waiters) { - this.waiters = Ember.A(); - } - this.waiters.push([context, callback]); - }, + min: null, + /** - `unregisterWaiter` is used to unregister a callback that was - registered with `registerWaiter`. + The `max` attribute of input element used with `type="number"` or `type="range"`. - @public - @method unregisterWaiter - @param {Object} context (optional) - @param {Function} callback - @since 1.2.0 + @property max + @type String + @default null + @since 1.4.0 */ - unregisterWaiter: function(context, callback) { - var pair; - if (!this.waiters) { return; } - if (arguments.length === 1) { - callback = context; - context = null; - } - pair = [context, callback]; - this.waiters = Ember.A(this.waiters.filter(function(elt) { - return compare(elt, pair)!==0; - })); - } - }; - - function helper(app, name) { - var fn = helpers[name].method; - var meta = helpers[name].meta; + max: null + }); - return function() { - var args = slice.call(arguments); - var lastPromise = Test.lastPromise; + __exports__["default"] = TextField; + }); +define("ember-handlebars/controls/text_support", + ["ember-metal/property_get","ember-metal/property_set","ember-metal/mixin","ember-runtime/mixins/target_action_support","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-handlebars + */ - args.unshift(app); + var get = __dependency1__.get; + var set = __dependency2__.set; + var Mixin = __dependency3__.Mixin; + var TargetActionSupport = __dependency4__["default"]; - // some helpers are not async and - // need to return a value immediately. - // example: `find` - if (!meta.wait) { - return fn.apply(app, args); - } + /** + Shared mixin used by `Ember.TextField` and `Ember.TextArea`. - if (!lastPromise) { - // It's the first async helper in current context - lastPromise = fn.apply(app, args); - } else { - // wait for last helper's promise to resolve - // and then execute - run(function() { - lastPromise = Test.resolve(lastPromise).then(function() { - return fn.apply(app, args); - }); - }); - } + @class TextSupport + @namespace Ember + @uses Ember.TargetActionSupport + @extends Ember.Mixin + @private + */ + var TextSupport = Mixin.create(TargetActionSupport, { + value: "", - return lastPromise; - }; - } + attributeBindings: ['placeholder', 'disabled', 'maxlength', 'tabindex', 'readonly', + 'autofocus', 'form', 'selectionDirection', 'spellcheck', 'required', + 'title', 'autocapitalize', 'autocorrect'], + placeholder: null, + disabled: false, + maxlength: null, - function run(fn) { - if (!emberRun.currentRunLoop) { - emberRun(fn); - } else { - fn(); - } - } + init: function() { + this._super(); + this.on("focusOut", this, this._elementValueDidChange); + this.on("change", this, this._elementValueDidChange); + this.on("paste", this, this._elementValueDidChange); + this.on("cut", this, this._elementValueDidChange); + this.on("input", this, this._elementValueDidChange); + this.on("keyUp", this, this.interpretKeyEvents); + }, - EmberApplication.reopen({ /** - This property contains the testing helpers for the current application. These - are created once you call `injectTestHelpers` on your `Ember.Application` - instance. The included helpers are also available on the `window` object by - default, but can be used from this object on the individual application also. + The action to be sent when the user presses the return key. - @property testHelpers - @type {Object} - @default {} + This is similar to the `{{action}}` helper, but is fired when + the user presses the return key when editing a text field, and sends + the value of the field as the context. + + @property action + @type String + @default null */ - testHelpers: {}, + action: null, /** - This property will contain the original methods that were registered - on the `helperContainer` before `injectTestHelpers` is called. - - When `removeTestHelpers` is called, these methods are restored to the - `helperContainer`. - - @property originalMethods - @type {Object} - @default {} - @private - @since 1.3.0 - */ - originalMethods: {}, + The event that should send the action. + Options are: - /** - This property indicates whether or not this application is currently in - testing mode. This is set when `setupForTesting` is called on the current - application. + * `enter`: the user pressed enter + * `keyPress`: the user pressed a key - @property testing - @type {Boolean} - @default false - @since 1.3.0 + @property onEvent + @type String + @default enter */ - testing: false, + onEvent: 'enter', /** - This hook defers the readiness of the application, so that you can start - the app when your tests are ready to run. It also sets the router's - location to 'none', so that the window's location will not be modified - (preventing both accidental leaking of state between tests and interference - with your testing framework). + Whether they `keyUp` event that triggers an `action` to be sent continues + propagating to other views. - Example: + By default, when the user presses the return key on their keyboard and + the text field has an `action` set, the action will be sent to the view's + controller and the key event will stop propagating. - ``` - App.setupForTesting(); - ``` + If you would like parent views to receive the `keyUp` event even after an + action has been dispatched, set `bubbles` to true. - @method setupForTesting + @property bubbles + @type Boolean + @default false */ - setupForTesting: function() { - setupForTesting(); + bubbles: false, - this.testing = true; + interpretKeyEvents: function(event) { + var map = TextSupport.KEY_EVENTS; + var method = map[event.keyCode]; - this.Router.reopen({ - location: 'none' - }); + this._elementValueDidChange(); + if (method) { return this[method](event); } + }, + + _elementValueDidChange: function() { + set(this, 'value', this.$().val()); }, /** - This will be used as the container to inject the test helpers into. By - default the helpers are injected into `window`. + The action to be sent when the user inserts a new line. - @property helperContainer - @type {Object} The object to be used for test helpers. - @default window - @since 1.2.0 + Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 13. + Uses sendAction to send the `enter` action to the controller. + + @method insertNewline + @param {Event} event */ - helperContainer: window, + insertNewline: function(event) { + sendAction('enter', this, event); + sendAction('insert-newline', this, event); + }, /** - This injects the test helpers into the `helperContainer` object. If an object is provided - it will be used as the helperContainer. If `helperContainer` is not set it will default - to `window`. If a function of the same name has already been defined it will be cached - (so that it can be reset if the helper is removed with `unregisterHelper` or - `removeTestHelpers`). + Called when the user hits escape. - Any callbacks registered with `onInjectHelpers` will be called once the - helpers have been injected. + Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 27. + Uses sendAction to send the `escape-press` action to the controller. - Example: - ``` - App.injectTestHelpers(); - ``` + @method cancel + @param {Event} event + */ + cancel: function(event) { + sendAction('escape-press', this, event); + }, + + /** + Called when the text area is focused. - @method injectTestHelpers + @method focusIn + @param {Event} event */ - injectTestHelpers: function(helperContainer) { - if (helperContainer) { this.helperContainer = helperContainer; } + focusIn: function(event) { + sendAction('focus-in', this, event); + }, - this.testHelpers = {}; - for (var name in helpers) { - this.originalMethods[name] = this.helperContainer[name]; - this.testHelpers[name] = this.helperContainer[name] = helper(this, name); - protoWrap(Test.Promise.prototype, name, helper(this, name), helpers[name].meta.wait); - } + /** + Called when the text area is blurred. - for(var i = 0, l = injectHelpersCallbacks.length; i < l; i++) { - injectHelpersCallbacks[i](this); - } + @method focusOut + @param {Event} event + */ + focusOut: function(event) { + sendAction('focus-out', this, event); }, /** - This removes all helpers that have been registered, and resets and functions - that were overridden by the helpers. - - Example: + The action to be sent when the user presses a key. Enabled by setting + the `onEvent` property to `keyPress`. - ```javascript - App.removeTestHelpers(); - ``` + Uses sendAction to send the `keyPress` action to the controller. - @public - @method removeTestHelpers + @method keyPress + @param {Event} event */ - removeTestHelpers: function() { - for (var name in helpers) { - this.helperContainer[name] = this.originalMethods[name]; - delete this.testHelpers[name]; - delete this.originalMethods[name]; - } + keyPress: function(event) { + sendAction('key-press', this, event); } - }); - - // This method is no longer needed - // But still here for backwards compatibility - // of helper chaining - function protoWrap(proto, name, callback, isAsync) { - proto[name] = function() { - var args = arguments; - if (isAsync) { - return callback.apply(this, args); - } else { - return this.then(function() { - return callback.apply(this, args); - }); - } - }; - } - - Test.Promise = function() { - RSVP.Promise.apply(this, arguments); - Test.lastPromise = this; - }; - Test.Promise.prototype = create(RSVP.Promise.prototype); - Test.Promise.prototype.constructor = Test.Promise; + }); - // Patch `then` to isolate async methods - // specifically `Ember.Test.lastPromise` - var originalThen = RSVP.Promise.prototype.then; - Test.Promise.prototype.then = function(onSuccess, onFailure) { - return originalThen.call(this, function(val) { - return isolate(onSuccess, val); - }, onFailure); + TextSupport.KEY_EVENTS = { + 13: 'insertNewline', + 27: 'cancel' }; - // This method isolates nested async methods - // so that they don't conflict with other last promises. - // - // 1. Set `Ember.Test.lastPromise` to null - // 2. Invoke method - // 3. Return the last promise created during method - // 4. Restore `Ember.Test.lastPromise` to original value - function isolate(fn, val) { - var value, lastPromise; - - // Reset lastPromise for nested helpers - Test.lastPromise = null; + // In principle, this shouldn't be necessary, but the legacy + // sendAction semantics for TextField are different from + // the component semantics so this method normalizes them. + function sendAction(eventName, view, event) { + var action = get(view, eventName), + on = get(view, 'onEvent'), + value = get(view, 'value'); - value = fn(val); + // back-compat support for keyPress as an event name even though + // it's also a method name that consumes the event (and therefore + // incompatible with sendAction semantics). + if (on === eventName || (on === 'keyPress' && eventName === 'key-press')) { + view.sendAction('action', value); + } - lastPromise = Test.lastPromise; + view.sendAction(eventName, value); - // If the method returned a promise - // return that promise. If not, - // return the last async helper's promise - if ((value && (value instanceof Test.Promise)) || !lastPromise) { - return value; - } else { - run(function() { - lastPromise = Test.resolve(lastPromise).then(function() { - return value; - }); - }); - return lastPromise; + if (action || on === eventName) { + if(!get(view, 'bubbles')) { + event.stopPropagation(); + } } } - __exports__["default"] = Test; + __exports__["default"] = TextSupport; }); -enifed("ember-views", - ["ember-runtime","ember-views/system/jquery","ember-views/system/utils","ember-views/system/render_buffer","ember-views/system/ext","ember-views/views/states","ember-views/views/core_view","ember-views/views/view","ember-views/views/container_view","ember-views/views/collection_view","ember-views/views/component","ember-views/system/event_dispatcher","ember-views/mixins/view_target_action_support","ember-views/component_lookup","ember-views/views/checkbox","ember-views/mixins/text_support","ember-views/views/text_field","ember-views/views/text_area","ember-views/views/bound_view","ember-views/views/simple_bound_view","ember-views/views/metamorph_view","ember-views/views/select","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__, __dependency22__, __exports__) { +define("ember-handlebars/ext", + ["ember-metal/core","ember-runtime/system/string","ember-handlebars-compiler","ember-metal/property_get","ember-metal/binding","ember-metal/error","ember-metal/mixin","ember-metal/is_empty","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { "use strict"; - /** - Ember Views + var Ember = __dependency1__["default"]; + // Ember.FEATURES, Ember.assert, Ember.Handlebars, Ember.lookup + // var emberAssert = Ember.assert; - @module ember - @submodule ember-views - @requires ember-runtime - @main ember-views - */ + var fmt = __dependency2__.fmt; - // BEGIN IMPORTS - var Ember = __dependency1__["default"]; - var jQuery = __dependency2__["default"]; - var isSimpleClick = __dependency3__.isSimpleClick; - var getViewClientRects = __dependency3__.getViewClientRects; - var getViewBoundingClientRect = __dependency3__.getViewBoundingClientRect; - var RenderBuffer = __dependency4__["default"]; - // for the side effect of extending Ember.run.queues - var cloneStates = __dependency6__.cloneStates; - var states = __dependency6__.states; - - var CoreView = __dependency7__["default"]; - var View = __dependency8__["default"]; - var ContainerView = __dependency9__["default"]; - var CollectionView = __dependency10__["default"]; - var Component = __dependency11__["default"]; - - var EventDispatcher = __dependency12__["default"]; - var ViewTargetActionSupport = __dependency13__["default"]; - var ComponentLookup = __dependency14__["default"]; - var Checkbox = __dependency15__["default"]; - var TextSupport = __dependency16__["default"]; - var TextField = __dependency17__["default"]; - var TextArea = __dependency18__["default"]; - - var BoundView = __dependency19__["default"]; - var SimpleBoundView = __dependency20__["default"]; - var _MetamorphView = __dependency21__["default"]; - var _SimpleMetamorphView = __dependency21__._SimpleMetamorphView; - var _Metamorph = __dependency21__._Metamorph; - var Select = __dependency22__.Select; - var SelectOption = __dependency22__.SelectOption; - var SelectOptgroup = __dependency22__.SelectOptgroup; - // END IMPORTS - - /** - Alias for jQuery - - @method $ - @for Ember - */ + var EmberHandlebars = __dependency3__["default"]; + var helpers = EmberHandlebars.helpers; - // BEGIN EXPORTS - Ember.$ = jQuery; + var get = __dependency4__.get; + var isGlobalPath = __dependency5__.isGlobalPath; + var EmberError = __dependency6__["default"]; + var IS_BINDING = __dependency7__.IS_BINDING; - Ember.ViewTargetActionSupport = ViewTargetActionSupport; - Ember.RenderBuffer = RenderBuffer; + // late bound via requireModule because of circular dependencies. + var resolveHelper, + SimpleHandlebarsView; - var ViewUtils = Ember.ViewUtils = {}; - ViewUtils.isSimpleClick = isSimpleClick; - ViewUtils.getViewClientRects = getViewClientRects; - ViewUtils.getViewBoundingClientRect = getViewBoundingClientRect; - - Ember.CoreView = CoreView; - Ember.View = View; - Ember.View.states = states; - Ember.View.cloneStates = cloneStates; - Ember.Checkbox = Checkbox; - Ember.TextField = TextField; - Ember.TextArea = TextArea; + var isEmpty = __dependency8__["default"]; - Ember._SimpleBoundView = SimpleBoundView; - Ember._BoundView = BoundView; - Ember._SimpleMetamorphView = _SimpleMetamorphView; - Ember._MetamorphView = _MetamorphView; - Ember._Metamorph = _Metamorph; - Ember.Select = Select; - Ember.SelectOption = SelectOption; - Ember.SelectOptgroup = SelectOptgroup; + var slice = [].slice, originalTemplate = EmberHandlebars.template; + + /** + If a path starts with a reserved keyword, returns the root + that should be used. + + @private + @method normalizePath + @for Ember + @param root {Object} + @param path {String} + @param data {Hash} + */ + function normalizePath(root, path, data) { + var keywords = (data && data.keywords) || {}, + keyword, isKeyword; + + // Get the first segment of the path. For example, if the + // path is "foo.bar.baz", returns "foo". + keyword = path.split('.', 1)[0]; + + // Test to see if the first path is a keyword that has been + // passed along in the view's data hash. If so, we will treat + // that object as the new root. + if (keywords.hasOwnProperty(keyword)) { + // Look up the value in the template's data hash. + root = keywords[keyword]; + isKeyword = true; + + // Handle cases where the entire path is the reserved + // word. In that case, return the object itself. + if (path === keyword) { + path = ''; + } else { + // Strip the keyword from the path and look up + // the remainder from the newly found root. + path = path.substr(keyword.length+1); + } + } + + return { root: root, path: path, isKeyword: isKeyword }; + }; - Ember.TextSupport = TextSupport; - Ember.ComponentLookup = ComponentLookup; - Ember.ContainerView = ContainerView; - Ember.CollectionView = CollectionView; - Ember.Component = Component; - Ember.EventDispatcher = EventDispatcher; - // END EXPORTS - __exports__["default"] = Ember; - }); -enifed("ember-views/attr_nodes/attr_node", - ["ember-metal/streams/utils","ember-metal/run_loop","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; /** - @module ember - @submodule ember-htmlbars + Lookup both on root and on window. If the path starts with + a keyword, the corresponding object will be looked up in the + template's data hash and used to resolve the path. + + @method get + @for Ember.Handlebars + @param {Object} root The object to look up the property on + @param {String} path The path to be lookedup + @param {Object} options The template's option hash */ + function handlebarsGet(root, path, options) { + var data = options && options.data, + normalizedPath = normalizePath(root, path, data), + value; - var read = __dependency1__.read; - var subscribe = __dependency1__.subscribe; - var unsubscribe = __dependency1__.unsubscribe; - var run = __dependency2__["default"]; + + root = normalizedPath.root; + path = normalizedPath.path; + + value = get(root, path); + + if (value === undefined && root !== Ember.lookup && isGlobalPath(path)) { + value = get(Ember.lookup, path); + } + - function AttrNode(attrName, attrValue) { - this.init(attrName, attrValue); + return value; } - AttrNode.prototype.init = function init(attrName, simpleAttrValue){ - this.isView = true; + /** + This method uses `Ember.Handlebars.get` to lookup a value, then ensures + that the value is escaped properly. + + If `unescaped` is a truthy value then the escaping will not be performed. - // That these semantics are used is very unfortunate. - this.tagName = ''; - this.classNameBindings = []; + @method getEscaped + @for Ember.Handlebars + @param {Object} root The object to look up the property on + @param {String} path The path to be lookedup + @param {Object} options The template's option hash + @since 1.4.0 + */ + function getEscaped(root, path, options) { + var result = handlebarsGet(root, path, options); - this.attrName = attrName; - this.attrValue = simpleAttrValue; - this.isDirty = true; - this.lastValue = null; + if (result === null || result === undefined) { + result = ""; + } else if (!(result instanceof Handlebars.SafeString)) { + result = String(result); + } + if (!options.hash.unescaped){ + result = Handlebars.Utils.escapeExpression(result); + } - subscribe(this.attrValue, this.rerender, this); + return result; }; - AttrNode.prototype.renderIfDirty = function renderIfDirty(){ - if (this.isDirty) { - var value = read(this.attrValue); - if (value !== this.lastValue) { - this._renderer.renderTree(this, this._parentView); + function resolveParams(context, params, options) { + var resolvedParams = [], types = options.types, param, type; + + for (var i=0, l=params.length; i _actions) - // - // Calling super is only OK here since we KNOW that - // there is another Mixin loaded first. - this._super.apply(this, arguments); + In this case, when the `name` property of the template's context changes, + the rendered value of the helper will update to reflect this change. - var deprecatedProperty, replacementProperty; - var layoutSpecified = (props.layoutName || props.layout || get(this, 'layoutName')); + ## Example with options - if (props.templateName && !layoutSpecified) { - deprecatedProperty = 'templateName'; - replacementProperty = 'layoutName'; + Like normal handlebars helpers, bound helpers have access to the options + passed into the helper call. - props.layoutName = props.templateName; - delete props['templateName']; + ```javascript + Ember.Handlebars.registerBoundHelper('repeat', function(value, options) { + var count = options.hash.count; + var a = []; + while(a.length < count) { + a.push(value); } + return a.join(''); + }); + ``` - if (props.template && !layoutSpecified) { - deprecatedProperty = 'template'; - replacementProperty = 'layout'; + This helper could be used in a template as follows: - props.layout = props.template; - delete props['template']; - } + ```handlebars + {{repeat text count=3}} + ``` - Ember.deprecate('Do not specify ' + deprecatedProperty + ' on a Component, use ' + replacementProperty + ' instead.', !deprecatedProperty); - } - }); - }); -enifed("ember-views/mixins/text_support", - ["ember-metal/property_get","ember-metal/property_set","ember-metal/mixin","ember-runtime/mixins/target_action_support","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-views - */ + ## Example with bound options - var get = __dependency1__.get; - var set = __dependency2__.set; - var Mixin = __dependency3__.Mixin; - var TargetActionSupport = __dependency4__["default"]; + Bound hash options are also supported. Example: - /** - `TextSupport` is a shared mixin used by both `Ember.TextField` and - `Ember.TextArea`. `TextSupport` adds a number of methods that allow you to - specify a controller action to invoke when a certain event is fired on your - text field or textarea. The specifed controller action would get the current - value of the field passed in as the only argument unless the value of - the field is empty. In that case, the instance of the field itself is passed - in as the only argument. + ```handlebars + {{repeat text count=numRepeats}} + ``` + + In this example, count will be bound to the value of + the `numRepeats` property on the context. If that property + changes, the helper will be re-rendered. - Let's use the pressing of the escape key as an example. If you wanted to - invoke a controller action when a user presses the escape key while on your - field, you would use the `escape-press` attribute on your field like so: + ## Example with extra dependencies - ```handlebars - {{! application.hbs}} + The `Ember.Handlebars.registerBoundHelper` method takes a variable length + third parameter which indicates extra dependencies on the passed in value. + This allows the handlebars helper to update when these dependencies change. - {{input escape-press='alertUser'}} + ```javascript + Ember.Handlebars.registerBoundHelper('capitalizeName', function(value) { + return value.get('name').toUpperCase(); + }, 'name'); ``` - ```javascript - App = Ember.Application.create(); + ## Example with multiple bound properties - App.ApplicationController = Ember.Controller.extend({ - actions: { - alertUser: function ( currentValue ) { - alert( 'escape pressed, current value: ' + currentValue ); - } - } - }); + `Ember.Handlebars.registerBoundHelper` supports binding to + multiple properties, e.g.: + + ```javascript + Ember.Handlebars.registerBoundHelper('concatenate', function() { + var values = Array.prototype.slice.call(arguments, 0, -1); + return values.join('||'); + }); ``` - The following chart is a visual representation of what takes place when the - escape key is pressed in this scenario: - - The Template - +---------------------------+ - | | - | escape-press='alertUser' | - | | TextSupport Mixin - +----+----------------------+ +-------------------------------+ - | | cancel method | - | escape button pressed | | - +-------------------------------> | checks for the `escape-press` | - | attribute and pulls out the | - +-------------------------------+ | `alertUser` value | - | action name 'alertUser' +-------------------------------+ - | sent to controller - v - Controller - +------------------------------------------ + - | | - | actions: { | - | alertUser: function( currentValue ){ | - | alert( 'the esc key was pressed!' ) | - | } | - | } | - | | - +-------------------------------------------+ - - Here are the events that we currently support along with the name of the - attribute you would need to use on your field. To reiterate, you would use the - attribute name like so: + Which allows for template syntax such as `{{concatenate prop1 prop2}}` or + `{{concatenate prop1 prop2 prop3}}`. If any of the properties change, + the helper will re-render. Note that dependency keys cannot be + using in conjunction with multi-property helpers, since it is ambiguous + which property the dependent keys would belong to. + + ## Use with unbound helper + + The `{{unbound}}` helper can be used with bound helper invocations + to render them in their unbound form, e.g. ```handlebars - {{input attribute-name='controllerAction'}} - ``` - - +--------------------+----------------+ - | | | - | event | attribute name | - +--------------------+----------------+ - | new line inserted | insert-newline | - | | | - | enter key pressed | insert-newline | - | | | - | cancel key pressed | escape-press | - | | | - | focusin | focus-in | - | | | - | focusout | focus-out | - | | | - | keypress | key-press | - | | | - | keyup | key-up | - | | | - | keydown | key-down | - +--------------------+----------------+ + {{unbound capitalize name}} + ``` - @class TextSupport - @namespace Ember - @uses Ember.TargetActionSupport - @extends Ember.Mixin - @private + In this example, if the name property changes, the helper + will not re-render. + + ## Use with blocks not supported + + Bound helpers do not support use with Handlebars blocks or + the addition of child views of any kind. + + @method registerBoundHelper + @for Ember.Handlebars + @param {String} name + @param {Function} function + @param {String} dependentKeys* */ - var TextSupport = Mixin.create(TargetActionSupport, { - value: "", + function registerBoundHelper(name, fn) { + var boundHelperArgs = slice.call(arguments, 1), + boundFn = makeBoundHelper.apply(this, boundHelperArgs); + EmberHandlebars.registerHelper(name, boundFn); + }; - attributeBindings: [ - 'autocapitalize', - 'autocorrect', - 'autofocus', - 'disabled', - 'form', - 'maxlength', - 'placeholder', - 'readonly', - 'required', - 'selectionDirection', - 'spellcheck', - 'tabindex', - 'title' - ], - placeholder: null, - disabled: false, - maxlength: null, + /** + A helper function used by `registerBoundHelper`. Takes the + provided Handlebars helper function fn and returns it in wrapped + bound helper form. - init: function() { - this._super(); - this.on("paste", this, this._elementValueDidChange); - this.on("cut", this, this._elementValueDidChange); - this.on("input", this, this._elementValueDidChange); - }, + The main use case for using this outside of `registerBoundHelper` + is for registering helpers on the container: - /** - The action to be sent when the user presses the return key. + ```js + var boundHelperFn = Ember.Handlebars.makeBoundHelper(function(word) { + return word.toUpperCase(); + }); - This is similar to the `{{action}}` helper, but is fired when - the user presses the return key when editing a text field, and sends - the value of the field as the context. + container.register('helper:my-bound-helper', boundHelperFn); + ``` - @property action - @type String - @default null - */ - action: null, + In the above example, if the helper function hadn't been wrapped in + `makeBoundHelper`, the registered helper would be unbound. - /** - The event that should send the action. + @method makeBoundHelper + @for Ember.Handlebars + @param {Function} function + @param {String} dependentKeys* + @since 1.2.0 + */ + function makeBoundHelper(fn) { + if (!SimpleHandlebarsView) { SimpleHandlebarsView = requireModule('ember-handlebars/views/handlebars_bound_view')['SimpleHandlebarsView']; } // ES6TODO: stupid circular dep + + var dependentKeys = slice.call(arguments, 1); + + function helper() { + var properties = slice.call(arguments, 0, -1), + numProperties = properties.length, + options = arguments[arguments.length - 1], + normalizedProperties = [], + data = options.data, + types = data.isUnbound ? slice.call(options.types, 1) : options.types, + hash = options.hash, + view = data.view, + contexts = options.contexts, + currentContext = (contexts && contexts.length) ? contexts[0] : this, + prefixPathForDependentKeys = '', + loc, len, hashOption, + boundOption, property, + normalizedValue = SimpleHandlebarsView.prototype.normalizedValue; + + Ember.assert("registerBoundHelper-generated helpers do not support use with Handlebars blocks.", !options.fn); + + // Detect bound options (e.g. countBinding="otherCount") + var boundOptions = hash.boundOptions = {}; + for (hashOption in hash) { + if (IS_BINDING.test(hashOption)) { + // Lop off 'Binding' suffix. + boundOptions[hashOption.slice(0, -7)] = hash[hashOption]; + } + } + + // Expose property names on data.properties object. + var watchedProperties = []; + data.properties = []; + for (loc = 0; loc < numProperties; ++loc) { + data.properties.push(properties[loc]); + if (types[loc] === 'ID') { + var normalizedProp = normalizePath(currentContext, properties[loc], data); + normalizedProperties.push(normalizedProp); + watchedProperties.push(normalizedProp); + } else { + if(data.isUnbound) { + normalizedProperties.push({path: properties[loc]}); + }else { + normalizedProperties.push(null); + } + } + } - Options are: + // Handle case when helper invocation is preceded by `unbound`, e.g. + // {{unbound myHelper foo}} + if (data.isUnbound) { + return evaluateUnboundHelper(this, fn, normalizedProperties, options); + } - * `enter`: the user pressed enter - * `keyPress`: the user pressed a key + var bindView = new SimpleHandlebarsView(null, null, !options.hash.unescaped, options.data); - @property onEvent - @type String - @default enter - */ - onEvent: 'enter', + // Override SimpleHandlebarsView's method for generating the view's content. + bindView.normalizedValue = function() { + var args = [], boundOption; - /** - Whether the `keyUp` event that triggers an `action` to be sent continues - propagating to other views. + // Copy over bound hash options. + for (boundOption in boundOptions) { + if (!boundOptions.hasOwnProperty(boundOption)) { continue; } + property = normalizePath(currentContext, boundOptions[boundOption], data); + bindView.path = property.path; + bindView.pathRoot = property.root; + hash[boundOption] = normalizedValue.call(bindView); + } - By default, when the user presses the return key on their keyboard and - the text field has an `action` set, the action will be sent to the view's - controller and the key event will stop propagating. + for (loc = 0; loc < numProperties; ++loc) { + property = normalizedProperties[loc]; + if (property) { + bindView.path = property.path; + bindView.pathRoot = property.root; + args.push(normalizedValue.call(bindView)); + } else { + args.push(properties[loc]); + } + } + args.push(options); - If you would like parent views to receive the `keyUp` event even after an - action has been dispatched, set `bubbles` to true. + // Run the supplied helper function. + return fn.apply(currentContext, args); + }; - @property bubbles - @type Boolean - @default false - */ - bubbles: false, + view.appendChild(bindView); - interpretKeyEvents: function(event) { - var map = TextSupport.KEY_EVENTS; - var method = map[event.keyCode]; + // Assemble list of watched properties that'll re-render this helper. + for (boundOption in boundOptions) { + if (boundOptions.hasOwnProperty(boundOption)) { + watchedProperties.push(normalizePath(currentContext, boundOptions[boundOption], data)); + } + } - this._elementValueDidChange(); - if (method) { return this[method](event); } - }, + // Observe each property. + for (loc = 0, len = watchedProperties.length; loc < len; ++loc) { + property = watchedProperties[loc]; + view.registerObserver(property.root, property.path, bindView, bindView.rerender); + } - _elementValueDidChange: function() { - set(this, 'value', this.$().val()); - }, + if (types[0] !== 'ID' || normalizedProperties.length === 0) { + return; + } + + // Add dependent key observers to the first param + var normalized = normalizedProperties[0], + pathRoot = normalized.root, + path = normalized.path; + + if(!isEmpty(path)) { + prefixPathForDependentKeys = path + '.'; + } + for (var i=0, l=dependentKeys.length; i 1) { - className = split[1]; - if (split.length === 3) { - falsyClassName = split[2]; - } + function _triageMustacheHelper(property, options) { + Ember.assert("You cannot pass more than one argument to the _triageMustache helper", arguments.length <= 2); - classNames = ':' + className; - if (falsyClassName) { - classNames += ":" + falsyClassName; - } + var helper = EmberHandlebars.resolveHelper(options.data.view.container, property); + if (helper) { + return helper.call(this, options); } - return { - path: propertyPath, - classNames: classNames, - className: (className === '') ? undefined : className, - falsyClassName: falsyClassName - }; + return helpers.bind.call(this, property, options); } - __exports__.parsePropertyPath = parsePropertyPath;/** - Get the class name for a given value, based on the path, optional - `className` and optional `falsyClassName`. - - - if a `className` or `falsyClassName` has been specified: - - if the value is truthy and `className` has been specified, - `className` is returned - - if the value is falsy and `falsyClassName` has been specified, - `falsyClassName` is returned - - otherwise `null` is returned - - if the value is `true`, the dasherized last part of the supplied path - is returned - - if the value is not `false`, `undefined` or `null`, the `value` - is returned - - if none of the above rules apply, `null` is returned - - @method classStringForValue - @param path - @param val - @param className - @param falsyClassName - @static + /** + Used to lookup/resolve handlebars helpers. The lookup order is: + + * Look for a registered helper + * If a dash exists in the name: + * Look for a helper registed in the container + * Use Ember.ComponentLookup to find an Ember.Component that resolves + to the given name + @private + @method resolveHelper + @param {Container} container + @param {String} name the name of the helper to lookup + @return {Handlebars Helper} */ - function classStringForValue(path, val, className, falsyClassName) { - if(isArray(val)) { - val = get(val, 'length') !== 0; + function resolveHelper(container, name) { + if (helpers[name]) { + return helpers[name]; } - // When using the colon syntax, evaluate the truthiness or falsiness - // of the value to determine which className to return - if (className || falsyClassName) { - if (className && !!val) { - return className; + if (!container || name.indexOf('-') === -1) { + return; + } - } else if (falsyClassName && !val) { - return falsyClassName; + var helper = container.lookup('helper:' + name); + if (!helper) { + var componentLookup = container.lookup('component-lookup:main'); + Ember.assert("Could not find 'component-lookup:main' on the provided container, which is necessary for performing component lookups", componentLookup); - } else { - return null; + var Component = componentLookup.lookupFactory(name, container); + if (Component) { + helper = EmberHandlebars.makeViewHelper(Component); + container.register('helper:' + name, helper); } - - // If value is a Boolean and true, return the dasherized property - // name. - } else if (val === true) { - // Normalize property path to be suitable for use - // as a class name. For exaple, content.foo.barBaz - // becomes bar-baz. - var parts = path.split('.'); - return dasherize(parts[parts.length-1]); - - // If the value is not false, undefined, or null, return the current - // value of the property. - } else if (val !== false && val != null) { - return val; - - // Nothing to display. Return null so that the old class is removed - // but no new class is added. - } else { - return null; } + return helper; } - __exports__.classStringForValue = classStringForValue;function streamifyClassNameBinding(view, classNameBinding, prefix){ - prefix = prefix || ''; - Ember.assert("classNameBindings must not have spaces in them. Multiple class name bindings can be provided as elements of an array, e.g. ['foo', ':bar']", classNameBinding.indexOf(' ') === -1); - var parsedPath = parsePropertyPath(classNameBinding); - if (parsedPath.path === '') { - return classStringForValue( - parsedPath.path, - true, - parsedPath.className, - parsedPath.falsyClassName - ); - } else { - var pathValue = view.getStream(prefix+parsedPath.path); - return chainStream(pathValue, function(){ - return classStringForValue( - parsedPath.path, - read(pathValue), - parsedPath.className, - parsedPath.falsyClassName - ); - }); - } - } - __exports__.streamifyClassNameBinding = streamifyClassNameBinding; - }); -enifed("ember-views/streams/conditional_stream", - ["ember-metal/streams/stream","ember-metal/streams/utils","ember-metal/platform","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var Stream = __dependency1__["default"]; - var read = __dependency2__.read; - var subscribe = __dependency2__.subscribe; - var unsubscribe = __dependency2__.unsubscribe; - var create = __dependency3__.create; + /** + `bind` can be used to display a value, then update that value if it + changes. For example, if you wanted to print the `title` property of + `content`: - function ConditionalStream(test, consequent, alternate) { - this.init(); + ```handlebars + {{bind "content.title"}} + ``` - this.oldTestResult = undefined; - this.test = test; - this.consequent = consequent; - this.alternate = alternate; - } + This will return the `title` property as a string, then create a new observer + at the specified path. If it changes, it will update the value in DOM. Note + that if you need to support IE7 and IE8 you must modify the model objects + properties using `Ember.get()` and `Ember.set()` for this to work as it + relies on Ember's KVO system. For all other browsers this will be handled for + you automatically. - ConditionalStream.prototype = create(Stream.prototype); + @private + @method bind + @for Ember.Handlebars.helpers + @param {String} property Property to bind + @param {Function} fn Context to provide for rendering + @return {String} HTML string + */ + function bindHelper(property, options) { + Ember.assert("You cannot pass more than one argument to the bind helper", arguments.length <= 2); - ConditionalStream.prototype.valueFn = function() { - var oldTestResult = this.oldTestResult; - var newTestResult = !!read(this.test); + var context = (options.contexts && options.contexts.length) ? options.contexts[0] : this; - if (newTestResult !== oldTestResult) { - switch (oldTestResult) { - case true: unsubscribe(this.consequent, this.notify, this); break; - case false: unsubscribe(this.alternate, this.notify, this); break; - case undefined: subscribe(this.test, this.notify, this); - } + if (!options.fn) { + return simpleBind(context, property, options); + } - switch (newTestResult) { - case true: subscribe(this.consequent, this.notify, this); break; - case false: subscribe(this.alternate, this.notify, this); - } + options.helperName = 'bind'; - this.oldTestResult = newTestResult; - } + return bind.call(context, property, options, false, exists); + } - return newTestResult ? read(this.consequent) : read(this.alternate); - }; + /** + Use the `boundIf` helper to create a conditional that re-evaluates + whenever the truthiness of the bound value changes. - __exports__["default"] = ConditionalStream; - }); -enifed("ember-views/streams/context_stream", - ["ember-metal/core","ember-metal/merge","ember-metal/platform","ember-metal/path_cache","ember-metal/streams/stream","ember-metal/streams/simple","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; + ```handlebars + {{#boundIf "content.shouldDisplayTitle"}} + {{content.title}} + {{/boundIf}} + ``` - var merge = __dependency2__["default"]; - var create = __dependency3__.create; - var isGlobal = __dependency4__.isGlobal; - var Stream = __dependency5__["default"]; - var SimpleStream = __dependency6__["default"]; + @private + @method boundIf + @for Ember.Handlebars.helpers + @param {String} property Property to bind + @param {Function} fn Context to provide for rendering + @return {String} HTML string + */ + function boundIfHelper(property, fn) { + var context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : this; - function ContextStream(view) { - Ember.assert("ContextStream error: the argument is not a view", view && view.isView); + fn.helperName = fn.helperName || 'boundIf'; - this.init(); - this.view = view; + return bind.call(context, property, fn, true, shouldDisplayIfHelperContent, shouldDisplayIfHelperContent, ['isTruthy', 'length']); } - ContextStream.prototype = create(Stream.prototype); - merge(ContextStream.prototype, { - value: function() {}, + /** + @private - _makeChildStream: function(key, _fullPath) { - var stream; + Use the `unboundIf` helper to create a conditional that evaluates once. - if (key === '' || key === 'this') { - stream = this.view._baseContext; - } else if (isGlobal(key) && Ember.lookup[key]) { - Ember.deprecate("Global lookup of " + _fullPath + " from a Handlebars template is deprecated."); - stream = new SimpleStream(Ember.lookup[key]); - stream._isGlobal = true; - } else if (key in this.view._keywords) { - stream = new SimpleStream(this.view._keywords[key]); - } else { - stream = new SimpleStream(this.view._baseContext.get(key)); - } + ```handlebars + {{#unboundIf "content.shouldDisplayTitle"}} + {{content.title}} + {{/unboundIf}} + ``` - stream._isRoot = true; + @method unboundIf + @for Ember.Handlebars.helpers + @param {String} property Property to bind + @param {Function} fn Context to provide for rendering + @return {String} HTML string + @since 1.4.0 + */ + function unboundIfHelper(property, fn) { + var context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : this, + data = fn.data, + template = fn.fn, + inverse = fn.inverse, + normalized, propertyValue, result; - if (key === 'controller') { - stream._isController = true; - } + normalized = normalizePath(context, property, data); + propertyValue = handlebarsGet(context, property, fn); - return stream; + if (!shouldDisplayIfHelperContent(propertyValue)) { + template = inverse; } - }); - __exports__["default"] = ContextStream; - }); -enifed("ember-views/streams/key_stream", - ["ember-metal/core","ember-metal/merge","ember-metal/platform","ember-metal/property_get","ember-metal/property_set","ember-metal/observer","ember-metal/streams/stream","ember-metal/streams/utils","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; + template(context, { data: data }); + } - var merge = __dependency2__["default"]; - var create = __dependency3__.create; - var get = __dependency4__.get; - var set = __dependency5__.set; - var addObserver = __dependency6__.addObserver; - var removeObserver = __dependency6__.removeObserver; - var Stream = __dependency7__["default"]; - var read = __dependency8__.read; - var isStream = __dependency8__.isStream; + /** + Use the `{{with}}` helper when you want to scope context. Take the following code as an example: - function KeyStream(source, key) { - Ember.assert("KeyStream error: key must be a non-empty string", typeof key === 'string' && key.length > 0); - Ember.assert("KeyStream error: key must not have a '.'", key.indexOf('.') === -1); + ```handlebars +
{{user.name}}
- this.init(); - this.source = source; - this.obj = undefined; - this.key = key; +
+
{{user.role.label}}
+ {{user.role.id}} - if (isStream(source)) { - source.subscribe(this._didChange, this); - } - } +

{{user.role.description}}

+
+ ``` - KeyStream.prototype = create(Stream.prototype); + `{{with}}` can be our best friend in these cases, + instead of writing `user.role.*` over and over, we use `{{#with user.role}}`. + Now the context within the `{{#with}} .. {{/with}}` block is `user.role` so you can do the following: - merge(KeyStream.prototype, { - valueFn: function() { - var prevObj = this.obj; - var nextObj = read(this.source); + ```handlebars +
{{user.name}}
- if (nextObj !== prevObj) { - if (prevObj && typeof prevObj === 'object') { - removeObserver(prevObj, this.key, this, this._didChange); - } +
+ {{#with user.role}} +
{{label}}
+ {{id}} - if (nextObj && typeof nextObj === 'object') { - addObserver(nextObj, this.key, this, this._didChange); - } +

{{description}}

+ {{/with}} +
+ ``` - this.obj = nextObj; - } + ### `as` operator - if (nextObj) { - return get(nextObj, this.key); - } - }, + This operator aliases the scope to a new name. It's helpful for semantic clarity and to retain + default scope or to reference from another `{{with}}` block. - setValue: function(value) { - if (this.obj) { - set(this.obj, this.key, value); - } - }, + ```handlebars + // posts might not be + {{#with user.posts as blogPosts}} +
+ There are {{blogPosts.length}} blog posts written by {{user.name}}. +
- setSource: function(nextSource) { - Ember.assert("KeyStream error: source must be an object", typeof nextSource === 'object'); + {{#each post in blogPosts}} +
  • {{post.title}}
  • + {{/each}} + {{/with}} + ``` - var prevSource = this.source; + Without the `as` operator, it would be impossible to reference `user.name` in the example above. - if (nextSource !== prevSource) { - if (isStream(prevSource)) { - prevSource.unsubscribe(this._didChange, this); - } + NOTE: The alias should not reuse a name from the bound property path. + For example: `{{#with foo.bar as foo}}` is not supported because it attempts to alias using + the first part of the property path, `foo`. Instead, use `{{#with foo.bar as baz}}`. - if (isStream(nextSource)) { - nextSource.subscribe(this._didChange, this); - } + ### `controller` option - this.source = nextSource; - this.notify(); - } - }, + Adding `controller='something'` instructs the `{{with}}` helper to create and use an instance of + the specified controller with the new context as its content. - _didChange: function() { - this.notify(); - }, + This is very similar to using an `itemController` option with the `{{each}}` helper. - _super$destroy: Stream.prototype.destroy, + ```handlebars + {{#with users.posts controller='userBlogPosts'}} + {{!- The current context is wrapped in our controller instance }} + {{/with}} + ``` - destroy: function() { - if (this._super$destroy()) { - if (isStream(this.source)) { - this.source.unsubscribe(this._didChange, this); - } + In the above example, the template provided to the `{{with}}` block is now wrapped in the + `userBlogPost` controller, which provides a very elegant way to decorate the context with custom + functions/properties. - if (this.obj && typeof this.obj === 'object') { - removeObserver(this.obj, this.key, this, this._didChange); - } + @method with + @for Ember.Handlebars.helpers + @param {Function} context + @param {Hash} options + @return {String} HTML string + */ + function withHelper(context, options) { + var bindContext, preserveContext, controller, helperName = 'with'; - this.source = undefined; - this.obj = undefined; - return true; + if (arguments.length === 4) { + var keywordName, path, rootPath, normalized, contextPath; + + Ember.assert("If you pass more than one argument to the with helper, it must be in the form #with foo as bar", arguments[1] === "as"); + options = arguments[3]; + keywordName = arguments[2]; + path = arguments[0]; + + if (path) { + helperName += ' ' + path + ' as ' + keywordName; } - } - }); - __exports__["default"] = KeyStream; + Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop); - // The transpiler does not resolve cycles, so we export - // the `_makeChildStream` method onto `Stream` here. + var localizedOptions = o_create(options); + localizedOptions.data = o_create(options.data); + localizedOptions.data.keywords = o_create(options.data.keywords || {}); - Stream.prototype._makeChildStream = function(key) { - return new KeyStream(this, key); - }; - }); -enifed("ember-views/streams/utils", - ["ember-metal/core","ember-metal/property_get","ember-metal/path_cache","ember-runtime/system/string","ember-metal/streams/utils","ember-views/views/view","ember-runtime/mixins/controller","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - var get = __dependency2__.get; - var isGlobal = __dependency3__.isGlobal; - var fmt = __dependency4__.fmt; - var read = __dependency5__.read; - var isStream = __dependency5__.isStream; - var View = __dependency6__["default"]; - var ControllerMixin = __dependency7__["default"]; - - function readViewFactory(object, container) { - var value = read(object); - var viewClass; - - if (typeof value === 'string') { - if (isGlobal(value)) { - viewClass = get(null, value); - Ember.deprecate('Resolved the view "'+value+'" on the global context. Pass a view name to be looked up on the container instead, such as {{view "select"}}. http://emberjs.com/guides/deprecations#toc_global-lookup-of-views', !viewClass); + if (isGlobalPath(path)) { + contextPath = path; } else { - Ember.assert("View requires a container to resolve views not passed in through the context", !!container); - viewClass = container.lookupFactory('view:'+value); + normalized = normalizePath(this, path, options.data); + path = normalized.path; + rootPath = normalized.root; + + // This is a workaround for the fact that you cannot bind separate objects + // together. When we implement that functionality, we should use it here. + var contextKey = jQuery.expando + guidFor(rootPath); + localizedOptions.data.keywords[contextKey] = rootPath; + // if the path is '' ("this"), just bind directly to the current context + contextPath = path ? contextKey + '.' + path : contextKey; } + + localizedOptions.hash.keywordName = keywordName; + localizedOptions.hash.keywordPath = contextPath; + + bindContext = this; + context = path; + options = localizedOptions; + preserveContext = true; } else { - viewClass = value; + Ember.assert("You must pass exactly one argument to the with helper", arguments.length === 2); + Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop); + + helperName += ' ' + context; + bindContext = options.contexts[0]; + preserveContext = false; } - Ember.assert(fmt(value+" must be a subclass or an instance of Ember.View, not %@", [viewClass]), View.detect(viewClass) || View.detectInstance(viewClass)); + options.helperName = helperName; + options.isWithHelper = true; - return viewClass; + return bind.call(bindContext, context, options, preserveContext, exists); } + /** + See [boundIf](/api/classes/Ember.Handlebars.helpers.html#method_boundIf) + and [unboundIf](/api/classes/Ember.Handlebars.helpers.html#method_unboundIf) - __exports__.readViewFactory = readViewFactory;function readUnwrappedModel(object) { - if (isStream(object)) { - var result = object.value(); + @method if + @for Ember.Handlebars.helpers + @param {Function} context + @param {Hash} options + @return {String} HTML string + */ + function ifHelper(context, options) { + Ember.assert("You must pass exactly one argument to the if helper", arguments.length === 2); + Ember.assert("You must pass a block to the if helper", options.fn && options.fn !== Handlebars.VM.noop); - // If the path is exactly `controller` then we don't unwrap it. - if (!object._isController) { - while (ControllerMixin.detect(result)) { - result = get(result, 'model'); - } - } + options.helperName = options.helperName || ('if ' + context); - return result; + if (options.data.isUnbound) { + return helpers.unboundIf.call(options.contexts[0], context, options); } else { - return object; + return helpers.boundIf.call(options.contexts[0], context, options); } } - __exports__.readUnwrappedModel = readUnwrappedModel; - }); -enifed("ember-views/system/action_manager", - ["exports"], - function(__exports__) { - "use strict"; /** - @module ember - @submodule ember-views + @method unless + @for Ember.Handlebars.helpers + @param {Function} context + @param {Hash} options + @return {String} HTML string */ + function unlessHelper(context, options) { + Ember.assert("You must pass exactly one argument to the unless helper", arguments.length === 2); + Ember.assert("You must pass a block to the unless helper", options.fn && options.fn !== Handlebars.VM.noop); - function ActionManager() {} + var fn = options.fn, inverse = options.inverse, helperName = 'unless'; - /** - Global action id hash. - - @private - @property registeredActions - @type Object - */ - ActionManager.registeredActions = {}; + if (context) { + helperName += ' ' + context; + } - __exports__["default"] = ActionManager; - }); -enifed("ember-views/system/event_dispatcher", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/is_none","ember-metal/run_loop","ember-metal/utils","ember-runtime/system/string","ember-runtime/system/object","ember-views/system/jquery","ember-views/system/action_manager","ember-views/views/view","ember-metal/merge","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-views - */ - var Ember = __dependency1__["default"]; - // Ember.assert + options.fn = inverse; + options.inverse = fn; - var get = __dependency2__.get; - var set = __dependency3__.set; - var isNone = __dependency4__["default"]; - var run = __dependency5__["default"]; - var typeOf = __dependency6__.typeOf; - var fmt = __dependency7__.fmt; - var EmberObject = __dependency8__["default"]; - var jQuery = __dependency9__["default"]; - var ActionManager = __dependency10__["default"]; - var View = __dependency11__["default"]; - var merge = __dependency12__["default"]; + options.helperName = options.helperName || helperName; - //ES6TODO: - // find a better way to do Ember.View.views without global state + if (options.data.isUnbound) { + return helpers.unboundIf.call(options.contexts[0], context, options); + } else { + return helpers.boundIf.call(options.contexts[0], context, options); + } + } /** - `Ember.EventDispatcher` handles delegating browser events to their - corresponding `Ember.Views.` For example, when you click on a view, - `Ember.EventDispatcher` ensures that that view's `mouseDown` method gets - called. - - @class EventDispatcher - @namespace Ember - @private - @extends Ember.Object - */ - __exports__["default"] = EmberObject.extend({ + `bind-attr` allows you to create a binding between DOM element attributes and + Ember objects. For example: - /** - The set of events names (and associated handler function names) to be setup - and dispatched by the `EventDispatcher`. Custom events can added to this list at setup - time, generally via the `Ember.Application.customEvents` hash. Only override this - default set to prevent the EventDispatcher from listening on some events all together. + ```handlebars + imageTitle + ``` - This set will be modified by `setup` to also include any events added at that time. + The above handlebars template will fill the ``'s `src` attribute will + the value of the property referenced with `"imageUrl"` and its `alt` + attribute with the value of the property referenced with `"imageTitle"`. - @property events - @type Object - */ - events: { - touchstart : 'touchStart', - touchmove : 'touchMove', - touchend : 'touchEnd', - touchcancel : 'touchCancel', - keydown : 'keyDown', - keyup : 'keyUp', - keypress : 'keyPress', - mousedown : 'mouseDown', - mouseup : 'mouseUp', - contextmenu : 'contextMenu', - click : 'click', - dblclick : 'doubleClick', - mousemove : 'mouseMove', - focusin : 'focusIn', - focusout : 'focusOut', - mouseenter : 'mouseEnter', - mouseleave : 'mouseLeave', - submit : 'submit', - input : 'input', - change : 'change', - dragstart : 'dragStart', - drag : 'drag', - dragenter : 'dragEnter', - dragleave : 'dragLeave', - dragover : 'dragOver', - drop : 'drop', - dragend : 'dragEnd' - }, + If the rendering context of this template is the following object: - /** - The root DOM element to which event listeners should be attached. Event - listeners will be attached to the document unless this is overridden. + ```javascript + { + imageUrl: 'http://lolcats.info/haz-a-funny', + imageTitle: 'A humorous image of a cat' + } + ``` - Can be specified as a DOMElement or a selector string. + The resulting HTML output will be: - The default body is a string since this may be evaluated before document.body - exists in the DOM. + ```html + A humorous image of a cat + ``` - @private - @property rootElement - @type DOMElement - @default 'body' - */ - rootElement: 'body', + `bind-attr` cannot redeclare existing DOM element attributes. The use of `src` + in the following `bind-attr` example will be ignored and the hard coded value + of `src="/failwhale.gif"` will take precedence: - /** - It enables events to be dispatched to the view's `eventManager.` When present, - this object takes precedence over handling of events on the view itself. + ```handlebars + imageTitle + ``` - Note that most Ember applications do not use this feature. If your app also - does not use it, consider setting this property to false to gain some performance - improvement by allowing the EventDispatcher to skip the search for the - `eventManager` on the view tree. + ### `bind-attr` and the `class` attribute - ```javascript - var EventDispatcher = Em.EventDispatcher.extend({ - events: { - click : 'click', - focusin : 'focusIn', - focusout : 'focusOut', - change : 'change' - }, - canDispatchToEventManager: false - }); - container.register('event_dispatcher:main', EventDispatcher); - ``` + `bind-attr` supports a special syntax for handling a number of cases unique + to the `class` DOM element attribute. The `class` attribute combines + multiple discrete values into a single attribute as a space-delimited + list of strings. Each string can be: - @property canDispatchToEventManager - @type boolean - @default 'true' - @since 1.7.0 - */ - canDispatchToEventManager: true, + * a string return value of an object's property. + * a boolean return value of an object's property + * a hard-coded value - /** - Sets up event listeners for standard browser events. + A string return value works identically to other uses of `bind-attr`. The + return value of the property will become the value of the attribute. For + example, the following view and template: - This will be called after the browser sends a `DOMContentReady` event. By - default, it will set up all of the listeners on the document body. If you - would like to register the listeners on a different element, set the event - dispatcher's `root` property. + ```javascript + AView = View.extend({ + someProperty: function() { + return "aValue"; + }.property() + }) + ``` - @private - @method setup - @param addedEvents {Hash} - */ - setup: function(addedEvents, rootElement) { - var event, events = get(this, 'events'); + ```handlebars + + ``` - rootElement = jQuery(get(this, 'rootElement')); + A boolean return value will insert a specified class name if the property + returns `true` and remove the class name if the property returns `false`. - Ember.assert(fmt('You cannot use the same root element (%@) multiple times in an Ember.Application', [rootElement.selector || rootElement[0].tagName]), !rootElement.is('.ember-application')); - Ember.assert('You cannot make a new Ember.Application using a root element that is a descendent of an existing Ember.Application', !rootElement.closest('.ember-application').length); - Ember.assert('You cannot make a new Ember.Application using a root element that is an ancestor of an existing Ember.Application', !rootElement.find('.ember-application').length); + A class name is provided via the syntax + `somePropertyName:class-name-if-true`. - rootElement.addClass('ember-application'); + ```javascript + AView = View.extend({ + someBool: true + }) + ``` - Ember.assert('Unable to add "ember-application" class to rootElement. Make sure you set rootElement to the body or an element in the body.', rootElement.is('.ember-application')); + ```handlebars + + ``` - for (event in events) { - if (events.hasOwnProperty(event)) { - this.setupHandler(rootElement, event, events[event]); - } - } - }, + Result in the following rendered output: - /** - Registers an event listener on the rootElement. If the given event is - triggered, the provided event handler will be triggered on the target view. + ```html + + ``` - If the target view does not implement the event handler, or if the handler - returns `false`, the parent view will be called. The event will continue to - bubble to each successive parent view until it reaches the top. + An additional section of the binding can be provided if you want to + replace the existing class instead of removing it when the boolean + value changes: - @private - @method setupHandler - @param {Element} rootElement - @param {String} event the browser-originated event to listen to - @param {String} eventName the name of the method to call on the view - */ - setupHandler: function(rootElement, event, eventName) { - var self = this; + ```handlebars + + ``` - rootElement.on(event + '.ember', '.ember-view', function(evt, triggeringManager) { - var view = View.views[this.id]; - var result = true; + A hard-coded value can be used by prepending `:` to the desired + class name: `:class-name-to-always-apply`. - var manager = self.canDispatchToEventManager ? self._findNearestEventManager(view, eventName) : null; + ```handlebars + + ``` - if (manager && manager !== triggeringManager) { - result = self._dispatchEvent(manager, evt, eventName, view); - } else if (view) { - result = self._bubbleEvent(view, evt, eventName); - } + Results in the following rendered output: - return result; - }); + ```html + + ``` - rootElement.on(event + '.ember', '[data-ember-action]', function(evt) { - var actionId = jQuery(evt.currentTarget).attr('data-ember-action'); - var action = ActionManager.registeredActions[actionId]; + All three strategies - string return value, boolean return value, and + hard-coded value – can be combined in a single declaration: - // We have to check for action here since in some cases, jQuery will trigger - // an event on `removeChild` (i.e. focusout) after we've already torn down the - // action handlers for the view. - if (action && action.eventName === eventName) { - return action.handler(evt); - } - }); - }, + ```handlebars + + ``` - _findNearestEventManager: function(view, eventName) { - var manager = null; + @method bind-attr + @for Ember.Handlebars.helpers + @param {Hash} options + @return {String} HTML string + */ + function bindAttrHelper(options) { + var attrs = options.hash; - while (view) { - manager = get(view, 'eventManager'); - if (manager && manager[eventName]) { break; } + Ember.assert("You must specify at least one hash argument to bind-attr", !!keys(attrs).length); - view = get(view, 'parentView'); - } + var view = options.data.view; + var ret = []; - return manager; - }, + // we relied on the behavior of calling without + // context to mean this === window, but when running + // "use strict", it's possible for this to === undefined; + var ctx = this || window; - _dispatchEvent: function(object, evt, eventName, view) { - var result = true; + // Generate a unique id for this element. This will be added as a + // data attribute to the element so it can be looked up when + // the bound property changes. + var dataId = ++Ember.uuid; - var handler = object[eventName]; - if (typeOf(handler) === 'function') { - result = run(object, handler, evt, view); - // Do not preventDefault in eventManagers. - evt.stopPropagation(); - } - else { - result = this._bubbleEvent(view, evt, eventName); - } + // Handle classes differently, as we can bind multiple classes + var classBindings = attrs['class']; + if (classBindings != null) { + var classResults = bindClasses(ctx, classBindings, view, dataId, options); - return result; - }, + ret.push('class="' + Handlebars.Utils.escapeExpression(classResults.join(' ')) + '"'); + delete attrs['class']; + } - _bubbleEvent: function(view, evt, eventName) { - return run.join(view, view.handleEvent, eventName, evt); - }, + var attrKeys = keys(attrs); - destroy: function() { - var rootElement = get(this, 'rootElement'); - jQuery(rootElement).off('.ember', '**').removeClass('ember-application'); - return this._super(); - }, + // For each attribute passed, create an observer and emit the + // current value of the property as an attribute. + forEach.call(attrKeys, function(attr) { + var path = attrs[attr], + normalized; - toString: function() { - return '(EventDispatcher)'; - } - }); - }); -enifed("ember-views/system/ext", - ["ember-metal/run_loop"], - function(__dependency1__) { - "use strict"; - /** - @module ember - @submodule ember-views - */ + Ember.assert(fmt("You must provide an expression as the value of bound attribute. You specified: %@=%@", [attr, path]), typeof path === 'string'); - var run = __dependency1__["default"]; + normalized = normalizePath(ctx, path, options.data); - // Add a new named queue for rendering views that happens - // after bindings have synced, and a queue for scheduling actions - // that that should occur after view rendering. - run._addQueue('render', 'actions'); - run._addQueue('afterRender', 'render'); - }); -enifed("ember-views/system/jquery", - ["ember-metal/core","ember-metal/enumerable_utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // Ember.assert + var value = (path === 'this') ? normalized.root : handlebarsGet(ctx, path, options), + type = typeOf(value); - // ES6TODO: the functions on EnumerableUtils need their own exports - var forEach = __dependency2__.forEach; + Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [value]), value === null || value === undefined || type === 'number' || type === 'string' || type === 'boolean'); - /** - Ember Views + var observer, invoker; - @module ember - @submodule ember-views - @requires ember-runtime - @main ember-views - */ + observer = function observer() { + var result = handlebarsGet(ctx, path, options); - var jQuery = (Ember.imports && Ember.imports.jQuery) || (this && this.jQuery); - if (!jQuery && typeof eriuqer === 'function') { - jQuery = eriuqer('jquery'); - } + Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [result]), + result === null || result === undefined || typeof result === 'number' || + typeof result === 'string' || typeof result === 'boolean'); - Ember.assert("Ember Views require jQuery between 1.7 and 2.1", jQuery && - (jQuery().jquery.match(/^((1\.(7|8|9|10|11))|(2\.(0|1)))(\.\d+)?(pre|rc\d?)?/) || - Ember.ENV.FORCE_JQUERY)); + var elem = view.$("[data-bindattr-" + dataId + "='" + dataId + "']"); - /** - @module ember - @submodule ember-views - */ - if (jQuery) { - // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#dndevents - var dragEvents = [ - 'dragstart', - 'drag', - 'dragenter', - 'dragleave', - 'dragover', - 'drop', - 'dragend' - ]; + // If we aren't able to find the element, it means the element + // to which we were bound has been removed from the view. + // In that case, we can assume the template has been re-rendered + // and we need to clean up the observer. + if (!elem || elem.length === 0) { + removeObserver(normalized.root, normalized.path, invoker); + return; + } - // Copies the `dataTransfer` property from a browser event object onto the - // jQuery event object for the specified events - forEach(dragEvents, function(eventName) { - jQuery.event.fixHooks[eventName] = { - props: ['dataTransfer'] + View.applyAttributeBindings(elem, attr, result); }; - }); + + // Add an observer to the view for when the property changes. + // When the observer fires, find the element using the + // unique data id and update the attribute to the new value. + // Note: don't add observer when path is 'this' or path + // is whole keyword e.g. {{#each x in list}} ... {{bind-attr attr="x"}} + if (path !== 'this' && !(normalized.isKeyword && normalized.path === '' )) { + view.registerObserver(normalized.root, normalized.path, observer); + } + + // if this changes, also change the logic in ember-views/lib/views/view.js + if ((type === 'string' || (type === 'number' && !isNaN(value)))) { + ret.push(attr + '="' + Handlebars.Utils.escapeExpression(value) + '"'); + } else if (value && type === 'boolean') { + // The developer controls the attr name, so it should always be safe + ret.push(attr + '="' + attr + '"'); + } + }, this); + + // Add the unique identifier + // NOTE: We use all lower-case since Firefox has problems with mixed case in SVG + ret.push('data-bindattr-' + dataId + '="' + dataId + '"'); + return new SafeString(ret.join(' ')); } - __exports__["default"] = jQuery; - }); -enifed("ember-views/system/render_buffer", - ["ember-views/system/jquery","morph","ember-metal/core","ember-metal/platform","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; /** - @module ember - @submodule ember-views - */ + See `bind-attr` - var jQuery = __dependency1__["default"]; - var DOMHelper = __dependency2__.DOMHelper; - var Ember = __dependency3__["default"]; - var create = __dependency4__.create; + @method bindAttr + @for Ember.Handlebars.helpers + @deprecated + @param {Function} context + @param {Hash} options + @return {String} HTML string + */ + function bindAttrHelperDeprecated() { + Ember.warn("The 'bindAttr' view helper is deprecated in favor of 'bind-attr'"); + return helpers['bind-attr'].apply(this, arguments); + } - // The HTML spec allows for "omitted start tags". These tags are optional - // when their intended child is the first thing in the parent tag. For - // example, this is a tbody start tag: - // - // - // - // - // - // The tbody may be omitted, and the browser will accept and render: - // - //
    - // - // - // However, the omitted start tag will still be added to the DOM. Here - // we test the string and context to see if the browser is about to - // perform this cleanup, but with a special allowance for disregarding - // + ``` - ## Layout and LayoutName properties + Take note that `"welcome"` is a string and not an object + reference. - Because HTML `input` elements are self closing `layout` and `layoutName` - properties will not be applied. See [Ember.View](/api/classes/Ember.View.html)'s - layout section for more information. + See [Ember.String.loc](/api/classes/Ember.String.html#method_loc) for how to + set up localized string references. - @class Checkbox - @namespace Ember - @extends Ember.View + @method loc + @for Ember.Handlebars.helpers + @param {String} str The string to format + @see {Ember.String#loc} */ - __exports__["default"] = View.extend({ - instrumentDisplay: '{{input type="checkbox"}}', - - classNames: ['ember-checkbox'], - - tagName: 'input', - - attributeBindings: [ - 'type', - 'checked', - 'indeterminate', - 'disabled', - 'tabindex', - 'name', - 'autofocus', - 'required', - 'form' - ], - - type: 'checkbox', - checked: false, - disabled: false, - indeterminate: false, - - init: function() { - this._super(); - this.on('change', this, this._updateElementValue); - }, - - didInsertElement: function() { - this._super(); - get(this, 'element').indeterminate = !!get(this, 'indeterminate'); - }, + function locHelper(str) { + return loc(str); + } - _updateElementValue: function() { - set(this, 'checked', this.$().prop('checked')); - } - }); + __exports__["default"] = locHelper; }); -enifed("ember-views/views/collection_view", - ["ember-metal/core","ember-metal/binding","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/string","ember-views/views/container_view","ember-views/views/core_view","ember-views/views/view","ember-metal/mixin","ember-views/streams/utils","ember-runtime/mixins/array","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { +define("ember-handlebars/helpers/partial", + ["ember-metal/core","ember-metal/is_none","ember-handlebars/ext","ember-handlebars/helpers/binding","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; + var Ember = __dependency1__["default"]; + // Ember.assert + // var emberAssert = Ember.assert; + + var isNone = __dependency2__.isNone; + var handlebarsGet = __dependency3__.handlebarsGet; + var bind = __dependency4__.bind; /** @module ember - @submodule ember-views + @submodule ember-handlebars */ - var Ember = __dependency1__["default"]; - // Ember.assert - var isGlobalPath = __dependency2__.isGlobalPath; - var get = __dependency3__.get; - var set = __dependency4__.set; - var fmt = __dependency5__.fmt; - var ContainerView = __dependency6__["default"]; - var CoreView = __dependency7__["default"]; - var View = __dependency8__["default"]; - var observer = __dependency9__.observer; - var beforeObserver = __dependency9__.beforeObserver; - var readViewFactory = __dependency10__.readViewFactory; - var EmberArray = __dependency11__["default"]; - /** - `Ember.CollectionView` is an `Ember.View` descendent responsible for managing - a collection (an array or array-like object) by maintaining a child view object - and associated DOM representation for each item in the array and ensuring - that child views and their associated rendered HTML are updated when items in - the array are added, removed, or replaced. - - ## Setting content - - The managed collection of objects is referenced as the `Ember.CollectionView` - instance's `content` property. + The `partial` helper renders another template without + changing the template context: - ```javascript - someItemsView = Ember.CollectionView.create({ - content: ['A', 'B','C'] - }) + ```handlebars + {{foo}} + {{partial "nav"}} ``` - The view for each item in the collection will have its `content` property set - to the item. + The above example template will render a template named + "_nav", which has the same context as the parent template + it's rendered into, so if the "_nav" template also referenced + `{{foo}}`, it would print the same thing as the `{{foo}}` + in the above example. - ## Specifying `itemViewClass` + If a "_nav" template isn't found, the `partial` helper will + fall back to a template named "nav". - By default the view class for each item in the managed collection will be an - instance of `Ember.View`. You can supply a different class by setting the - `CollectionView`'s `itemViewClass` property. + ## Bound template names - Given the following application code: + The parameter supplied to `partial` can also be a path + to a property containing a template name, e.g.: - ```javascript - var App = Ember.Application.create(); - App.ItemListView = Ember.CollectionView.extend({ - classNames: ['a-collection'], - content: ['A','B','C'], - itemViewClass: Ember.View.extend({ - template: Ember.Handlebars.compile("the letter: {{view.content}}") - }) - }); + ```handlebars + {{partial someTemplateName}} ``` - And a simple application template: + The above example will look up the value of `someTemplateName` + on the template context (e.g. a controller) and use that + value as the name of the template to render. If the resolved + value is falsy, nothing will be rendered. If `someTemplateName` + changes, the partial will be re-rendered using the new template + name. + + ## Setting the partial's context with `with` + + The `partial` helper can be used in conjunction with the `with` + helper to set a context that will be used by the partial: ```handlebars - {{view 'item-list'}} + {{#with currentUser}} + {{partial "user_info"}} + {{/with}} ``` - The following HTML will result: + @method partial + @for Ember.Handlebars.helpers + @param {String} partialName the name of the template to render minus the leading underscore + */ - ```html -
    -
    the letter: A
    -
    the letter: B
    -
    the letter: C
    -
    - ``` + function partialHelper(name, options) { - ## Automatic matching of parent/child tagNames + var context = (options.contexts && options.contexts.length) ? options.contexts[0] : this; - Setting the `tagName` property of a `CollectionView` to any of - "ul", "ol", "table", "thead", "tbody", "tfoot", "tr", or "select" will result - in the item views receiving an appropriately matched `tagName` property. + options.helperName = options.helperName || 'partial'; - Given the following application code: + if (options.types[0] === "ID") { + // Helper was passed a property path; we need to + // create a binding that will re-render whenever + // this property changes. + options.fn = function(context, fnOptions) { + var partialName = handlebarsGet(context, name, fnOptions); + renderPartial(context, partialName, fnOptions); + }; - ```javascript - var App = Ember.Application.create(); - App.UnorderedListView = Ember.CollectionView.create({ - tagName: 'ul', - content: ['A','B','C'], - itemViewClass: Ember.View.extend({ - template: Ember.Handlebars.compile("the letter: {{view.content}}") - }) - }); - ``` + return bind.call(context, name, options, true, exists); + } else { + // Render the partial right into parent template. + renderPartial(context, name, options); + } + } - And a simple application template: + function exists(value) { + return !isNone(value); + } - ```handlebars - {{view 'unordered-list-view'}} - ``` + function renderPartial(context, name, options) { + var nameParts = name.split("/"); + var lastPart = nameParts[nameParts.length - 1]; - The following HTML will result: + nameParts[nameParts.length - 1] = "_" + lastPart; - ```html -
      -
    • the letter: A
    • -
    • the letter: B
    • -
    • the letter: C
    • -
    - ``` + var view = options.data.view; + var underscoredName = nameParts.join("/"); + var template = view.templateForName(underscoredName); + var deprecatedTemplate = !template && view.templateForName(name); - Additional `tagName` pairs can be provided by adding to - `Ember.CollectionView.CONTAINER_MAP`. For example: + Ember.assert("Unable to find partial with name '"+name+"'.", template || deprecatedTemplate); - ```javascript - Ember.CollectionView.CONTAINER_MAP['article'] = 'section' - ``` + template = template || deprecatedTemplate; - ## Programmatic creation of child views + template(context, { data: options.data }); + } - For cases where additional customization beyond the use of a single - `itemViewClass` or `tagName` matching is required CollectionView's - `createChildView` method can be overidden: + __exports__["default"] = partialHelper; + }); +define("ember-handlebars/helpers/shared", + ["ember-handlebars/ext","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var handlebarsGet = __dependency1__.handlebarsGet; - ```javascript - App.CustomCollectionView = Ember.CollectionView.extend({ - createChildView: function(viewClass, attrs) { - if (attrs.content.kind == 'album') { - viewClass = App.AlbumView; - } else { - viewClass = App.SongView; - } - return this._super(viewClass, attrs); - } - }); - ``` + function resolvePaths(options) { + var ret = [], + contexts = options.contexts, + roots = options.roots, + data = options.data; - ## Empty View + for (var i=0, l=contexts.length; i + {{#with loggedInUser}} + Last Login: {{lastLogin}} + User Info: {{template "user_info"}} + {{/with}} + + ``` ```html -
    -
    - The collection is empty -
    -
    + ``` - ## Adding and Removing items + ```handlebars + {{#if isUser}} + {{template "user_info"}} + {{else}} + {{template "unlogged_user_info"}} + {{/if}} + ``` - The `childViews` property of a `CollectionView` should not be directly - manipulated. Instead, add, remove, replace items from its `content` property. - This will trigger appropriate changes to its rendered HTML. + This helper looks for templates in the global `Ember.TEMPLATES` hash. If you + add ` - ``` + __exports__["default"] = renderHelper; + }); +define("ember-routing/helpers/shared", + ["ember-metal/property_get","ember-metal/array","ember-runtime/system/lazy_load","ember-runtime/controllers/controller","ember-routing/system/router","ember-handlebars/ext","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { + "use strict"; + var get = __dependency1__.get; + var map = __dependency2__.map; + var onLoad = __dependency3__.onLoad; + var ControllerMixin = __dependency4__.ControllerMixin; + var EmberRouter = __dependency5__["default"]; + var handlebarsResolve = __dependency6__.resolveParams; + var handlebarsGet = __dependency6__.handlebarsGet; + + function resolveParams(context, params, options) { + return map.call(resolvePaths(context, params, options), function(path, i) { + if (null === path) { + // Param was string/number, not a path, so just return raw string/number. + return params[i]; + } else { + return handlebarsGet(context, path, options); + } + }); + } - And associate it by name using a view's `templateName` property: + function resolvePaths(context, params, options) { + var resolved = handlebarsResolve(context, params, options), + types = options.types; - ```javascript - AView = Ember.View.extend({ - templateName: 'some-template' + return map.call(resolved, function(object, i) { + if (types[i] === 'ID') { + return unwrap(object, params[i]); + } else { + return null; + } }); - ``` - If you have nested resources, your Handlebars template will look like this: + function unwrap(object, path) { + if (path === 'controller') { return path; } - ```html - - ``` + if (ControllerMixin.detect(object)) { + return unwrap(get(object, 'model'), path ? path + '.model' : 'model'); + } else { + return path; + } + } + } - And `templateName` property: + __exports__.resolveParams = resolveParams; + __exports__.resolvePaths = resolvePaths; + }); +define("ember-routing/location/api", + ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // deprecate, assert + var get = __dependency2__.get; + var set = __dependency3__.set; - ```javascript - AView = Ember.View.extend({ - templateName: 'posts/new' - }); - ``` + /** + @module ember + @submodule ember-routing + */ - Using a value for `templateName` that does not have a Handlebars template - with a matching `data-template-name` attribute will throw an error. + /** + Ember.Location returns an instance of the correct implementation of + the `location` API. - For views classes that may have a template later defined (e.g. as the block - portion of a `{{view}}` Handlebars helper call in another template or in - a subclass), you can provide a `defaultTemplate` property set to compiled - template function. If a template is not later provided for the view instance - the `defaultTemplate` value will be used: + ## Implementations - ```javascript - AView = Ember.View.extend({ - defaultTemplate: Ember.Handlebars.compile('I was the default'), - template: null, - templateName: null - }); - ``` + You can pass an implementation name (`hash`, `history`, `none`) to force a + particular implementation to be used in your application. - Will result in instances with an HTML representation of: + ### HashLocation - ```html -
    I was the default
    - ``` + Using `HashLocation` results in URLs with a `#` (hash sign) separating the + server side URL portion of the URL from the portion that is used by Ember. + This relies upon the `hashchange` event existing in the browser. - If a `template` or `templateName` is provided it will take precedence over - `defaultTemplate`: + Example: ```javascript - AView = Ember.View.extend({ - defaultTemplate: Ember.Handlebars.compile('I was the default') + App.Router.map(function() { + this.resource('posts', function() { + this.route('new'); + }); }); - aView = AView.create({ - template: Ember.Handlebars.compile('I was the template, not default') + App.Router.reopen({ + location: 'hash' }); ``` - Will result in the following HTML representation when rendered: + This will result in a posts.new url of `/#/posts/new`. - ```html -
    I was the template, not default
    - ``` + ### HistoryLocation - ## View Context + Using `HistoryLocation` results in URLs that are indistinguishable from a + standard URL. This relies upon the browser's `history` API. - The default context of the compiled template is the view's controller: + Example: ```javascript - AView = Ember.View.extend({ - template: Ember.Handlebars.compile('Hello {{excitedGreeting}}') - }); - - aController = Ember.Object.create({ - firstName: 'Barry', - excitedGreeting: function() { - return this.get("content.firstName") + "!!!" - }.property() + App.Router.map(function() { + this.resource('posts', function() { + this.route('new'); + }); }); - aView = AView.create({ - controller: aController + App.Router.reopen({ + location: 'history' }); ``` - Will result in an HTML representation of: - - ```html -
    Hello Barry!!!
    - ``` - - A context can also be explicitly supplied through the view's `context` - property. If the view has neither `context` nor `controller` properties, the - `parentView`'s context will be used. + This will result in a posts.new url of `/posts/new`. - ## Layouts + Keep in mind that your server must serve the Ember app at all the routes you + define. - Views can have a secondary template that wraps their main template. Like - primary templates, layouts can be any function that accepts an optional - context parameter and returns a string of HTML that will be inserted inside - view's tag. Views whose HTML element is self closing (e.g. ``) - cannot have a layout and this property will be ignored. + ### AutoLocation - Most typically in Ember a layout will be a compiled `Ember.Handlebars` - template. + Using `AutoLocation`, the router will use the best Location class supported by + the browser it is running in. - A view's layout can be set directly with the `layout` property or reference - an existing Handlebars template by name with the `layoutName` property. + Browsers that support the `history` API will use `HistoryLocation`, those that + do not, but still support the `hashchange` event will use `HashLocation`, and + in the rare case neither is supported will use `NoneLocation`. - A template used as a layout must contain a single use of the Handlebars - `{{yield}}` helper. The HTML contents of a view's rendered `template` will be - inserted at this location: + Example: ```javascript - AViewWithLayout = Ember.View.extend({ - layout: Ember.Handlebars.compile("
    {{yield}}
    "), - template: Ember.Handlebars.compile("I got wrapped") + App.Router.map(function() { + this.resource('posts', function() { + this.route('new'); + }); + }); + + App.Router.reopen({ + location: 'auto' }); ``` - Will result in view instances with an HTML representation of: + This will result in a posts.new url of `/posts/new` for modern browsers that + support the `history` api or `/#/posts/new` for older ones, like Internet + Explorer 9 and below. - ```html -
    -
    - I got wrapped -
    -
    - ``` + When a user visits a link to your application, they will be automatically + upgraded or downgraded to the appropriate `Location` class, with the URL + transformed accordingly, if needed. - See [Ember.Handlebars.helpers.yield](/api/classes/Ember.Handlebars.helpers.html#method_yield) - for more information. + Keep in mind that since some of your users will use `HistoryLocation`, your + server must serve the Ember app at all the routes you define. - ## Responding to Browser Events + ### NoneLocation - Views can respond to user-initiated events in one of three ways: method - implementation, through an event manager, and through `{{action}}` helper use - in their template or layout. + Using `NoneLocation` causes Ember to not store the applications URL state + in the actual URL. This is generally used for testing purposes, and is one + of the changes made when calling `App.setupForTesting()`. - ### Method Implementation + ## Location API - Views can respond to user-initiated events by implementing a method that - matches the event name. A `jQuery.Event` object will be passed as the - argument to this method. + Each location implementation must provide the following methods: - ```javascript - AView = Ember.View.extend({ - click: function(event) { - // will be called when when an instance's - // rendered element is clicked - } - }); - ``` + * implementation: returns the string name used to reference the implementation. + * getURL: returns the current URL. + * setURL(path): sets the current URL. + * replaceURL(path): replace the current URL (optional). + * onUpdateURL(callback): triggers the callback when the URL changes. + * formatURL(url): formats `url` to be placed into `href` attribute. - ### Event Managers + Calling setURL or replaceURL will not trigger onUpdateURL callbacks. - Views can define an object as their `eventManager` property. This object can - then implement methods that match the desired event names. Matching events - that occur on the view's rendered HTML or the rendered HTML of any of its DOM - descendants will trigger this method. A `jQuery.Event` object will be passed - as the first argument to the method and an `Ember.View` object as the - second. The `Ember.View` will be the view whose rendered HTML was interacted - with. This may be the view with the `eventManager` property or one of its - descendent views. + @class Location + @namespace Ember + @static + */ + var EmberLocation = { + /** + This is deprecated in favor of using the container to lookup the location + implementation as desired. - ```javascript - AView = Ember.View.extend({ - eventManager: Ember.Object.create({ - doubleClick: function(event, view) { - // will be called when when an instance's - // rendered element or any rendering - // of this views's descendent - // elements is clicked - } - }) - }); - ``` + For example: - An event defined for an event manager takes precedence over events of the - same name handled through methods on the view. + ```javascript + // Given a location registered as follows: + container.register('location:history-test', HistoryTestLocation); - ```javascript - AView = Ember.View.extend({ - mouseEnter: function(event) { - // will never trigger. - }, - eventManager: Ember.Object.create({ - mouseEnter: function(event, view) { - // takes precedence over AView#mouseEnter - } - }) - }); - ``` + // You could create a new instance via: + container.lookup('location:history-test'); + ``` - Similarly a view's event manager will take precedence for events of any views - rendered as a descendent. A method name that matches an event name will not - be called if the view instance was rendered inside the HTML representation of - a view that has an `eventManager` property defined that handles events of the - name. Events not handled by the event manager will still trigger method calls - on the descendent. + @method create + @param {Object} options + @return {Object} an instance of an implementation of the `location` API + @deprecated Use the container to lookup the location implementation that you + need. + */ + create: function(options) { + var implementation = options && options.implementation; + Ember.assert("Ember.Location.create: you must specify a 'implementation' option", !!implementation); - ```javascript - var App = Ember.Application.create(); - App.OuterView = Ember.View.extend({ - template: Ember.Handlebars.compile("outer {{#view 'inner'}}inner{{/view}} outer"), - eventManager: Ember.Object.create({ - mouseEnter: function(event, view) { - // view might be instance of either - // OuterView or InnerView depending on - // where on the page the user interaction occurred - } - }) - }); + var implementationClass = this.implementations[implementation]; + Ember.assert("Ember.Location.create: " + implementation + " is not a valid implementation", !!implementationClass); - App.InnerView = Ember.View.extend({ - click: function(event) { - // will be called if rendered inside - // an OuterView because OuterView's - // eventManager doesn't handle click events - }, - mouseEnter: function(event) { - // will never be called if rendered inside - // an OuterView. - } - }); - ``` + return implementationClass.create.apply(implementationClass, arguments); + }, - ### Handlebars `{{action}}` Helper + /** + This is deprecated in favor of using the container to register the + location implementation as desired. - See [Handlebars.helpers.action](/api/classes/Ember.Handlebars.helpers.html#method_action). + Example: - ### Event Names + ```javascript + Application.initializer({ + name: "history-test-location", - All of the event handling approaches described above respond to the same set - of events. The names of the built-in events are listed below. (The hash of - built-in events exists in `Ember.EventDispatcher`.) Additional, custom events - can be registered by using `Ember.Application.customEvents`. + initialize: function(container, application) { + application.register('location:history-test', HistoryTestLocation); + } + }); + ``` - Touch events: + @method registerImplementation + @param {String} name + @param {Object} implementation of the `location` API + @deprecated Register your custom location implementation with the + container directly. + */ + registerImplementation: function(name, implementation) { + Ember.deprecate('Using the Ember.Location.registerImplementation is no longer supported. Register your custom location implementation with the container instead.', false); - * `touchStart` - * `touchMove` - * `touchEnd` - * `touchCancel` + this.implementations[name] = implementation; + }, - Keyboard events + implementations: {}, + _location: window.location, - * `keyDown` - * `keyUp` - * `keyPress` + /** + Returns the current `location.hash` by parsing location.href since browsers + inconsistently URL-decode `location.hash`. - Mouse events + https://bugzilla.mozilla.org/show_bug.cgi?id=483304 - * `mouseDown` - * `mouseUp` - * `contextMenu` - * `click` - * `doubleClick` - * `mouseMove` - * `focusIn` - * `focusOut` - * `mouseEnter` - * `mouseLeave` + @private + @method getHash + @since 1.4.0 + */ + _getHash: function () { + // AutoLocation has it at _location, HashLocation at .location. + // Being nice and not changing + var href = (this._location || this.location).href, + hashIndex = href.indexOf('#'); - Form events: + if (hashIndex === -1) { + return ''; + } else { + return href.substr(hashIndex); + } + } + }; - * `submit` - * `change` - * `focusIn` - * `focusOut` - * `input` + __exports__["default"] = EmberLocation; + }); +define("ember-routing/location/auto_location", + ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-routing/location/api","ember-routing/location/history_location","ember-routing/location/hash_location","ember-routing/location/none_location","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // FEATURES + var get = __dependency2__.get; + var set = __dependency3__.set; - HTML5 drag and drop events: + var EmberLocation = __dependency4__["default"]; + var HistoryLocation = __dependency5__["default"]; + var HashLocation = __dependency6__["default"]; + var NoneLocation = __dependency7__["default"]; + + /** + @module ember + @submodule ember-routing + */ - * `dragStart` - * `drag` - * `dragEnter` - * `dragLeave` - * `dragOver` - * `dragEnd` - * `drop` + /** + Ember.AutoLocation will select the best location option based off browser + support with the priority order: history, hash, none. - ## Handlebars `{{view}}` Helper + Clean pushState paths accessed by hashchange-only browsers will be redirected + to the hash-equivalent and vice versa so future transitions are consistent. - Other `Ember.View` instances can be included as part of a view's template by - using the `{{view}}` Handlebars helper. See [Ember.Handlebars.helpers.view](/api/classes/Ember.Handlebars.helpers.html#method_view) - for additional information. + Keep in mind that since some of your users will use `HistoryLocation`, your + server must serve the Ember app at all the routes you define. - @class View + @class AutoLocation @namespace Ember - @extends Ember.CoreView + @static */ - var View = CoreView.extend({ - - concatenatedProperties: ['classNames', 'classNameBindings', 'attributeBindings'], + var AutoLocation = { /** - @property isView - @type Boolean - @default true - @static - */ - isView: true, + @private - // .......................................................... - // TEMPLATE SUPPORT - // + This property is used by router:main to know whether to cancel the routing + setup process, which is needed while we redirect the browser. + + @since 1.5.1 + @property cancelRouterSetup + @default false + */ + cancelRouterSetup: false, /** - The name of the template to lookup if no template is provided. + @private - By default `Ember.View` will lookup a template with this name in - `Ember.TEMPLATES` (a shared global object). + Will be pre-pended to path upon state change. - @property templateName - @type String - @default null + @since 1.5.1 + @property rootURL + @default '/' */ - templateName: null, + rootURL: '/', /** - The name of the layout to lookup if no layout is provided. + @private - By default `Ember.View` will lookup a template with this name in - `Ember.TEMPLATES` (a shared global object). + Attached for mocking in tests - @property layoutName - @type String - @default null + @since 1.5.1 + @property _window + @default window */ - layoutName: null, + _window: window, /** - Used to identify this view during debugging + @private - @property instrumentDisplay - @type String + Attached for mocking in tests + + @property location + @default window.location */ - instrumentDisplay: computed(function() { - if (this.helperName) { - return '{{' + this.helperName + '}}'; - } - }), + _location: window.location, /** - The template used to render the view. This should be a function that - accepts an optional context parameter and returns a string of HTML that - will be inserted into the DOM relative to its parent view. + @private - In general, you should set the `templateName` property instead of setting - the template yourself. + Attached for mocking in tests - @property template - @type Function + @since 1.5.1 + @property _history + @default window.history */ - template: computed('templateName', function(key, value) { - if (value !== undefined) { return value; } - - var templateName = get(this, 'templateName'); - var template = this.templateForName(templateName, 'template'); + _history: window.history, - Ember.assert("You specified the templateName " + templateName + " for " + this + ", but it did not exist.", !templateName || !!template); + /** + @private - return template || get(this, 'defaultTemplate'); - }), + Attached for mocking in tests - _controller: null, + @since 1.5.1 + @property _HistoryLocation + @default Ember.HistoryLocation + */ + _HistoryLocation: HistoryLocation, /** - The controller managing this view. If this property is set, it will be - made available for use by the template. - - @property controller - @type Object - */ - controller: computed(function(key, value) { - if (arguments.length === 2) { - this._controller = value; - return value; - } + @private - if (this._controller) { - return this._controller; - } + Attached for mocking in tests - var parentView = get(this, '_parentView'); - return parentView ? get(parentView, 'controller') : null; - }), + @since 1.5.1 + @property _HashLocation + @default Ember.HashLocation + */ + _HashLocation: HashLocation, /** - A view may contain a layout. A layout is a regular template but - supersedes the `template` property during rendering. It is the - responsibility of the layout template to retrieve the `template` - property from the view (or alternatively, call `Handlebars.helpers.yield`, - `{{yield}}`) to render it in the correct location. + @private - This is useful for a view that has a shared wrapper, but which delegates - the rendering of the contents of the wrapper to the `template` property - on a subclass. + Attached for mocking in tests - @property layout - @type Function + @since 1.5.1 + @property _NoneLocation + @default Ember.NoneLocation */ - layout: computed(function(key) { - var layoutName = get(this, 'layoutName'); - var layout = this.templateForName(layoutName, 'layout'); + _NoneLocation: NoneLocation, - Ember.assert("You specified the layoutName " + layoutName + " for " + this + ", but it did not exist.", !layoutName || !!layout); + /** + @private - return layout || get(this, 'defaultLayout'); - }).property('layoutName'), + Returns location.origin or builds it if device doesn't support it. - _yield: function(context, options, morph) { - var template = get(this, 'template'); + @method _getOrigin + */ + _getOrigin: function () { + var location = this._location, + origin = location.origin; - if (template) { - var useHTMLBars = false; - - useHTMLBars = template.isHTMLBars; - + // Older browsers, especially IE, don't have origin + if (!origin) { + origin = location.protocol + '//' + location.hostname; - if (useHTMLBars) { - return template.render(this, options, morph.contextualElement); - } else { - return template(context, options); + if (location.port) { + origin += ':' + location.port; } } + + return origin; }, - _blockArguments: EMPTY_ARRAY, + /** + @private - templateForName: function(name, type) { - if (!name) { return; } - Ember.assert("templateNames are not allowed to contain periods: "+name, name.indexOf('.') === -1); + We assume that if the history object has a pushState method, the host should + support HistoryLocation. + + @method _getSupportsHistory + */ + _getSupportsHistory: function () { + // Boosted from Modernizr: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/history.js + // The stock browser on Android 2.2 & 2.3 returns positive on history support + // Unfortunately support is really buggy and there is no clean way to detect + // these bugs, so we fall back to a user agent sniff :( + var userAgent = this._window.navigator.userAgent; - if (!this.container) { - throw new EmberError('Container was not found when looking up a views template. ' + - 'This is most likely due to manually instantiating an Ember.View. ' + - 'See: http://git.io/EKPpnA'); + // We only want Android 2, stock browser, and not Chrome which identifies + // itself as 'Mobile Safari' as well + if (userAgent.indexOf('Android 2') !== -1 && + userAgent.indexOf('Mobile Safari') !== -1 && + userAgent.indexOf('Chrome') === -1) { + return false; } - return this.container.lookup('template:' + name); + return !!(this._history && 'pushState' in this._history); }, /** - The object from which templates should access properties. - - This object will be passed to the template function each time the render - method is called, but it is up to the individual function to decide what - to do with it. + @private - By default, this will be the view's controller. + IE8 running in IE7 compatibility mode gives false positive, so we must also + check documentMode. - @property context - @type Object + @method _getSupportsHashChange */ - context: computed(function(key, value) { - if (arguments.length === 2) { - set(this, '_context', value); - return value; - } else { - return get(this, '_context'); - } - })["volatile"](), + _getSupportsHashChange: function () { + var _window = this._window, + documentMode = _window.document.documentMode; - /** - Private copy of the view's template context. This can be set directly - by Handlebars without triggering the observer that causes the view - to be re-rendered. + return ('onhashchange' in _window && (documentMode === undefined || documentMode > 7 )); + }, - The context of a view is looked up as follows: + /** + @private - 1. Supplied context (usually by Handlebars) - 2. Specified controller - 3. `parentView`'s context (for a child of a ContainerView) + Redirects the browser using location.replace, prepending the locatin.origin + to prevent phishing attempts - The code in Handlebars that overrides the `_context` property first - checks to see whether the view has a specified controller. This is - something of a hack and should be revisited. + @method _replacePath + */ + _replacePath: function (path) { + this._location.replace(this._getOrigin() + path); + }, - @property _context + /** + @since 1.5.1 @private + @method _getRootURL */ - _context: computed(function(key, value) { - if (arguments.length === 2) { - return value; - } + _getRootURL: function () { + return this.rootURL; + }, - var parentView, controller; + /** + @private - if (controller = get(this, 'controller')) { - return controller; - } + Returns the current `location.pathname`, normalized for IE inconsistencies. - parentView = this._parentView; - if (parentView) { - return get(parentView, '_context'); + @method _getPath + */ + _getPath: function () { + var pathname = this._location.pathname; + // Various versions of IE/Opera don't always return a leading slash + if (pathname.charAt(0) !== '/') { + pathname = '/' + pathname; } - return null; - }), + return pathname; + }, /** - If a value that affects template rendering changes, the view should be - re-rendered to reflect the new value. - - @method _contextDidChange @private - */ - _contextDidChange: observer('context', function() { - this.rerender(); - }), - /** - If `false`, the view will appear hidden in DOM. + Returns normalized location.hash as an alias to Ember.Location._getHash - @property isVisible - @type Boolean - @default null + @since 1.5.1 + @method _getHash */ - isVisible: true, + _getHash: EmberLocation._getHash, /** - Array of child views. You should never edit this array directly. - Instead, use `appendChild` and `removeFromParent`. - - @property childViews - @type Array - @default [] @private - */ - childViews: childViewsProperty, - - _childViews: EMPTY_ARRAY, - // When it's a virtual view, we need to notify the parent that their - // childViews will change. - _childViewsWillChange: beforeObserver('childViews', function() { - if (this.isVirtual) { - var parentView = get(this, 'parentView'); - if (parentView) { propertyWillChange(parentView, 'childViews'); } - } - }), + Returns location.search - // When it's a virtual view, we need to notify the parent that their - // childViews did change. - _childViewsDidChange: observer('childViews', function() { - if (this.isVirtual) { - var parentView = get(this, 'parentView'); - if (parentView) { propertyDidChange(parentView, 'childViews'); } - } - }), + @since 1.5.1 + @method _getQuery + */ + _getQuery: function () { + return this._location.search; + }, /** - Return the nearest ancestor that is an instance of the provided - class. + @private - @method nearestInstanceOf - @param {Class} klass Subclass of Ember.View (or Ember.View itself) - @return Ember.View - @deprecated - */ - nearestInstanceOf: function(klass) { - Ember.deprecate("nearestInstanceOf is deprecated and will be removed from future releases. Use nearestOfType."); - var view = get(this, 'parentView'); + Returns the full pathname including query and hash - while (view) { - if (view instanceof klass) { return view; } - view = get(view, 'parentView'); - } + @method _getFullPath + */ + _getFullPath: function () { + return this._getPath() + this._getQuery() + this._getHash(); }, /** - Return the nearest ancestor that is an instance of the provided - class or mixin. + @private - @method nearestOfType - @param {Class,Mixin} klass Subclass of Ember.View (or Ember.View itself), - or an instance of Ember.Mixin. - @return Ember.View + Returns the current path as it should appear for HistoryLocation supported + browsers. This may very well differ from the real current path (e.g. if it + starts off as a hashed URL) + + @method _getHistoryPath */ - nearestOfType: function(klass) { - var view = get(this, 'parentView'); - var isOfType = klass instanceof Mixin ? - function(view) { return klass.detect(view); } : - function(view) { return klass.detect(view.constructor); }; + _getHistoryPath: function () { + var rootURL = this._getRootURL(), + path = this._getPath(), + hash = this._getHash(), + query = this._getQuery(), + rootURLIndex = path.indexOf(rootURL), + routeHash, hashParts; - while (view) { - if (isOfType(view)) { return view; } - view = get(view, 'parentView'); - } - }, + Ember.assert('Path ' + path + ' does not start with the provided rootURL ' + rootURL, rootURLIndex === 0); - /** - Return the nearest ancestor that has a given property. + // By convention, Ember.js routes using HashLocation are required to start + // with `#/`. Anything else should NOT be considered a route and should + // be passed straight through, without transformation. + if (hash.substr(0, 2) === '#/') { + // There could be extra hash segments after the route + hashParts = hash.substr(1).split('#'); + // The first one is always the route url + routeHash = hashParts.shift(); - @method nearestWithProperty - @param {String} property A property name - @return Ember.View - */ - nearestWithProperty: function(property) { - var view = get(this, 'parentView'); + // If the path already has a trailing slash, remove the one + // from the hashed route so we don't double up. + if (path.slice(-1) === '/') { + routeHash = routeHash.substr(1); + } - while (view) { - if (property in view) { return view; } - view = get(view, 'parentView'); + // This is the "expected" final order + path += routeHash; + path += query; + + if (hashParts.length) { + path += '#' + hashParts.join('#'); + } + } else { + path += query; + path += hash; } + + return path; }, /** - Return the nearest ancestor whose parent is an instance of - `klass`. + @private - @method nearestChildOf - @param {Class} klass Subclass of Ember.View (or Ember.View itself) - @return Ember.View + Returns the current path as it should appear for HashLocation supported + browsers. This may very well differ from the real current path. + + @method _getHashPath */ - nearestChildOf: function(klass) { - var view = get(this, 'parentView'); + _getHashPath: function () { + var rootURL = this._getRootURL(), + path = rootURL, + historyPath = this._getHistoryPath(), + routePath = historyPath.substr(rootURL.length); - while (view) { - if (get(view, 'parentView') instanceof klass) { return view; } - view = get(view, 'parentView'); + if (routePath !== '') { + if (routePath.charAt(0) !== '/') { + routePath = '/' + routePath; + } + + path += '#' + routePath; } + + return path; }, /** - When the parent view changes, recursively invalidate `controller` + Selects the best location option based off browser support and returns an + instance of that Location class. - @method _parentViewDidChange - @private + @see Ember.AutoLocation + @method create */ - _parentViewDidChange: observer('_parentView', function() { - if (this.isDestroying) { return; } - - this._setupKeywords(); - this.trigger('parentViewDidChange'); - - if (get(this, 'parentView.controller') && !get(this, 'controller')) { - this.notifyPropertyChange('controller'); + create: function (options) { + if (options && options.rootURL) { + Ember.assert('rootURL must end with a trailing forward slash e.g. "/app/"', options.rootURL.charAt(options.rootURL.length-1) === '/'); + this.rootURL = options.rootURL; } - }), - - _controllerDidChange: observer('controller', function() { - if (this.isDestroying) { return; } - - this.rerender(); - this.forEachChildView(function(view) { - view.propertyDidChange('controller'); - }); - }), + var historyPath, hashPath, + cancelRouterSetup = false, + implementationClass = this._NoneLocation, + currentPath = this._getFullPath(); - _setupKeywords: function() { - var keywords = this._keywords; - var contextView = this._contextView || this._parentView; + if (this._getSupportsHistory()) { + historyPath = this._getHistoryPath(); - if (contextView) { - var parentKeywords = contextView._keywords; + // Since we support history paths, let's be sure we're using them else + // switch the location over to it. + if (currentPath === historyPath) { + implementationClass = this._HistoryLocation; + } else { + cancelRouterSetup = true; + this._replacePath(historyPath); + } - keywords.view.setSource(this.isVirtual ? parentKeywords.view : this); + } else if (this._getSupportsHashChange()) { + hashPath = this._getHashPath(); - for (var name in parentKeywords) { - if (keywords[name]) continue; - keywords[name] = parentKeywords[name]; + // Be sure we're using a hashed path, otherwise let's switch over it to so + // we start off clean and consistent. We'll count an index path with no + // hash as "good enough" as well. + if (currentPath === hashPath || (currentPath === '/' && hashPath === '/#/')) { + implementationClass = this._HashLocation; + } else { + // Our URL isn't in the expected hash-supported format, so we want to + // cancel the router setup and replace the URL to start off clean + cancelRouterSetup = true; + this._replacePath(hashPath); } - } else { - keywords.view.setSource(this.isVirtual ? null : this); } - }, - - /** - Called on your view when it should push strings of HTML into a - `Ember.RenderBuffer`. Most users will want to override the `template` - or `templateName` properties instead of this method. - By default, `Ember.View` will look for a function in the `template` - property and invoke it with the value of `context`. The value of - `context` will be the view's controller unless you override it. + var implementation = implementationClass.create.apply(implementationClass, arguments); - @method render - @param {Ember.RenderBuffer} buffer The render buffer - */ - render: function(buffer) { - // If this view has a layout, it is the responsibility of the - // the layout to render the view's template. Otherwise, render the template - // directly. - var template = get(this, 'layout') || get(this, 'template'); + if (cancelRouterSetup) { + set(implementation, 'cancelRouterSetup', true); + } - if (template) { - var context = get(this, 'context'); - var output; + return implementation; + } + }; - var data = { - view: this, - buffer: buffer, - isRenderData: true - }; + __exports__["default"] = AutoLocation; + }); +define("ember-routing/location/hash_location", + ["ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-metal/utils","ember-runtime/system/object","ember-routing/location/api","ember-views/system/jquery","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; + var get = __dependency1__.get; + var set = __dependency2__.set; + var run = __dependency3__["default"]; + var guidFor = __dependency4__.guidFor; - // Invoke the template with the provided template context, which - // is the view's controller by default. A hash of data is also passed that provides - // the template with access to the view and render buffer. + var EmberObject = __dependency5__["default"]; + var EmberLocation = __dependency6__["default"]; + var jQuery = __dependency7__["default"]; - // The template should write directly to the render buffer instead - // of returning a string. - var options = { data: data }; - var useHTMLBars = false; + /** + @module ember + @submodule ember-routing + */ - - useHTMLBars = template.isHTMLBars; - + /** + `Ember.HashLocation` implements the location API using the browser's + hash. At present, it relies on a `hashchange` event existing in the + browser. - if (useHTMLBars) { - Ember.assert('template must be an object. Did you mean to call Ember.Handlebars.compile("...") or specify templateName instead?', typeof template === 'object'); - var env = Ember.merge(buildHTMLBarsDefaultEnv(), options); - output = template.render(this, env, buffer.innerContextualElement(), this._blockArguments); - } else { - Ember.assert('template must be a function. Did you mean to call Ember.Handlebars.compile("...") or specify templateName instead?', typeof template === 'function'); - output = template(context, options); - } + @class HashLocation + @namespace Ember + @extends Ember.Object + */ + var HashLocation = EmberObject.extend({ + implementation: 'hash', - // If the template returned a string instead of writing to the buffer, - // push the string onto the buffer. - if (output !== undefined) { buffer.push(output); } - } + init: function() { + set(this, 'location', get(this, '_location') || window.location); }, /** - Renders the view again. This will work regardless of whether the - view is already in the DOM or not. If the view is in the DOM, the - rendering process will be deferred to give bindings a chance - to synchronize. - - If children were added during the rendering process using `appendChild`, - `rerender` will remove them, because they will be added again - if needed by the next `render`. + @private - In general, if the display of your view changes, you should modify - the DOM element directly instead of manually calling `rerender`, which can - be slow. + Returns normalized location.hash - @method rerender + @since 1.5.1 + @method getHash */ - rerender: function() { - return this.currentState.rerender(this); - }, + getHash: EmberLocation._getHash, /** - Iterates over the view's `classNameBindings` array, inserts the value - of the specified property into the `classNames` array, then creates an - observer to update the view's element if the bound property ever changes - in the future. + Returns the current `location.hash`, minus the '#' at the front. - @method _applyClassNameBindings @private + @method getURL */ - _applyClassNameBindings: function(classBindings) { - var classNames = this.classNames; - var elem, newClass, dasherizedClass; - - // Loop through all of the configured bindings. These will be either - // property names ('isUrgent') or property paths relative to the view - // ('content.isUrgent') - forEach(classBindings, function(binding) { + getURL: function() { + return this.getHash().substr(1); + }, - var boundBinding; - if (isStream(binding)) { - boundBinding = binding; - } else { - boundBinding = streamifyClassNameBinding(this, binding, '_view.'); - } + /** + Set the `location.hash` and remembers what was set. This prevents + `onUpdateURL` callbacks from triggering when the hash was set by + `HashLocation`. - // Variable in which the old class value is saved. The observer function - // closes over this variable, so it knows which string to remove when - // the property changes. - var oldClass; + @private + @method setURL + @param path {String} + */ + setURL: function(path) { + get(this, 'location').hash = path; + set(this, 'lastSetURL', path); + }, - // Set up an observer on the context. If the property changes, toggle the - // class name. - var observer = this._wrapAsScheduled(function() { - // Get the current value of the property - elem = this.$(); - newClass = read(boundBinding); + /** + Uses location.replace to update the url without a page reload + or history modification. - // If we had previously added a class to the element, remove it. - if (oldClass) { - elem.removeClass(oldClass); - // Also remove from classNames so that if the view gets rerendered, - // the class doesn't get added back to the DOM. - classNames.removeObject(oldClass); - } + @private + @method replaceURL + @param path {String} + */ + replaceURL: function(path) { + get(this, 'location').replace('#' + path); + set(this, 'lastSetURL', path); + }, - // If necessary, add a new class. Make sure we keep track of it so - // it can be removed in the future. - if (newClass) { - elem.addClass(newClass); - oldClass = newClass; - } else { - oldClass = null; - } - }); + /** + Register a callback to be invoked when the hash changes. These + callbacks will execute when the user presses the back or forward + button, but not after `setURL` is invoked. - // Get the class name for the property at its current value - dasherizedClass = read(boundBinding); + @private + @method onUpdateURL + @param callback {Function} + */ + onUpdateURL: function(callback) { + var self = this; + var guid = guidFor(this); - if (dasherizedClass) { - // Ensure that it gets into the classNames array - // so it is displayed when we render. - addObject(classNames, dasherizedClass); + jQuery(window).on('hashchange.ember-location-'+guid, function() { + run(function() { + var path = self.getURL(); + if (get(self, 'lastSetURL') === path) { return; } - // Save a reference to the class name so we can remove it - // if the observer fires. Remember that this variable has - // been closed over by the observer. - oldClass = dasherizedClass; - } + set(self, 'lastSetURL', null); - subscribe(boundBinding, observer, this); - // Remove className so when the view is rerendered, - // the className is added based on binding reevaluation - this.one('willClearRender', function() { - if (oldClass) { - classNames.removeObject(oldClass); - oldClass = null; - } + callback(path); }); - - }, this); + }); }, - _unspecifiedAttributeBindings: null, - /** - Iterates through the view's attribute bindings, sets up observers for each, - then applies the current value of the attributes to the passed render buffer. + Given a URL, formats it to be placed into the page as part + of an element's `href` attribute. + + This is used, for example, when using the {{action}} helper + to generate a URL based on an event. - @method _applyAttributeBindings - @param {Ember.RenderBuffer} buffer @private + @method formatURL + @param url {String} */ - _applyAttributeBindings: function(buffer, attributeBindings) { - var attributeValue; - var unspecifiedAttributeBindings = this._unspecifiedAttributeBindings = this._unspecifiedAttributeBindings || {}; - - forEach(attributeBindings, function(binding) { - var split = binding.split(':'); - var property = split[0]; - var attributeName = split[1] || property; - - Ember.assert('You cannot use class as an attributeBinding, use classNameBindings instead.', attributeName !== 'class'); - - if (property in this) { - this._setupAttributeBindingObservation(property, attributeName); - - // Determine the current value and add it to the render buffer - // if necessary. - attributeValue = get(this, property); - View.applyAttributeBindings(buffer, attributeName, attributeValue); - } else { - unspecifiedAttributeBindings[property] = attributeName; - } - }, this); - - // Lazily setup setUnknownProperty after attributeBindings are initially applied - this.setUnknownProperty = this._setUnknownProperty; + formatURL: function(url) { + return '#'+url; }, - _setupAttributeBindingObservation: function(property, attributeName) { - var attributeValue, elem; + /** + Cleans up the HashLocation event listener. - // Create an observer to add/remove/change the attribute if the - // JavaScript property changes. - var observer = function() { - elem = this.$(); + @private + @method willDestroy + */ + willDestroy: function() { + var guid = guidFor(this); - attributeValue = get(this, property); + jQuery(window).off('hashchange.ember-location-'+guid); + } + }); - View.applyAttributeBindings(elem, attributeName, attributeValue); - }; + __exports__["default"] = HashLocation; + }); +define("ember-routing/location/history_location", + ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-runtime/system/object","ember-views/system/jquery","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // FEATURES + var get = __dependency2__.get; + var set = __dependency3__.set; + var guidFor = __dependency4__.guidFor; - this.registerObserver(this, property, observer); - }, + var EmberObject = __dependency5__["default"]; + var jQuery = __dependency6__["default"]; - /** - We're using setUnknownProperty as a hook to setup attributeBinding observers for - properties that aren't defined on a view at initialization time. + /** + @module ember + @submodule ember-routing + */ - Note: setUnknownProperty will only be called once for each property. + var popstateFired = false; + var supportsHistoryState = window.history && 'state' in window.history; - @method setUnknownProperty - @param key - @param value - @private - */ - setUnknownProperty: null, // Gets defined after initialization by _applyAttributeBindings + /** + Ember.HistoryLocation implements the location API using the browser's + history.pushState API. - _setUnknownProperty: function(key, value) { - var attributeName = this._unspecifiedAttributeBindings && this._unspecifiedAttributeBindings[key]; - if (attributeName) { - this._setupAttributeBindingObservation(key, attributeName); - } + @class HistoryLocation + @namespace Ember + @extends Ember.Object + */ + var HistoryLocation = EmberObject.extend({ + implementation: 'history', - defineProperty(this, key); - return set(this, key, value); + init: function() { + set(this, 'location', get(this, 'location') || window.location); + set(this, 'baseURL', jQuery('base').attr('href') || ''); }, /** - Given a property name, returns a dasherized version of that - property name if the property evaluates to a non-falsy value. - - For example, if the view has property `isUrgent` that evaluates to true, - passing `isUrgent` to this method will return `"is-urgent"`. + Used to set state on first call to setURL - @method _classStringForProperty - @param property @private + @method initState */ - _classStringForProperty: function(parsedPath) { - return View._classStringForValue(parsedPath.path, parsedPath.stream.value(), parsedPath.className, parsedPath.falsyClassName); + initState: function() { + set(this, 'history', get(this, 'history') || window.history); + this.replaceState(this.formatURL(this.getURL())); }, - // .......................................................... - // ELEMENT SUPPORT - // - /** - Returns the current DOM element for the view. + Will be pre-pended to path upon state change - @property element - @type DOMElement + @property rootURL + @default '/' */ - element: null, + rootURL: '/', /** - Returns a jQuery object for this view's element. If you pass in a selector - string, this method will return a jQuery object, using the current element - as its buffer. - - For example, calling `view.$('li')` will return a jQuery object containing - all of the `li` elements inside the DOM element of this view. + Returns the current `location.pathname` without `rootURL` or `baseURL` - @method $ - @param {String} [selector] a jQuery-compatible selector string - @return {jQuery} the jQuery object for the DOM node + @private + @method getURL + @return url {String} */ - $: function(sel) { - return this.currentState.$(this, sel); - }, - - mutateChildViews: function(callback) { - var childViews = this._childViews; - var idx = childViews.length; - var view; + getURL: function() { + var rootURL = get(this, 'rootURL'), + location = get(this, 'location'), + path = location.pathname, + baseURL = get(this, 'baseURL'); - while(--idx >= 0) { - view = childViews[idx]; - callback(this, view, idx); - } + rootURL = rootURL.replace(/\/$/, ''); + baseURL = baseURL.replace(/\/$/, ''); + var url = path.replace(baseURL, '').replace(rootURL, ''); - return this; + + return url; }, - forEachChildView: function(callback) { - var childViews = this._childViews; - - if (!childViews) { return this; } + /** + Uses `history.pushState` to update the url without a page reload. - var len = childViews.length; - var view, idx; + @private + @method setURL + @param path {String} + */ + setURL: function(path) { + var state = this.getState(); + path = this.formatURL(path); - for (idx = 0; idx < len; idx++) { - view = childViews[idx]; - callback(view); + if (!state || state.path !== path) { + this.pushState(path); } - - return this; }, /** - Appends the view's element to the specified parent element. - - If the view does not have an HTML representation yet, `createElement()` - will be called automatically. - - Note that this method just schedules the view to be appended; the DOM - element will not be appended to the given element until all bindings have - finished synchronizing. - - This is not typically a function that you will need to call directly when - building your application. You might consider using `Ember.ContainerView` - instead. If you do need to use `appendTo`, be sure that the target element - you are providing is associated with an `Ember.Application` and does not - have an ancestor element that is associated with an Ember view. + Uses `history.replaceState` to update the url without a page reload + or history modification. - @method appendTo - @param {String|DOMElement|jQuery} A selector, element, HTML string, or jQuery object - @return {Ember.View} receiver + @private + @method replaceURL + @param path {String} */ - appendTo: function(selector) { - var target = jQuery(selector); + replaceURL: function(path) { + var state = this.getState(); + path = this.formatURL(path); - Ember.assert("You tried to append to (" + selector + ") but that isn't in the DOM", target.length > 0); - Ember.assert("You cannot append to an existing Ember.View. Consider using Ember.ContainerView instead.", !target.is('.ember-view') && !target.parents().is('.ember-view')); + if (!state || state.path !== path) { + this.replaceState(path); + } + }, - this.constructor.renderer.appendTo(this, target[0]); + /** + Get the current `history.state`. Checks for if a polyfill is + required and if so fetches this._historyState. The state returned + from getState may be null if an iframe has changed a window's + history. - return this; + @private + @method getState + @return state {Object} + */ + getState: function() { + return supportsHistoryState ? get(this, 'history').state : this._historyState; }, /** - Replaces the content of the specified parent element with this view's - element. If the view does not have an HTML representation yet, - the element will be generated automatically. - - Note that this method just schedules the view to be appended; the DOM - element will not be appended to the given element until all bindings have - finished synchronizing + Pushes a new state. - @method replaceIn - @param {String|DOMElement|jQuery} target A selector, element, HTML string, or jQuery object - @return {Ember.View} received + @private + @method pushState + @param path {String} */ - replaceIn: function(selector) { - var target = jQuery(selector); + pushState: function(path) { + var state = { path: path }; - Ember.assert("You tried to replace in (" + selector + ") but that isn't in the DOM", target.length > 0); - Ember.assert("You cannot replace an existing Ember.View. Consider using Ember.ContainerView instead.", !target.is('.ember-view') && !target.parents().is('.ember-view')); + get(this, 'history').pushState(state, null, path); - this.constructor.renderer.replaceIn(this, target[0]); + // store state if browser doesn't support `history.state` + if (!supportsHistoryState) { + this._historyState = state; + } - return this; + // used for webkit workaround + this._previousURL = this.getURL(); }, /** - Appends the view's element to the document body. If the view does - not have an HTML representation yet - the element will be generated automatically. + Replaces the current state. - If your application uses the `rootElement` property, you must append - the view within that element. Rendering views outside of the `rootElement` - is not supported. + @private + @method replaceState + @param path {String} + */ + replaceState: function(path) { + var state = { path: path }; - Note that this method just schedules the view to be appended; the DOM - element will not be appended to the document body until all bindings have - finished synchronizing. + get(this, 'history').replaceState(state, null, path); - @method append - @return {Ember.View} receiver - */ - append: function() { - return this.appendTo(document.body); + // store state if browser doesn't support `history.state` + if (!supportsHistoryState) { + this._historyState = state; + } + + // used for webkit workaround + this._previousURL = this.getURL(); }, /** - Removes the view's element from the element to which it is attached. + Register a callback to be invoked whenever the browser + history changes, including using forward and back buttons. - @method remove - @return {Ember.View} receiver + @private + @method onUpdateURL + @param callback {Function} */ - remove: function() { - // What we should really do here is wait until the end of the run loop - // to determine if the element has been re-appended to a different - // element. - // In the interim, we will just re-render if that happens. It is more - // important than elements get garbage collected. - if (!this.removedFromDOM) { this.destroyElement(); } + onUpdateURL: function(callback) { + var guid = guidFor(this), + self = this; + + jQuery(window).on('popstate.ember-location-'+guid, function(e) { + // Ignore initial page load popstate event in Chrome + if (!popstateFired) { + popstateFired = true; + if (self.getURL() === self._previousURL) { return; } + } + callback(self.getURL()); + }); }, /** - The HTML `id` of the view's element in the DOM. You can provide this - value yourself but it must be unique (just as in HTML): - - ```handlebars - {{my-component elementId="a-really-cool-id"}} - ``` + Used when using `{{action}}` helper. The url is always appended to the rootURL. - If not manually set a default value will be provided by the framework. + @private + @method formatURL + @param url {String} + @return formatted url {String} + */ + formatURL: function(url) { + var rootURL = get(this, 'rootURL'), + baseURL = get(this, 'baseURL'); - Once rendered an element's `elementId` is considered immutable and you - should never change it. + if (url !== '') { + rootURL = rootURL.replace(/\/$/, ''); + baseURL = baseURL.replace(/\/$/, ''); + } else if(baseURL.match(/^\//) && rootURL.match(/^\//)) { + baseURL = baseURL.replace(/\/$/, ''); + } - @property elementId - @type String - */ - elementId: null, + return baseURL + rootURL + url; + }, /** - Attempts to discover the element in the parent element. The default - implementation looks for an element with an ID of `elementId` (or the - view's guid if `elementId` is null). You can override this method to - provide your own form of lookup. For example, if you want to discover your - element using a CSS class name instead of an ID. + Cleans up the HistoryLocation event listener. - @method findElementInParentElement - @param {DOMElement} parentElement The parent's DOM element - @return {DOMElement} The discovered element + @private + @method willDestroy */ - findElementInParentElement: function(parentElem) { - var id = "#" + this.elementId; - return jQuery(id)[0] || jQuery(id, parentElem)[0]; - }, + willDestroy: function() { + var guid = guidFor(this); - /** - Creates a DOM representation of the view and all of its child views by - recursively calling the `render()` method. + jQuery(window).off('popstate.ember-location-'+guid); + } + }); - After the element has been inserted into the DOM, `didInsertElement` will - be called on this view and all of its child views. + __exports__["default"] = HistoryLocation; + }); +define("ember-routing/location/none_location", + ["ember-metal/property_get","ember-metal/property_set","ember-runtime/system/object","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var get = __dependency1__.get; + var set = __dependency2__.set; + var EmberObject = __dependency3__["default"]; - @method createElement - @return {Ember.View} receiver - */ - createElement: function() { - if (this.element) { return this; } + /** + @module ember + @submodule ember-routing + */ - this._didCreateElementWithoutMorph = true; - this.constructor.renderer.renderTree(this); + /** + Ember.NoneLocation does not interact with the browser. It is useful for + testing, or when you need to manage state with your Router, but temporarily + don't want it to muck with the URL (for example when you embed your + application in a larger page). - return this; - }, + @class NoneLocation + @namespace Ember + @extends Ember.Object + */ + var NoneLocation = EmberObject.extend({ + implementation: 'none', + path: '', /** - Called when a view is going to insert an element into the DOM. + Returns the current path. - @event willInsertElement + @private + @method getURL + @return {String} path */ - willInsertElement: K, + getURL: function() { + return get(this, 'path'); + }, /** - Called when the element of the view has been inserted into the DOM - or after the view was re-rendered. Override this function to do any - set up that requires an element in the document body. - - When a view has children, didInsertElement will be called on the - child view(s) first, bubbling upwards through the hierarchy. + Set the path and remembers what was set. Using this method + to change the path will not invoke the `updateURL` callback. - @event didInsertElement + @private + @method setURL + @param path {String} */ - didInsertElement: K, + setURL: function(path) { + set(this, 'path', path); + }, /** - Called when the view is about to rerender, but before anything has - been torn down. This is a good opportunity to tear down any manual - observers you have installed based on the DOM state + Register a callback to be invoked when the path changes. These + callbacks will execute when the user presses the back or forward + button, but not after `setURL` is invoked. - @event willClearRender + @private + @method onUpdateURL + @param callback {Function} */ - willClearRender: K, + onUpdateURL: function(callback) { + this.updateCallback = callback; + }, /** - Destroys any existing element along with the element for any child views - as well. If the view does not currently have a element, then this method - will do nothing. - - If you implement `willDestroyElement()` on your view, then this method will - be invoked on your view before your element is destroyed to give you a - chance to clean up any event handlers, etc. - - If you write a `willDestroyElement()` handler, you can assume that your - `didInsertElement()` handler was called earlier for the same element. - - You should not call or override this method yourself, but you may - want to implement the above callbacks. + Sets the path and calls the `updateURL` callback. - @method destroyElement - @return {Ember.View} receiver + @private + @method handleURL + @param callback {Function} */ - destroyElement: function() { - return this.currentState.destroyElement(this); + handleURL: function(url) { + set(this, 'path', url); + this.updateCallback(url); }, /** - Called when the element of the view is going to be destroyed. Override - this function to do any teardown that requires an element, like removing - event listeners. + Given a URL, formats it to be placed into the page as part + of an element's `href` attribute. - Please note: any property changes made during this event will have no - effect on object observers. + This is used, for example, when using the {{action}} helper + to generate a URL based on an event. - @event willDestroyElement + @private + @method formatURL + @param url {String} + @return {String} url */ - willDestroyElement: K, - - /** - Called when the parentView property has changed. + formatURL: function(url) { + // The return value is not overly meaningful, but we do not want to throw + // errors when test code renders templates containing {{action href=true}} + // helpers. + return url; + } + }); - @event parentViewDidChange - */ - parentViewDidChange: K, + __exports__["default"] = NoneLocation; + }); +define("ember-routing", + ["ember-handlebars","ember-metal/core","ember-routing/ext/run_loop","ember-routing/ext/controller","ember-routing/ext/view","ember-routing/helpers/shared","ember-routing/helpers/link_to","ember-routing/location/api","ember-routing/location/none_location","ember-routing/location/hash_location","ember-routing/location/history_location","ember-routing/location/auto_location","ember-routing/system/controller_for","ember-routing/system/dsl","ember-routing/system/router","ember-routing/system/route","ember-routing/helpers/outlet","ember-routing/helpers/render","ember-routing/helpers/action","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __exports__) { + "use strict"; + // require('ember-runtime'); + // require('ember-views'); + // require('ember-handlebars'); - instrumentName: 'view', + /** + Ember Routing - instrumentDetails: function(hash) { - hash.template = get(this, 'templateName'); - this._super(hash); - }, + @module ember + @submodule ember-routing + @requires ember-views + */ - beforeRender: function(buffer) {}, + var EmberHandlebars = __dependency1__["default"]; + var Ember = __dependency2__["default"]; - afterRender: function(buffer) {}, + // ES6TODO: Cleanup modules with side-effects below - applyAttributesToBuffer: function(buffer) { - // Creates observers for all registered class name and attribute bindings, - // then adds them to the element. - var classNameBindings = this.classNameBindings; - if (classNameBindings.length) { - this._applyClassNameBindings(classNameBindings); - } + var resolvePaths = __dependency6__.resolvePaths; + var resolveParams = __dependency6__.resolveParams; + var deprecatedLinkToHelper = __dependency7__.deprecatedLinkToHelper; + var linkToHelper = __dependency7__.linkToHelper; + var LinkView = __dependency7__.LinkView; + + + // require('ember-views'); + var EmberLocation = __dependency8__["default"]; + var NoneLocation = __dependency9__["default"]; + var HashLocation = __dependency10__["default"]; + var HistoryLocation = __dependency11__["default"]; + var AutoLocation = __dependency12__["default"]; + + var controllerFor = __dependency13__.controllerFor; + var generateControllerFactory = __dependency13__.generateControllerFactory; + var generateController = __dependency13__.generateController; + var RouterDSL = __dependency14__["default"]; + var Router = __dependency15__["default"]; + var Route = __dependency16__["default"]; + var outletHelper = __dependency17__.outletHelper; + var OutletView = __dependency17__.OutletView; + var renderHelper = __dependency18__["default"]; + var ActionHelper = __dependency19__.ActionHelper; + var actionHelper = __dependency19__.actionHelper; - // Pass the render buffer so the method can apply attributes directly. - // This isn't needed for class name bindings because they use the - // existing classNames infrastructure. - var attributeBindings = this.attributeBindings; - if (attributeBindings.length) { - this._applyAttributeBindings(buffer, attributeBindings); - } - buffer.setClasses(this.classNames); - buffer.id(this.elementId); + Ember.Location = EmberLocation; + Ember.AutoLocation = AutoLocation; + Ember.HashLocation = HashLocation; + Ember.HistoryLocation = HistoryLocation; + Ember.NoneLocation = NoneLocation; - var role = get(this, 'ariaRole'); - if (role) { - buffer.attr('role', role); - } + Ember.controllerFor = controllerFor; + Ember.generateControllerFactory = generateControllerFactory; + Ember.generateController = generateController; + Ember.RouterDSL = RouterDSL; + Ember.Router = Router; + Ember.Route = Route; + Ember.LinkView = LinkView; - if (get(this, 'isVisible') === false) { - buffer.style('display', 'none'); - } - }, + Router.resolveParams = resolveParams; + Router.resolvePaths = resolvePaths; - // .......................................................... - // STANDARD RENDER PROPERTIES - // + EmberHandlebars.ActionHelper = ActionHelper; + EmberHandlebars.OutletView = OutletView; - /** - Tag name for the view's outer element. The tag name is only used when an - element is first created. If you change the `tagName` for an element, you - must destroy and recreate the view element. + EmberHandlebars.registerHelper('render', renderHelper) + EmberHandlebars.registerHelper('action', actionHelper); + EmberHandlebars.registerHelper('outlet', outletHelper); + EmberHandlebars.registerHelper('link-to', linkToHelper); + EmberHandlebars.registerHelper('linkTo', deprecatedLinkToHelper); - By default, the render buffer will use a `
    ` tag for views. + __exports__["default"] = Ember; + }); +define("ember-routing/system/controller_for", + ["ember-metal/core","ember-metal/property_get","ember-metal/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // Logger + var get = __dependency2__.get; + var isArray = __dependency3__.isArray; - @property tagName - @type String - @default null - */ + /** + @module ember + @submodule ember-routing + */ - // We leave this null by default so we can tell the difference between - // the default case and a user-specified tag. - tagName: null, + /** - /** - The WAI-ARIA role of the control represented by this view. For example, a - button may have a role of type 'button', or a pane may have a role of - type 'alertdialog'. This property is used by assistive software to help - visually challenged users navigate rich web applications. + Finds a controller instance. - The full list of valid WAI-ARIA roles is available at: - [http://www.w3.org/TR/wai-aria/roles#roles_categorization](http://www.w3.org/TR/wai-aria/roles#roles_categorization) + @for Ember + @method controllerFor + @private + */ + var controllerFor = function(container, controllerName, lookupOptions) { + return container.lookup('controller:' + controllerName, lookupOptions); + }; - @property ariaRole - @type String - @default null - */ - ariaRole: null, + /** + Generates a controller factory - /** - Standard CSS class names to apply to the view's outer element. This - property automatically inherits any class names defined by the view's - superclasses as well. + The type of the generated controller factory is derived + from the context. If the context is an array an array controller + is generated, if an object, an object controller otherwise, a basic + controller is generated. - @property classNames - @type Array - @default ['ember-view'] - */ - classNames: ['ember-view'], + You can customize your generated controllers by defining + `App.ObjectController` or `App.ArrayController`. - /** - A list of properties of the view to apply as class names. If the property - is a string value, the value of that string will be applied as a class - name. + @for Ember + @method generateControllerFactory + @private + */ + var generateControllerFactory = function(container, controllerName, context) { + var Factory, fullName, instance, name, factoryName, controllerType; - ```javascript - // Applies the 'high' class to the view element - Ember.View.extend({ - classNameBindings: ['priority'] - priority: 'high' - }); - ``` + if (context && isArray(context)) { + controllerType = 'array'; + } else if (context) { + controllerType = 'object'; + } else { + controllerType = 'basic'; + } - If the value of the property is a Boolean, the name of that property is - added as a dasherized class name. + factoryName = 'controller:' + controllerType; - ```javascript - // Applies the 'is-urgent' class to the view element - Ember.View.extend({ - classNameBindings: ['isUrgent'] - isUrgent: true - }); - ``` + Factory = container.lookupFactory(factoryName).extend({ + isGenerated: true, + toString: function() { + return "(generated " + controllerName + " controller)"; + } + }); - If you would prefer to use a custom value instead of the dasherized - property name, you can pass a binding like this: + fullName = 'controller:' + controllerName; - ```javascript - // Applies the 'urgent' class to the view element - Ember.View.extend({ - classNameBindings: ['isUrgent:urgent'] - isUrgent: true - }); - ``` + container.register(fullName, Factory); - This list of properties is inherited from the view's superclasses as well. + return Factory; + }; - @property classNameBindings - @type Array - @default [] - */ - classNameBindings: EMPTY_ARRAY, + /** + Generates and instantiates a controller. - /** - A list of properties of the view to apply as attributes. If the property is - a string value, the value of that string will be applied as the attribute. + The type of the generated controller factory is derived + from the context. If the context is an array an array controller + is generated, if an object, an object controller otherwise, a basic + controller is generated. - ```javascript - // Applies the type attribute to the element - // with the value "button", like
    - Ember.View.extend({ - attributeBindings: ['type'], - type: 'button' - }); - ``` + @for Ember + @method generateController + @private + @since 1.3.0 + */ + var generateController = function(container, controllerName, context) { + generateControllerFactory(container, controllerName, context); + var fullName = 'controller:' + controllerName; + var instance = container.lookup(fullName); - If the value of the property is a Boolean, the name of that property is - added as an attribute. + if (get(instance, 'namespace.LOG_ACTIVE_GENERATION')) { + Ember.Logger.info("generated -> " + fullName, { fullName: fullName }); + } - ```javascript - // Renders something like
    - Ember.View.extend({ - attributeBindings: ['enabled'], - enabled: true - }); - ``` + return instance; + }; - @property attributeBindings - */ - attributeBindings: EMPTY_ARRAY, + __exports__.controllerFor = controllerFor; + __exports__.generateControllerFactory = generateControllerFactory; + __exports__.generateController = generateController; + }); +define("ember-routing/system/dsl", + ["ember-metal/core","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // FEATURES, assert - // ....................................................... - // CORE DISPLAY METHODS - // + /** + @module ember + @submodule ember-routing + */ - /** - Setup a view, but do not finish waking it up. + function DSL(name) { + this.parent = name; + this.matches = []; + } - * configure `childViews` - * register the view with the global views hash, which is used for event - dispatch + DSL.prototype = { + resource: function(name, options, callback) { + Ember.assert("'basic' cannot be used as a resource name.", name !== 'basic'); - @method init - @private - */ - init: function() { - if (!this.isVirtual && !this.elementId) { - this.elementId = guidFor(this); + if (arguments.length === 2 && typeof options === 'function') { + callback = options; + options = {}; } - this._super(); + if (arguments.length === 1) { + options = {}; + } - // setup child views. be sure to clone the child views array first - this._childViews = this._childViews.slice(); - this._baseContext = undefined; - this._contextStream = undefined; - this._streamBindings = undefined; + if (typeof options.path !== 'string') { + options.path = "/" + name; + } - if (!this._keywords) { - this._keywords = create(null); + if (callback) { + var dsl = new DSL(name); + route(dsl, 'loading'); + route(dsl, 'error', { path: "/_unused_dummy_error_path_route_" + name + "/:error" }); + callback.call(dsl); + this.push(options.path, name, dsl.generate()); + } else { + this.push(options.path, name, null); } - this._keywords.view = new SimpleStream(); - this._keywords._view = this; - this._keywords.controller = new KeyStream(this, 'controller'); - this._setupKeywords(); - Ember.assert("Only arrays are allowed for 'classNameBindings'", typeOf(this.classNameBindings) === 'array'); - this.classNameBindings = emberA(this.classNameBindings.slice()); - Ember.assert("Only arrays are allowed for 'classNames'", typeOf(this.classNames) === 'array'); - this.classNames = emberA(this.classNames.slice()); - }, + }, - appendChild: function(view, options) { - return this.currentState.appendChild(this, view, options); + push: function(url, name, callback) { + var parts = name.split('.'); + if (url === "" || url === "/" || parts[parts.length-1] === "index") { this.explicitIndex = true; } + + this.matches.push([url, name, callback]); }, - /** - Removes the child view from the parent view. + route: function(name, options) { + Ember.assert("'basic' cannot be used as a route name.", name !== 'basic'); - @method removeChild - @param {Ember.View} view - @return {Ember.View} receiver - */ - removeChild: function(view) { - // If we're destroying, the entire subtree will be - // freed, and the DOM will be handled separately, - // so no need to mess with childViews. - if (this.isDestroying) { return; } + route(this, name, options); + }, - // update parent node - set(view, '_parentView', null); + generate: function() { + var dslMatches = this.matches; - // remove view from childViews array. - var childViews = this._childViews; + if (!this.explicitIndex) { + this.route("index", { path: "/" }); + } - removeObject(childViews, view); + return function(match) { + for (var i=0, l=dslMatches.length; i 0) { - this.opcode('repairClonedNode', blankChildTextNodes); - } - }; + /** + A hook you can use to setup the controller for the current route. - HydrationOpcodeCompiler.prototype.endProgram = function(/* program */) { - distributeMorphs(this.morphs, this.opcodes); - }; + This method is called with the controller for the current route and the + model supplied by the `model` hook. - HydrationOpcodeCompiler.prototype.text = function(/* string, pos, len */) { - ++this.currentDOMChildIndex; - }; + By default, the `setupController` hook sets the `content` property of + the controller to the `model`. - HydrationOpcodeCompiler.prototype.comment = function(/* string, pos, len */) { - ++this.currentDOMChildIndex; - }; + If you implement the `setupController` hook in your Route, it will + prevent this default behavior. If you want to preserve that behavior + when implementing your `setupController` function, make sure to call + `_super`: - HydrationOpcodeCompiler.prototype.openElement = function(element, pos, len, isSingleRoot, mustacheCount, blankChildTextNodes) { - distributeMorphs(this.morphs, this.opcodes); - ++this.currentDOMChildIndex; + ```js + App.PhotosRoute = Ember.Route.extend({ + model: function() { + return App.Photo.find(); + }, - this.element = this.currentDOMChildIndex; + setupController: function (controller, model) { + // Call _super for default behavior + this._super(controller, model); + // Implement your custom setup after + this.controllerFor('application').set('showingPhotos', true); + } + }); + ``` - if (!isSingleRoot) { - this.opcode('consumeParent', this.currentDOMChildIndex); + This means that your template will get a proxy for the model as its + context, and you can act as though the model itself was the context. - // If our parent reference will be used more than once, cache its reference. - if (mustacheCount > 1) { - this.opcode('shareElement', ++this.elementNum); - this.element = null; // Set element to null so we don't cache it twice - } - } - var isElementChecked = detectIsElementChecked(element); - if (blankChildTextNodes.length > 0 || isElementChecked) { - this.opcode( 'repairClonedNode', - blankChildTextNodes, - isElementChecked ); - } + The provided controller will be one resolved based on the name + of this route. - this.paths.push(this.currentDOMChildIndex); - this.currentDOMChildIndex = -1; + If no explicit controller is defined, Ember will automatically create + an appropriate controller for the model. - forEach(element.attributes, this.attribute, this); - forEach(element.helpers, this.elementHelper, this); - }; + * if the model is an `Ember.Array` (including record arrays from Ember + Data), the controller is an `Ember.ArrayController`. + * otherwise, the controller is an `Ember.ObjectController`. - HydrationOpcodeCompiler.prototype.closeElement = function(element, pos, len, isSingleRoot) { - distributeMorphs(this.morphs, this.opcodes); - if (!isSingleRoot) { this.opcode('popParent'); } - this.currentDOMChildIndex = this.paths.pop(); - }; + As an example, consider the router: - HydrationOpcodeCompiler.prototype.block = function(block, childIndex, childrenLength) { - var sexpr = block.sexpr; + ```js + App.Router.map(function() { + this.resource('post', {path: '/posts/:post_id'}); + }); + ``` - var currentDOMChildIndex = this.currentDOMChildIndex; - var start = (currentDOMChildIndex < 0) ? null : currentDOMChildIndex; - var end = (childIndex === childrenLength - 1) ? null : currentDOMChildIndex + 1; + For the `post` route, a controller named `App.PostController` would + be used if it is defined. If it is not defined, an `Ember.ObjectController` + instance would be used. - var morphNum = this.morphNum++; - this.morphs.push([morphNum, this.paths.slice(), start, end, true]); + Example - var templateId = this.templateId++; - var inverseId = block.inverse === null ? null : this.templateId++; + ```js + App.PostRoute = Ember.Route.extend({ + setupController: function(controller, model) { + controller.set('model', model); + } + }); + ``` - prepareSexpr(this, sexpr); - this.opcode('printBlockHook', morphNum, templateId, inverseId); - }; + @method setupController + @param {Controller} controller instance + @param {Object} model + */ + setupController: function(controller, context, transition) { + if (controller && (context !== undefined)) { + set(controller, 'model', context); + } + }, - HydrationOpcodeCompiler.prototype.component = function(component, childIndex, childrenLength) { - var currentDOMChildIndex = this.currentDOMChildIndex; - var program = component.program || {}; - var blockParams = program.blockParams || []; + /** + Returns the controller for a particular route or name. - var start = (currentDOMChildIndex < 0 ? null : currentDOMChildIndex), - end = (childIndex === childrenLength - 1 ? null : currentDOMChildIndex + 1); + The controller instance must already have been created, either through entering the + associated route or using `generateController`. - var morphNum = this.morphNum++; - this.morphs.push([morphNum, this.paths.slice(), start, end, true]); + ```js + App.PostRoute = Ember.Route.extend({ + setupController: function(controller, post) { + this._super(controller, post); + this.controllerFor('posts').set('currentPost', post); + } + }); + ``` - var attrs = component.attributes; - for (var i = attrs.length - 1; i >= 0; i--) { - var name = attrs[i].name; - var value = attrs[i].value; + @method controllerFor + @param {String} name the name of the route or controller + @return {Ember.Controller} + */ + controllerFor: function(name, _skipAssert) { + var container = this.container, + route = container.lookup('route:'+name), + controller; - // TODO: Introduce context specific AST nodes to avoid switching here. - if (value.type === 'TextNode') { - this.opcode('pushLiteral', value.chars); - } else if (value.type === 'MustacheStatement') { - this.accept(unwrapMustache(value)); - } else if (value.type === 'ConcatStatement') { - prepareParams(this, value.parts); - this.opcode('pushConcatHook'); + if (route && route.controllerName) { + name = route.controllerName; } - this.opcode('pushLiteral', name); - } + controller = container.lookup('controller:' + name); - this.opcode('prepareObject', attrs.length); - this.opcode('pushLiteral', component.tag); - this.opcode('printComponentHook', morphNum, this.templateId++, blockParams.length); - }; + // NOTE: We're specifically checking that skipAssert is true, because according + // to the old API the second parameter was model. We do not want people who + // passed a model to skip the assertion. + Ember.assert("The controller named '"+name+"' could not be found. Make sure " + + "that this route exists and has already been entered at least " + + "once. If you are accessing a controller not associated with a " + + "route, make sure the controller class is explicitly defined.", + controller || _skipAssert === true); - HydrationOpcodeCompiler.prototype.attribute = function(attr) { - var value = attr.value; - var escaped = true; + return controller; + }, - // TODO: Introduce context specific AST nodes to avoid switching here. - if (value.type === 'TextNode') { - return; - } else if (value.type === 'MustacheStatement') { - escaped = value.escaped; - this.accept(unwrapMustache(value)); - } else if (value.type === 'ConcatStatement') { - prepareParams(this, value.parts); - this.opcode('pushConcatHook'); - } + /** + Generates a controller for a route. - this.opcode('pushLiteral', attr.name); + If the optional model is passed then the controller type is determined automatically, + e.g., an ArrayController for arrays. - if (this.element !== null) { - this.opcode('shareElement', ++this.elementNum); - this.element = null; - } + Example - var attrMorphNum = this.attrMorphNum++; - this.opcode('createAttrMorph', attrMorphNum, this.elementNum, attr.name, escaped); - this.opcode('printAttributeHook', attrMorphNum, this.elementNum); - }; + ```js + App.PostRoute = Ember.Route.extend({ + setupController: function(controller, post) { + this._super(controller, post); + this.generateController('posts', post); + } + }); + ``` - HydrationOpcodeCompiler.prototype.elementHelper = function(sexpr) { - prepareSexpr(this, sexpr); + @method generateController + @param {String} name the name of the controller + @param {Object} model the model to infer the type of the controller (optional) + */ + generateController: function(name, model) { + var container = this.container; - // If we have a helper in a node, and this element has not been cached, cache it - if (this.element !== null) { - this.opcode('shareElement', ++this.elementNum); - this.element = null; // Reset element so we don't cache it more than once - } + model = model || this.modelFor(name); - this.opcode('printElementHook', this.elementNum); - }; + return generateController(container, name, model); + }, - HydrationOpcodeCompiler.prototype.mustache = function(mustache, childIndex, childrenLength) { - var sexpr = mustache.sexpr; - var currentDOMChildIndex = this.currentDOMChildIndex; + /** + Returns the model of a parent (or any ancestor) route + in a route hierarchy. During a transition, all routes + must resolve a model object, and if a route + needs access to a parent route's model in order to + resolve a model (or just reuse the model from a parent), + it can call `this.modelFor(theNameOfParentRoute)` to + retrieve it. - var start = currentDOMChildIndex, - end = (childIndex === childrenLength - 1 ? -1 : currentDOMChildIndex + 1); + Example - var morphNum = this.morphNum++; - this.morphs.push([morphNum, this.paths.slice(), start, end, mustache.escaped]); + ```js + App.Router.map(function() { + this.resource('post', { path: '/post/:post_id' }, function() { + this.resource('comments'); + }); + }); - if (isHelper(sexpr)) { - prepareSexpr(this, sexpr); - this.opcode('printInlineHook', morphNum); - } else { - preparePath(this, sexpr.path); - this.opcode('printContentHook', morphNum); - } - }; + App.CommentsRoute = Ember.Route.extend({ + afterModel: function() { + this.set('post', this.modelFor('post')); + } + }); + ``` - HydrationOpcodeCompiler.prototype.SubExpression = function(sexpr) { - prepareSexpr(this, sexpr); - this.opcode('pushSexprHook'); - }; + @method modelFor + @param {String} name the name of the route + @return {Object} the model object + */ + modelFor: function(name) { + var route = this.container.lookup('route:' + name), + transition = this.router ? this.router.router.activeTransition : null; - HydrationOpcodeCompiler.prototype.PathExpression = function(path) { - this.opcode('pushGetHook', path.original); - }; + // If we are mid-transition, we want to try and look up + // resolved parent contexts on the current transitionEvent. + if (transition) { + var modelLookupName = (route && route.routeName) || name; + if (transition.resolvedModels.hasOwnProperty(modelLookupName)) { + return transition.resolvedModels[modelLookupName]; + } + } - HydrationOpcodeCompiler.prototype.StringLiteral = function(node) { - this.opcode('pushLiteral', node.value); - }; + return route && route.currentModel; + }, - HydrationOpcodeCompiler.prototype.BooleanLiteral = function(node) { - this.opcode('pushLiteral', node.value); - }; + /** + A hook you can use to render the template for the current route. - HydrationOpcodeCompiler.prototype.NumberLiteral = function(node) { - this.opcode('pushLiteral', node.value); - }; + This method is called with the controller for the current route and the + model supplied by the `model` hook. By default, it renders the route's + template, configured with the controller for the route. - function preparePath(compiler, path) { - compiler.opcode('pushLiteral', path.original); - } + This method can be overridden to set up and render additional or + alternative templates. - function prepareParams(compiler, params) { - for (var i = params.length - 1; i >= 0; i--) { - var param = params[i]; - compiler[param.type](param); - } + ```js + App.PostsRoute = Ember.Route.extend({ + renderTemplate: function(controller, model) { + var favController = this.controllerFor('favoritePost'); - compiler.opcode('prepareArray', params.length); - } + // Render the `favoritePost` template into + // the outlet `posts`, and display the `favoritePost` + // controller. + this.render('favoritePost', { + outlet: 'posts', + controller: favController + }); + } + }); + ``` - function prepareHash(compiler, hash) { - var pairs = hash.pairs; + @method renderTemplate + @param {Object} controller the route's controller + @param {Object} model the route's model + */ + renderTemplate: function(controller, model) { + this.render(); + }, - for (var i = pairs.length - 1; i >= 0; i--) { - var key = pairs[i].key; - var value = pairs[i].value; + /** + Renders a template into an outlet. - compiler[value.type](value); - compiler.opcode('pushLiteral', key); - } + This method has a number of defaults, based on the name of the + route specified in the router. - compiler.opcode('prepareObject', pairs.length); - } + For example: - function prepareSexpr(compiler, sexpr) { - prepareHash(compiler, sexpr.hash); - prepareParams(compiler, sexpr.params); - preparePath(compiler, sexpr.path); - } + ```js + App.Router.map(function() { + this.route('index'); + this.resource('post', {path: '/posts/:post_id'}); + }); - function distributeMorphs(morphs, opcodes) { - if (morphs.length === 0) { - return; - } + App.PostRoute = App.Route.extend({ + renderTemplate: function() { + this.render(); + } + }); + ``` - // Splice morphs after the most recent shareParent/consumeParent. - var o; - for (o = opcodes.length - 1; o >= 0; --o) { - var opcode = opcodes[o][0]; - if (opcode === 'shareElement' || opcode === 'consumeParent' || opcode === 'popParent') { - break; - } - } + The name of the `PostRoute`, as defined by the router, is `post`. - var spliceArgs = [o + 1, 0]; - for (var i = 0; i < morphs.length; ++i) { - spliceArgs.push(['createMorph', morphs[i].slice()]); - } - opcodes.splice.apply(opcodes, spliceArgs); - morphs.length = 0; - } - }); -enifed("htmlbars-compiler/template-compiler", - ["./fragment-opcode-compiler","./fragment-javascript-compiler","./hydration-opcode-compiler","./hydration-javascript-compiler","./template-visitor","./utils","../htmlbars-util/quoting","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { - "use strict"; - var FragmentOpcodeCompiler = __dependency1__["default"]; - var FragmentJavaScriptCompiler = __dependency2__["default"]; - var HydrationOpcodeCompiler = __dependency3__["default"]; - var HydrationJavaScriptCompiler = __dependency4__["default"]; - var TemplateVisitor = __dependency5__["default"]; - var processOpcodes = __dependency6__.processOpcodes; - var repeat = __dependency7__.repeat; - - function TemplateCompiler(options) { - this.options = options || {}; - this.fragmentOpcodeCompiler = new FragmentOpcodeCompiler(); - this.fragmentCompiler = new FragmentJavaScriptCompiler(); - this.hydrationOpcodeCompiler = new HydrationOpcodeCompiler(); - this.hydrationCompiler = new HydrationJavaScriptCompiler(); - this.templates = []; - this.childTemplates = []; - } + By default, render will: - __exports__["default"] = TemplateCompiler; + * render the `post` template + * with the `post` view (`PostView`) for event handling, if one exists + * and the `post` controller (`PostController`), if one exists + * into the `main` outlet of the `application` template - TemplateCompiler.prototype.compile = function(ast) { - var templateVisitor = new TemplateVisitor(); - templateVisitor.visit(ast); + You can override this behavior: - processOpcodes(this, templateVisitor.actions); + ```js + App.PostRoute = App.Route.extend({ + renderTemplate: function() { + this.render('myPost', { // the template to render + into: 'index', // the template to render into + outlet: 'detail', // the name of the outlet in that template + controller: 'blogPost' // the controller to use for the template + }); + } + }); + ``` - return this.templates.pop(); - }; + Remember that the controller's `content` will be the route's model. In + this case, the default model will be `App.Post.find(params.post_id)`. - TemplateCompiler.prototype.startProgram = function(program, childTemplateCount, blankChildTextNodes) { - this.fragmentOpcodeCompiler.startProgram(program, childTemplateCount, blankChildTextNodes); - this.hydrationOpcodeCompiler.startProgram(program, childTemplateCount, blankChildTextNodes); + @method render + @param {String} name the name of the template to render + @param {Object} options the options + */ + render: function(name, options) { + Ember.assert("The name in the given arguments is undefined", arguments.length > 0 ? !isNone(arguments[0]) : true); - this.childTemplates.length = 0; - while(childTemplateCount--) { - this.childTemplates.push(this.templates.pop()); - } - }; + var namePassed = typeof name === 'string' && !!name; - TemplateCompiler.prototype.getChildTemplateVars = function(indent) { - var vars = ''; - if (this.childTemplates) { - for (var i = 0; i < this.childTemplates.length; i++) { - vars += indent + 'var child' + i + ' = ' + this.childTemplates[i] + ';\n'; + if (typeof name === 'object' && !options) { + options = name; + name = this.routeName; } - } - return vars; - }; - - TemplateCompiler.prototype.getHydrationHooks = function(indent, hooks) { - var hookVars = []; - for (var hook in hooks) { - hookVars.push(hook + ' = hooks.' + hook); - } - - if (hookVars.length > 0) { - return indent + 'var hooks = env.hooks, ' + hookVars.join(', ') + ';\n'; - } else { - return ''; - } - }; - TemplateCompiler.prototype.endProgram = function(program, programDepth) { - this.fragmentOpcodeCompiler.endProgram(program); - this.hydrationOpcodeCompiler.endProgram(program); + options = options || {}; - var indent = repeat(" ", programDepth); - var options = { - indent: indent + " " - }; + var templateName; - // function build(dom) { return fragment; } - var fragmentProgram = this.fragmentCompiler.compile( - this.fragmentOpcodeCompiler.opcodes, - options - ); + if (name) { + name = name.replace(/\//g, '.'); + templateName = name; + } else { + name = this.routeName; + templateName = this.templateName || name; + } - // function hydrate(fragment) { return mustaches; } - var hydrationProgram = this.hydrationCompiler.compile( - this.hydrationOpcodeCompiler.opcodes, - options - ); + var viewName = options.view || namePassed && name || this.viewName || name; - var blockParams = program.blockParams || []; + var container = this.container, + view = container.lookup('view:' + viewName), + template = view ? view.get('template') : null; - var templateSignature = 'context, env, contextualElement'; - if (blockParams.length > 0) { - templateSignature += ', blockArguments'; - } + if (!template) { + template = container.lookup('template:' + templateName); + } - var template = - '(function() {\n' + - this.getChildTemplateVars(indent + ' ') + - indent+' return {\n' + - indent+' isHTMLBars: true,\n' + - indent+' blockParams: ' + blockParams.length + ',\n' + - indent+' cachedFragment: null,\n' + - indent+' hasRendered: false,\n' + - indent+' build: ' + fragmentProgram + ',\n' + - indent+' render: function render(' + templateSignature + ') {\n' + - indent+' var dom = env.dom;\n' + - this.getHydrationHooks(indent + ' ', this.hydrationCompiler.hooks) + - indent+' dom.detectNamespace(contextualElement);\n' + - indent+' var fragment;\n' + - indent+' if (this.cachedFragment === null) {\n' + - indent+' fragment = this.build(dom);\n' + - indent+' if (this.hasRendered) {\n' + - indent+' this.cachedFragment = fragment;\n' + - indent+' } else {\n' + - indent+' this.hasRendered = true;\n' + - indent+' }\n' + - indent+' }\n' + - indent+' if (this.cachedFragment) {\n' + - indent+' fragment = dom.cloneNode(this.cachedFragment, true);\n' + - indent+' }\n' + - hydrationProgram + - indent+' return fragment;\n' + - indent+' }\n' + - indent+' };\n' + - indent+'}())'; + if (!view && !template) { + Ember.assert("Could not find \"" + name + "\" template or view.", Ember.isEmpty(arguments[0])); + if (get(this.router, 'namespace.LOG_VIEW_LOOKUPS')) { + Ember.Logger.info("Could not find \"" + name + "\" template or view. Nothing will be rendered", { fullName: 'template:' + name }); + } + return; + } - this.templates.push(template); - }; + options = normalizeOptions(this, name, template, options); + view = setupView(view, container, options); - TemplateCompiler.prototype.openElement = function(element, i, l, r, c, b) { - this.fragmentOpcodeCompiler.openElement(element, i, l, r, c, b); - this.hydrationOpcodeCompiler.openElement(element, i, l, r, c, b); - }; + if (options.outlet === 'main') { this.lastRenderedTemplate = name; } - TemplateCompiler.prototype.closeElement = function(element, i, l, r) { - this.fragmentOpcodeCompiler.closeElement(element, i, l, r); - this.hydrationOpcodeCompiler.closeElement(element, i, l, r); - }; + appendView(this, view, options); + }, - TemplateCompiler.prototype.component = function(component, i, l) { - this.fragmentOpcodeCompiler.component(component, i, l); - this.hydrationOpcodeCompiler.component(component, i, l); - }; + /** + Disconnects a view that has been rendered into an outlet. - TemplateCompiler.prototype.block = function(block, i, l) { - this.fragmentOpcodeCompiler.block(block, i, l); - this.hydrationOpcodeCompiler.block(block, i, l); - }; + You may pass any or all of the following options to `disconnectOutlet`: - TemplateCompiler.prototype.text = function(string, i, l, r) { - this.fragmentOpcodeCompiler.text(string, i, l, r); - this.hydrationOpcodeCompiler.text(string, i, l, r); - }; + * `outlet`: the name of the outlet to clear (default: 'main') + * `parentView`: the name of the view containing the outlet to clear + (default: the view rendered by the parent route) - TemplateCompiler.prototype.comment = function(string, i, l, r) { - this.fragmentOpcodeCompiler.comment(string, i, l, r); - this.hydrationOpcodeCompiler.comment(string, i, l, r); - }; + Example: - TemplateCompiler.prototype.mustache = function (mustache, i, l) { - this.fragmentOpcodeCompiler.mustache(mustache, i, l); - this.hydrationOpcodeCompiler.mustache(mustache, i, l); - }; + ```js + App.ApplicationRoute = App.Route.extend({ + actions: { + showModal: function(evt) { + this.render(evt.modalName, { + outlet: 'modal', + into: 'application' + }); + }, + hideModal: function(evt) { + this.disconnectOutlet({ + outlet: 'modal', + parentView: 'application' + }); + } + } + }); + ``` - TemplateCompiler.prototype.setNamespace = function(namespace) { - this.fragmentOpcodeCompiler.setNamespace(namespace); - }; - }); -enifed("htmlbars-compiler/template-visitor", - ["exports"], - function(__exports__) { - "use strict"; - var push = Array.prototype.push; + Alternatively, you can pass the `outlet` name directly as a string. - function Frame() { - this.parentNode = null; - this.children = null; - this.childIndex = null; - this.childCount = null; - this.childTemplateCount = 0; - this.mustacheCount = 0; - this.actions = []; - } + Example: - /** - * Takes in an AST and outputs a list of actions to be consumed - * by a compiler. For example, the template - * - * foo{{bar}}
    baz
    - * - * produces the actions - * - * [['startProgram', [programNode, 0]], - * ['text', [textNode, 0, 3]], - * ['mustache', [mustacheNode, 1, 3]], - * ['openElement', [elementNode, 2, 3, 0]], - * ['text', [textNode, 0, 1]], - * ['closeElement', [elementNode, 2, 3], - * ['endProgram', [programNode]]] - * - * This visitor walks the AST depth first and backwards. As - * a result the bottom-most child template will appear at the - * top of the actions list whereas the root template will appear - * at the bottom of the list. For example, - * - *
    {{#if}}foo{{else}}bar{{/if}}
    - * - * produces the actions - * - * [['startProgram', [programNode, 0]], - * ['text', [textNode, 0, 2, 0]], - * ['openElement', [elementNode, 1, 2, 0]], - * ['closeElement', [elementNode, 1, 2]], - * ['endProgram', [programNode]], - * ['startProgram', [programNode, 0]], - * ['text', [textNode, 0, 1]], - * ['endProgram', [programNode]], - * ['startProgram', [programNode, 2]], - * ['openElement', [elementNode, 0, 1, 1]], - * ['block', [blockNode, 0, 1]], - * ['closeElement', [elementNode, 0, 1]], - * ['endProgram', [programNode]]] - * - * The state of the traversal is maintained by a stack of frames. - * Whenever a node with children is entered (either a ProgramNode - * or an ElementNode) a frame is pushed onto the stack. The frame - * contains information about the state of the traversal of that - * node. For example, - * - * - index of the current child node being visited - * - the number of mustaches contained within its child nodes - * - the list of actions generated by its child nodes - */ + ```js + hideModal: function(evt) { + this.disconnectOutlet('modal'); + } + ``` - function TemplateVisitor() { - this.frameStack = []; - this.actions = []; - this.programDepth = -1; - } + @method disconnectOutlet + @param {Object|String} options the options hash or outlet name + */ + disconnectOutlet: function(options) { + if (!options || typeof options === "string") { + var outletName = options; + options = {}; + options.outlet = outletName; + } + options.parentView = options.parentView ? options.parentView.replace(/\//g, '.') : parentTemplate(this); + options.outlet = options.outlet || 'main'; - // Traversal methods + var parentView = this.router._lookupActiveView(options.parentView); + if (parentView) { parentView.disconnectOutlet(options.outlet); } + }, - TemplateVisitor.prototype.visit = function(node) { - this[node.type](node); - }; + willDestroy: function() { + this.teardownViews(); + }, - TemplateVisitor.prototype.Program = function(program) { - this.programDepth++; + /** + @private - var parentFrame = this.getCurrentFrame(); - var programFrame = this.pushFrame(); + @method teardownViews + */ + teardownViews: function() { + // Tear down the top level view + if (this.teardownTopLevelView) { this.teardownTopLevelView(); } - programFrame.parentNode = program; - programFrame.children = program.body; - programFrame.childCount = program.body.length; - programFrame.blankChildTextNodes = []; - programFrame.actions.push(['endProgram', [program, this.programDepth]]); + // Tear down any outlets rendered with 'into' + var teardownOutletViews = this.teardownOutletViews || []; + a_forEach(teardownOutletViews, function(teardownOutletView) { + teardownOutletView(); + }); - for (var i = program.body.length - 1; i >= 0; i--) { - programFrame.childIndex = i; - this.visit(program.body[i]); + delete this.teardownTopLevelView; + delete this.teardownOutletViews; + delete this.lastRenderedTemplate; } + }); - programFrame.actions.push(['startProgram', [ - program, programFrame.childTemplateCount, - programFrame.blankChildTextNodes.reverse() - ]]); - this.popFrame(); - this.programDepth--; + + function parentRoute(route) { + var handlerInfos = route.router.router.state.handlerInfos; - // Push the completed template into the global actions list - if (parentFrame) { parentFrame.childTemplateCount++; } - push.apply(this.actions, programFrame.actions.reverse()); - }; + if (!handlerInfos) { return; } - TemplateVisitor.prototype.ElementNode = function(element) { - var parentFrame = this.getCurrentFrame(); - var elementFrame = this.pushFrame(); - var parentNode = parentFrame.parentNode; + var parent, current; - elementFrame.parentNode = element; - elementFrame.children = element.children; - elementFrame.childCount = element.children.length; - elementFrame.mustacheCount += element.helpers.length; - elementFrame.blankChildTextNodes = []; + for (var i=0, l=handlerInfos.length; i= 0; i--) { - this.visit(element.attributes[i]); + if (template = parent.lastRenderedTemplate) { + return template; + } else { + return parentTemplate(parent); } + } - for (i = element.children.length - 1; i >= 0; i--) { - elementFrame.childIndex = i; - this.visit(element.children[i]); + function normalizeOptions(route, name, template, options) { + options = options || {}; + options.into = options.into ? options.into.replace(/\//g, '.') : parentTemplate(route); + options.outlet = options.outlet || 'main'; + options.name = name; + options.template = template; + options.LOG_VIEW_LOOKUPS = get(route.router, 'namespace.LOG_VIEW_LOOKUPS'); + + Ember.assert("An outlet ("+options.outlet+") was specified but was not found.", options.outlet === 'main' || options.into); + + var controller = options.controller, + model = options.model, + namedController; + + if (options.controller) { + controller = options.controller; + } else if (namedController = route.container.lookup('controller:' + name)) { + controller = namedController; + } else { + controller = route.controllerName || route.routeName; } - elementFrame.actions.push(['openElement', actionArgs.concat([ - elementFrame.mustacheCount, elementFrame.blankChildTextNodes.reverse() ])]); - this.popFrame(); - - // Propagate the element's frame state to the parent frame - if (elementFrame.mustacheCount > 0) { parentFrame.mustacheCount++; } - parentFrame.childTemplateCount += elementFrame.childTemplateCount; - push.apply(parentFrame.actions, elementFrame.actions); - }; - - TemplateVisitor.prototype.AttrNode = function(attr) { - if (attr.value.type !== 'TextNode') { - this.getCurrentFrame().mustacheCount++; + if (typeof controller === 'string') { + var controllerName = controller; + controller = route.container.lookup('controller:' + controllerName); + if (!controller) { + throw new EmberError("You passed `controller: '" + controllerName + "'` into the `render` method, but no such controller could be found."); + } } - }; - TemplateVisitor.prototype.TextNode = function(text) { - var frame = this.getCurrentFrame(); - var isSingleRoot = frame.parentNode.type === 'Program' && frame.childCount === 1; - if (text.chars === '') { - frame.blankChildTextNodes.push(domIndexOf(frame.children, text)); + if (model) { + controller.set('model', model); } - frame.actions.push(['text', [text, frame.childIndex, frame.childCount, isSingleRoot]]); - }; - - TemplateVisitor.prototype.BlockStatement = function(node) { - var frame = this.getCurrentFrame(); - - frame.mustacheCount++; - frame.actions.push(['block', [node, frame.childIndex, frame.childCount]]); - - if (node.inverse) { this.visit(node.inverse); } - if (node.program) { this.visit(node.program); } - }; - - TemplateVisitor.prototype.ComponentNode = function(node) { - var frame = this.getCurrentFrame(); - - frame.mustacheCount++; - frame.actions.push(['component', [node, frame.childIndex, frame.childCount]]); - - if (node.program) { this.visit(node.program); } - }; + options.controller = controller; - TemplateVisitor.prototype.PartialStatement = function(node) { - var frame = this.getCurrentFrame(); - frame.mustacheCount++; - frame.actions.push(['mustache', [node, frame.childIndex, frame.childCount]]); - }; - - TemplateVisitor.prototype.CommentStatement = function(text) { - var frame = this.getCurrentFrame(); - var isSingleRoot = frame.parentNode.type === 'Program' && frame.childCount === 1; - - frame.actions.push(['comment', [text, frame.childIndex, frame.childCount, isSingleRoot]]); - }; - - TemplateVisitor.prototype.MustacheStatement = function(mustache) { - var frame = this.getCurrentFrame(); - frame.mustacheCount++; - frame.actions.push(['mustache', [mustache, frame.childIndex, frame.childCount]]); - }; - - // Frame helpers - - TemplateVisitor.prototype.getCurrentFrame = function() { - return this.frameStack[this.frameStack.length - 1]; - }; - - TemplateVisitor.prototype.pushFrame = function() { - var frame = new Frame(); - this.frameStack.push(frame); - return frame; - }; - - TemplateVisitor.prototype.popFrame = function() { - return this.frameStack.pop(); - }; + return options; + } - __exports__["default"] = TemplateVisitor; + function setupView(view, container, options) { + if (view) { + if (options.LOG_VIEW_LOOKUPS) { + Ember.Logger.info("Rendering " + options.name + " with " + view, { fullName: 'view:' + options.name }); + } + } else { + var defaultView = options.into ? 'view:default' : 'view:toplevel'; + view = container.lookup(defaultView); + if (options.LOG_VIEW_LOOKUPS) { + Ember.Logger.info("Rendering " + options.name + " with default view " + view, { fullName: 'view:' + options.name }); + } + } + if (!get(view, 'templateName')) { + set(view, 'template', options.template); - // Returns the index of `domNode` in the `nodes` array, skipping - // over any nodes which do not represent DOM nodes. - function domIndexOf(nodes, domNode) { - var index = -1; + set(view, '_debugTemplateName', options.name); + } - for (var i = 0; i < nodes.length; i++) { - var node = nodes[i]; + set(view, 'renderedName', options.name); + set(view, 'controller', options.controller); - if (node.type !== 'TextNode' && node.type !== 'ElementNode') { - continue; - } else { - index++; - } + return view; + } - if (node === domNode) { - return index; + function appendView(route, view, options) { + if (options.into) { + var parentView = route.router._lookupActiveView(options.into); + var teardownOutletView = generateOutletTeardown(parentView, options.outlet); + if (!route.teardownOutletViews) { route.teardownOutletViews = []; } + a_replace(route.teardownOutletViews, 0, 0, [teardownOutletView]); + parentView.connectOutlet(options.outlet, view); + } else { + var rootElement = get(route, 'router.namespace.rootElement'); + // tear down view if one is already rendered + if (route.teardownTopLevelView) { + route.teardownTopLevelView(); } + route.router._connectActiveView(options.name, view); + route.teardownTopLevelView = generateTopLevelTeardown(view); + view.appendTo(rootElement); } + } - return -1; + function generateTopLevelTeardown(view) { + return function() { view.destroy(); }; } - }); -enifed("htmlbars-compiler/utils", - ["exports"], - function(__exports__) { - "use strict"; - function processOpcodes(compiler, opcodes) { - for (var i=0, l=opcodes.length; i 0) { - throw new Exception('Invalid path: ' + original, {loc: locInfo}); - } else if (part === '..') { - depth++; - depthString += '../'; - } - } else { - dig.push(part); - } - } + if (!handler) { + container.register(routeName, DefaultRoute.extend()); + handler = container.lookup(routeName); - return new this.PathExpression(data, depth, dig, original, locInfo); - } + if (get(self, 'namespace.LOG_ACTIVE_GENERATION')) { + Ember.Logger.info("generated -> " + routeName, { fullName: routeName }); + } + } - __exports__.preparePath = preparePath;function prepareMustache(sexpr, open, strip, locInfo) { - /*jshint -W040 */ - // Must use charAt to support IE pre-10 - var escapeFlag = open.charAt(3) || open.charAt(2), - escaped = escapeFlag !== '{' && escapeFlag !== '&'; + handler.routeName = name; + return handler; + }; + }, - return new this.MustacheStatement(sexpr, escaped, strip, this.locInfo(locInfo)); - } + _setupRouter: function(router, location) { + var lastURL, emberRouter = this; - __exports__.prepareMustache = prepareMustache;function prepareRawBlock(openRawBlock, content, close, locInfo) { - /*jshint -W040 */ - if (openRawBlock.sexpr.path.original !== close) { - var errorNode = {loc: openRawBlock.sexpr.loc}; + router.getHandler = this._getHandlerFunction(); - throw new Exception(openRawBlock.sexpr.path.original + " doesn't match " + close, errorNode); - } + var doUpdateURL = function() { + location.setURL(lastURL); + }; - locInfo = this.locInfo(locInfo); - var program = new this.Program([content], null, {}, locInfo); + router.updateURL = function(path) { + lastURL = path; + run.once(doUpdateURL); + }; - return new this.BlockStatement( - openRawBlock.sexpr, program, undefined, - {}, {}, {}, - locInfo); - } + if (location.replaceURL) { + var doReplaceURL = function() { + location.replaceURL(lastURL); + }; - __exports__.prepareRawBlock = prepareRawBlock;function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) { - /*jshint -W040 */ - // When we are chaining inverse calls, we will not have a close path - if (close && close.path && openBlock.sexpr.path.original !== close.path.original) { - var errorNode = {loc: openBlock.sexpr.loc}; + router.replaceURL = function(path) { + lastURL = path; + run.once(doReplaceURL); + }; + } - throw new Exception(openBlock.sexpr.path.original + ' doesn\'t match ' + close.path.original, errorNode); - } + router.didTransition = function(infos) { + emberRouter.didTransition(infos); + }; + }, - program.blockParams = openBlock.blockParams; + _doTransition: function(method, args) { + // Normalize blank route to root URL. + args = slice.call(args); + args[0] = args[0] || '/'; - var inverse, - inverseStrip; + var name = args[0], self = this, + isQueryParamsOnly = false, queryParams; - if (inverseAndProgram) { - if (inverseAndProgram.chain) { - inverseAndProgram.program.body[0].closeStrip = close.strip || close.openStrip; + + if (!isQueryParamsOnly && name.charAt(0) !== '/') { + Ember.assert("The route " + name + " was not found", this.router.hasRoute(name)); } - inverseStrip = inverseAndProgram.strip; - inverse = inverseAndProgram.program; - } + if (queryParams) { + // router.js expects queryParams to be passed in in + // their final serialized form, so we need to translate. - if (inverted) { - inverted = inverse; - inverse = program; - program = inverted; - } + if (!name) { + // Need to determine destination route name. + var handlerInfos = this.router.activeTransition ? + this.router.activeTransition.state.handlerInfos : + this.router.state.handlerInfos; + name = handlerInfos[handlerInfos.length - 1].name; + args.unshift(name); + } - return new this.BlockStatement( - openBlock.sexpr, program, inverse, - openBlock.strip, inverseStrip, close && (close.strip || close.openStrip), - this.locInfo(locInfo)); - } + var qpCache = this._queryParamsFor(name), qps = qpCache.qps; - __exports__.prepareBlock = prepareBlock; - }); -enifed("htmlbars-syntax/handlebars/compiler/parser", - ["exports"], - function(__exports__) { - "use strict"; - /* jshint ignore:start */ - /* istanbul ignore next */ - /* Jison generated parser */ - var handlebars = (function(){ - var parser = {trace: function trace() { }, - yy: {}, - symbols_: {"error":2,"root":3,"program":4,"EOF":5,"program_repetition0":6,"statement":7,"mustache":8,"block":9,"rawBlock":10,"partial":11,"content":12,"COMMENT":13,"CONTENT":14,"openRawBlock":15,"END_RAW_BLOCK":16,"OPEN_RAW_BLOCK":17,"sexpr":18,"CLOSE_RAW_BLOCK":19,"openBlock":20,"block_option0":21,"closeBlock":22,"openInverse":23,"block_option1":24,"OPEN_BLOCK":25,"openBlock_option0":26,"CLOSE":27,"OPEN_INVERSE":28,"openInverse_option0":29,"openInverseChain":30,"OPEN_INVERSE_CHAIN":31,"openInverseChain_option0":32,"inverseAndProgram":33,"INVERSE":34,"inverseChain":35,"inverseChain_option0":36,"OPEN_ENDBLOCK":37,"path":38,"OPEN":39,"OPEN_UNESCAPED":40,"CLOSE_UNESCAPED":41,"OPEN_PARTIAL":42,"helperName":43,"sexpr_repetition0":44,"sexpr_option0":45,"dataName":46,"param":47,"STRING":48,"NUMBER":49,"BOOLEAN":50,"OPEN_SEXPR":51,"CLOSE_SEXPR":52,"hash":53,"hash_repetition_plus0":54,"hashSegment":55,"ID":56,"EQUALS":57,"blockParams":58,"OPEN_BLOCK_PARAMS":59,"blockParams_repetition_plus0":60,"CLOSE_BLOCK_PARAMS":61,"DATA":62,"pathSegments":63,"SEP":64,"$accept":0,"$end":1}, - terminals_: {2:"error",5:"EOF",13:"COMMENT",14:"CONTENT",16:"END_RAW_BLOCK",17:"OPEN_RAW_BLOCK",19:"CLOSE_RAW_BLOCK",25:"OPEN_BLOCK",27:"CLOSE",28:"OPEN_INVERSE",31:"OPEN_INVERSE_CHAIN",34:"INVERSE",37:"OPEN_ENDBLOCK",39:"OPEN",40:"OPEN_UNESCAPED",41:"CLOSE_UNESCAPED",42:"OPEN_PARTIAL",48:"STRING",49:"NUMBER",50:"BOOLEAN",51:"OPEN_SEXPR",52:"CLOSE_SEXPR",56:"ID",57:"EQUALS",59:"OPEN_BLOCK_PARAMS",61:"CLOSE_BLOCK_PARAMS",62:"DATA",64:"SEP"}, - productions_: [0,[3,2],[4,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[12,1],[10,3],[15,3],[9,4],[9,4],[20,4],[23,4],[30,4],[33,2],[35,3],[35,1],[22,3],[8,3],[8,3],[11,3],[18,3],[18,1],[47,1],[47,1],[47,1],[47,1],[47,1],[47,3],[53,1],[55,3],[58,3],[43,1],[43,1],[43,1],[46,2],[38,1],[63,3],[63,1],[6,0],[6,2],[21,0],[21,1],[24,0],[24,1],[26,0],[26,1],[29,0],[29,1],[32,0],[32,1],[36,0],[36,1],[44,0],[44,2],[45,0],[45,1],[54,1],[54,2],[60,1],[60,2]], - performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) { - - var $0 = $$.length - 1; - switch (yystate) { - case 1: return $$[$0-1]; - break; - case 2:this.$ = new yy.Program($$[$0], null, {}, yy.locInfo(this._$)); - break; - case 3:this.$ = $$[$0]; - break; - case 4:this.$ = $$[$0]; - break; - case 5:this.$ = $$[$0]; - break; - case 6:this.$ = $$[$0]; - break; - case 7:this.$ = $$[$0]; - break; - case 8:this.$ = new yy.CommentStatement(yy.stripComment($$[$0]), yy.stripFlags($$[$0], $$[$0]), yy.locInfo(this._$)); - break; - case 9:this.$ = new yy.ContentStatement($$[$0], yy.locInfo(this._$)); - break; - case 10:this.$ = yy.prepareRawBlock($$[$0-2], $$[$0-1], $$[$0], this._$); - break; - case 11:this.$ = { sexpr: $$[$0-1] }; - break; - case 12:this.$ = yy.prepareBlock($$[$0-3], $$[$0-2], $$[$0-1], $$[$0], false, this._$); - break; - case 13:this.$ = yy.prepareBlock($$[$0-3], $$[$0-2], $$[$0-1], $$[$0], true, this._$); - break; - case 14:this.$ = { sexpr: $$[$0-2], blockParams: $$[$0-1], strip: yy.stripFlags($$[$0-3], $$[$0]) }; - break; - case 15:this.$ = { sexpr: $$[$0-2], blockParams: $$[$0-1], strip: yy.stripFlags($$[$0-3], $$[$0]) }; - break; - case 16:this.$ = { sexpr: $$[$0-2], blockParams: $$[$0-1], strip: yy.stripFlags($$[$0-3], $$[$0]) }; - break; - case 17:this.$ = { strip: yy.stripFlags($$[$0-1], $$[$0-1]), program: $$[$0] }; - break; - case 18: - var inverse = yy.prepareBlock($$[$0-2], $$[$0-1], $$[$0], $$[$0], false, this._$), - program = new yy.Program([inverse], null, {}, yy.locInfo(this._$)); - program.chained = true; - - this.$ = { strip: $$[$0-2].strip, program: program, chain: true }; - - break; - case 19:this.$ = $$[$0]; - break; - case 20:this.$ = {path: $$[$0-1], strip: yy.stripFlags($$[$0-2], $$[$0])}; - break; - case 21:this.$ = yy.prepareMustache($$[$0-1], $$[$0-2], yy.stripFlags($$[$0-2], $$[$0]), this._$); - break; - case 22:this.$ = yy.prepareMustache($$[$0-1], $$[$0-2], yy.stripFlags($$[$0-2], $$[$0]), this._$); - break; - case 23:this.$ = new yy.PartialStatement($$[$0-1], yy.stripFlags($$[$0-2], $$[$0]), yy.locInfo(this._$)); - break; - case 24:this.$ = new yy.SubExpression($$[$0-2], $$[$0-1], $$[$0], yy.locInfo(this._$)); - break; - case 25:this.$ = new yy.SubExpression($$[$0], null, null, yy.locInfo(this._$)); - break; - case 26:this.$ = $$[$0]; - break; - case 27:this.$ = new yy.StringLiteral($$[$0], yy.locInfo(this._$)); - break; - case 28:this.$ = new yy.NumberLiteral($$[$0], yy.locInfo(this._$)); - break; - case 29:this.$ = new yy.BooleanLiteral($$[$0], yy.locInfo(this._$)); - break; - case 30:this.$ = $$[$0]; - break; - case 31:this.$ = $$[$0-1]; - break; - case 32:this.$ = new yy.Hash($$[$0], yy.locInfo(this._$)); - break; - case 33:this.$ = new yy.HashPair($$[$0-2], $$[$0], yy.locInfo(this._$)); - break; - case 34:this.$ = $$[$0-1]; - break; - case 35:this.$ = $$[$0]; - break; - case 36:this.$ = new yy.StringLiteral($$[$0], yy.locInfo(this._$)), yy.locInfo(this._$); - break; - case 37:this.$ = new yy.NumberLiteral($$[$0], yy.locInfo(this._$)); - break; - case 38:this.$ = yy.preparePath(true, $$[$0], this._$); - break; - case 39:this.$ = yy.preparePath(false, $$[$0], this._$); - break; - case 40: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2]; - break; - case 41:this.$ = [{part: $$[$0]}]; - break; - case 42:this.$ = []; - break; - case 43:$$[$0-1].push($$[$0]); - break; - case 56:this.$ = []; - break; - case 57:$$[$0-1].push($$[$0]); - break; - case 60:this.$ = [$$[$0]]; - break; - case 61:$$[$0-1].push($$[$0]); - break; - case 62:this.$ = [$$[$0]]; - break; - case 63:$$[$0-1].push($$[$0]); - break; - } - }, - table: [{3:1,4:2,5:[2,42],6:3,13:[2,42],14:[2,42],17:[2,42],25:[2,42],28:[2,42],39:[2,42],40:[2,42],42:[2,42]},{1:[3]},{5:[1,4]},{5:[2,2],7:5,8:6,9:7,10:8,11:9,12:10,13:[1,11],14:[1,18],15:16,17:[1,21],20:14,23:15,25:[1,19],28:[1,20],31:[2,2],34:[2,2],37:[2,2],39:[1,12],40:[1,13],42:[1,17]},{1:[2,1]},{5:[2,43],13:[2,43],14:[2,43],17:[2,43],25:[2,43],28:[2,43],31:[2,43],34:[2,43],37:[2,43],39:[2,43],40:[2,43],42:[2,43]},{5:[2,3],13:[2,3],14:[2,3],17:[2,3],25:[2,3],28:[2,3],31:[2,3],34:[2,3],37:[2,3],39:[2,3],40:[2,3],42:[2,3]},{5:[2,4],13:[2,4],14:[2,4],17:[2,4],25:[2,4],28:[2,4],31:[2,4],34:[2,4],37:[2,4],39:[2,4],40:[2,4],42:[2,4]},{5:[2,5],13:[2,5],14:[2,5],17:[2,5],25:[2,5],28:[2,5],31:[2,5],34:[2,5],37:[2,5],39:[2,5],40:[2,5],42:[2,5]},{5:[2,6],13:[2,6],14:[2,6],17:[2,6],25:[2,6],28:[2,6],31:[2,6],34:[2,6],37:[2,6],39:[2,6],40:[2,6],42:[2,6]},{5:[2,7],13:[2,7],14:[2,7],17:[2,7],25:[2,7],28:[2,7],31:[2,7],34:[2,7],37:[2,7],39:[2,7],40:[2,7],42:[2,7]},{5:[2,8],13:[2,8],14:[2,8],17:[2,8],25:[2,8],28:[2,8],31:[2,8],34:[2,8],37:[2,8],39:[2,8],40:[2,8],42:[2,8]},{18:22,38:25,43:23,46:24,48:[1,26],49:[1,27],56:[1,30],62:[1,28],63:29},{18:31,38:25,43:23,46:24,48:[1,26],49:[1,27],56:[1,30],62:[1,28],63:29},{4:32,6:3,13:[2,42],14:[2,42],17:[2,42],25:[2,42],28:[2,42],31:[2,42],34:[2,42],37:[2,42],39:[2,42],40:[2,42],42:[2,42]},{4:33,6:3,13:[2,42],14:[2,42],17:[2,42],25:[2,42],28:[2,42],34:[2,42],37:[2,42],39:[2,42],40:[2,42],42:[2,42]},{12:34,14:[1,18]},{18:35,38:25,43:23,46:24,48:[1,26],49:[1,27],56:[1,30],62:[1,28],63:29},{5:[2,9],13:[2,9],14:[2,9],16:[2,9],17:[2,9],25:[2,9],28:[2,9],31:[2,9],34:[2,9],37:[2,9],39:[2,9],40:[2,9],42:[2,9]},{18:36,38:25,43:23,46:24,48:[1,26],49:[1,27],56:[1,30],62:[1,28],63:29},{18:37,38:25,43:23,46:24,48:[1,26],49:[1,27],56:[1,30],62:[1,28],63:29},{18:38,38:25,43:23,46:24,48:[1,26],49:[1,27],56:[1,30],62:[1,28],63:29},{27:[1,39]},{19:[2,56],27:[2,56],41:[2,56],44:40,48:[2,56],49:[2,56],50:[2,56],51:[2,56],52:[2,56],56:[2,56],59:[2,56],62:[2,56]},{19:[2,25],27:[2,25],41:[2,25],52:[2,25],59:[2,25]},{19:[2,35],27:[2,35],41:[2,35],48:[2,35],49:[2,35],50:[2,35],51:[2,35],52:[2,35],56:[2,35],59:[2,35],62:[2,35]},{19:[2,36],27:[2,36],41:[2,36],48:[2,36],49:[2,36],50:[2,36],51:[2,36],52:[2,36],56:[2,36],59:[2,36],62:[2,36]},{19:[2,37],27:[2,37],41:[2,37],48:[2,37],49:[2,37],50:[2,37],51:[2,37],52:[2,37],56:[2,37],59:[2,37],62:[2,37]},{56:[1,30],63:41},{19:[2,39],27:[2,39],41:[2,39],48:[2,39],49:[2,39],50:[2,39],51:[2,39],52:[2,39],56:[2,39],59:[2,39],62:[2,39],64:[1,42]},{19:[2,41],27:[2,41],41:[2,41],48:[2,41],49:[2,41],50:[2,41],51:[2,41],52:[2,41],56:[2,41],59:[2,41],62:[2,41],64:[2,41]},{41:[1,43]},{21:44,30:46,31:[1,48],33:47,34:[1,49],35:45,37:[2,44]},{24:50,33:51,34:[1,49],37:[2,46]},{16:[1,52]},{27:[1,53]},{26:54,27:[2,48],58:55,59:[1,56]},{27:[2,50],29:57,58:58,59:[1,56]},{19:[1,59]},{5:[2,21],13:[2,21],14:[2,21],17:[2,21],25:[2,21],28:[2,21],31:[2,21],34:[2,21],37:[2,21],39:[2,21],40:[2,21],42:[2,21]},{19:[2,58],27:[2,58],38:63,41:[2,58],45:60,46:67,47:61,48:[1,64],49:[1,65],50:[1,66],51:[1,68],52:[2,58],53:62,54:69,55:70,56:[1,71],59:[2,58],62:[1,28],63:29},{19:[2,38],27:[2,38],41:[2,38],48:[2,38],49:[2,38],50:[2,38],51:[2,38],52:[2,38],56:[2,38],59:[2,38],62:[2,38],64:[1,42]},{56:[1,72]},{5:[2,22],13:[2,22],14:[2,22],17:[2,22],25:[2,22],28:[2,22],31:[2,22],34:[2,22],37:[2,22],39:[2,22],40:[2,22],42:[2,22]},{22:73,37:[1,74]},{37:[2,45]},{4:75,6:3,13:[2,42],14:[2,42],17:[2,42],25:[2,42],28:[2,42],31:[2,42],34:[2,42],37:[2,42],39:[2,42],40:[2,42],42:[2,42]},{37:[2,19]},{18:76,38:25,43:23,46:24,48:[1,26],49:[1,27],56:[1,30],62:[1,28],63:29},{4:77,6:3,13:[2,42],14:[2,42],17:[2,42],25:[2,42],28:[2,42],37:[2,42],39:[2,42],40:[2,42],42:[2,42]},{22:78,37:[1,74]},{37:[2,47]},{5:[2,10],13:[2,10],14:[2,10],17:[2,10],25:[2,10],28:[2,10],31:[2,10],34:[2,10],37:[2,10],39:[2,10],40:[2,10],42:[2,10]},{5:[2,23],13:[2,23],14:[2,23],17:[2,23],25:[2,23],28:[2,23],31:[2,23],34:[2,23],37:[2,23],39:[2,23],40:[2,23],42:[2,23]},{27:[1,79]},{27:[2,49]},{56:[1,81],60:80},{27:[1,82]},{27:[2,51]},{14:[2,11]},{19:[2,24],27:[2,24],41:[2,24],52:[2,24],59:[2,24]},{19:[2,57],27:[2,57],41:[2,57],48:[2,57],49:[2,57],50:[2,57],51:[2,57],52:[2,57],56:[2,57],59:[2,57],62:[2,57]},{19:[2,59],27:[2,59],41:[2,59],52:[2,59],59:[2,59]},{19:[2,26],27:[2,26],41:[2,26],48:[2,26],49:[2,26],50:[2,26],51:[2,26],52:[2,26],56:[2,26],59:[2,26],62:[2,26]},{19:[2,27],27:[2,27],41:[2,27],48:[2,27],49:[2,27],50:[2,27],51:[2,27],52:[2,27],56:[2,27],59:[2,27],62:[2,27]},{19:[2,28],27:[2,28],41:[2,28],48:[2,28],49:[2,28],50:[2,28],51:[2,28],52:[2,28],56:[2,28],59:[2,28],62:[2,28]},{19:[2,29],27:[2,29],41:[2,29],48:[2,29],49:[2,29],50:[2,29],51:[2,29],52:[2,29],56:[2,29],59:[2,29],62:[2,29]},{19:[2,30],27:[2,30],41:[2,30],48:[2,30],49:[2,30],50:[2,30],51:[2,30],52:[2,30],56:[2,30],59:[2,30],62:[2,30]},{18:83,38:25,43:23,46:24,48:[1,26],49:[1,27],56:[1,30],62:[1,28],63:29},{19:[2,32],27:[2,32],41:[2,32],52:[2,32],55:84,56:[1,85],59:[2,32]},{19:[2,60],27:[2,60],41:[2,60],52:[2,60],56:[2,60],59:[2,60]},{19:[2,41],27:[2,41],41:[2,41],48:[2,41],49:[2,41],50:[2,41],51:[2,41],52:[2,41],56:[2,41],57:[1,86],59:[2,41],62:[2,41],64:[2,41]},{19:[2,40],27:[2,40],41:[2,40],48:[2,40],49:[2,40],50:[2,40],51:[2,40],52:[2,40],56:[2,40],59:[2,40],62:[2,40],64:[2,40]},{5:[2,12],13:[2,12],14:[2,12],17:[2,12],25:[2,12],28:[2,12],31:[2,12],34:[2,12],37:[2,12],39:[2,12],40:[2,12],42:[2,12]},{38:87,56:[1,30],63:29},{30:46,31:[1,48],33:47,34:[1,49],35:89,36:88,37:[2,54]},{27:[2,52],32:90,58:91,59:[1,56]},{37:[2,17]},{5:[2,13],13:[2,13],14:[2,13],17:[2,13],25:[2,13],28:[2,13],31:[2,13],34:[2,13],37:[2,13],39:[2,13],40:[2,13],42:[2,13]},{13:[2,14],14:[2,14],17:[2,14],25:[2,14],28:[2,14],31:[2,14],34:[2,14],37:[2,14],39:[2,14],40:[2,14],42:[2,14]},{56:[1,93],61:[1,92]},{56:[2,62],61:[2,62]},{13:[2,15],14:[2,15],17:[2,15],25:[2,15],28:[2,15],34:[2,15],37:[2,15],39:[2,15],40:[2,15],42:[2,15]},{52:[1,94]},{19:[2,61],27:[2,61],41:[2,61],52:[2,61],56:[2,61],59:[2,61]},{57:[1,86]},{38:63,46:67,47:95,48:[1,64],49:[1,65],50:[1,66],51:[1,68],56:[1,30],62:[1,28],63:29},{27:[1,96]},{37:[2,18]},{37:[2,55]},{27:[1,97]},{27:[2,53]},{27:[2,34]},{56:[2,63],61:[2,63]},{19:[2,31],27:[2,31],41:[2,31],48:[2,31],49:[2,31],50:[2,31],51:[2,31],52:[2,31],56:[2,31],59:[2,31],62:[2,31]},{19:[2,33],27:[2,33],41:[2,33],52:[2,33],56:[2,33],59:[2,33]},{5:[2,20],13:[2,20],14:[2,20],17:[2,20],25:[2,20],28:[2,20],31:[2,20],34:[2,20],37:[2,20],39:[2,20],40:[2,20],42:[2,20]},{13:[2,16],14:[2,16],17:[2,16],25:[2,16],28:[2,16],31:[2,16],34:[2,16],37:[2,16],39:[2,16],40:[2,16],42:[2,16]}], - defaultActions: {4:[2,1],45:[2,45],47:[2,19],51:[2,47],55:[2,49],58:[2,51],59:[2,11],77:[2,17],88:[2,18],89:[2,55],91:[2,53],92:[2,34]}, - parseError: function parseError(str, hash) { - throw new Error(str); - }, - parse: function parse(input) { - var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1; - this.lexer.setInput(input); - this.lexer.yy = this.yy; - this.yy.lexer = this.lexer; - this.yy.parser = this; - if (typeof this.lexer.yylloc == "undefined") - this.lexer.yylloc = {}; - var yyloc = this.lexer.yylloc; - lstack.push(yyloc); - var ranges = this.lexer.options && this.lexer.options.ranges; - if (typeof this.yy.parseError === "function") - this.parseError = this.yy.parseError; - function popStack(n) { - stack.length = stack.length - 2 * n; - vstack.length = vstack.length - n; - lstack.length = lstack.length - n; - } - function lex() { - var token; - token = self.lexer.lex() || 1; - if (typeof token !== "number") { - token = self.symbols_[token] || token; - } - return token; - } - var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected; - while (true) { - state = stack[stack.length - 1]; - if (this.defaultActions[state]) { - action = this.defaultActions[state]; - } else { - if (symbol === null || typeof symbol == "undefined") { - symbol = lex(); - } - action = table[state] && table[state][symbol]; - } - if (typeof action === "undefined" || !action.length || !action[0]) { - var errStr = ""; - if (!recovering) { - expected = []; - for (p in table[state]) - if (this.terminals_[p] && p > 2) { - expected.push("'" + this.terminals_[p] + "'"); - } - if (this.lexer.showPosition) { - errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; - } else { - errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'"); - } - this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); - } - } - if (action[0] instanceof Array && action.length > 1) { - throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); - } - switch (action[0]) { - case 1: - stack.push(symbol); - vstack.push(this.lexer.yytext); - lstack.push(this.lexer.yylloc); - stack.push(action[1]); - symbol = null; - if (!preErrorSymbol) { - yyleng = this.lexer.yyleng; - yytext = this.lexer.yytext; - yylineno = this.lexer.yylineno; - yyloc = this.lexer.yylloc; - if (recovering > 0) - recovering--; - } else { - symbol = preErrorSymbol; - preErrorSymbol = null; - } - break; - case 2: - len = this.productions_[action[1]][1]; - yyval.$ = vstack[vstack.length - len]; - yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column}; - if (ranges) { - yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; - } - r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); - if (typeof r !== "undefined") { - return r; - } - if (len) { - stack = stack.slice(0, -1 * len * 2); - vstack = vstack.slice(0, -1 * len); - lstack = lstack.slice(0, -1 * len); - } - stack.push(this.productions_[action[1]][0]); - vstack.push(yyval.$); - lstack.push(yyval._$); - newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; - stack.push(newState); - break; - case 3: - return true; - } - } - return true; - } - }; - /* Jison generated lexer */ - var lexer = (function(){ - var lexer = ({EOF:1, - parseError:function parseError(str, hash) { - if (this.yy.parser) { - this.yy.parser.parseError(str, hash); - } else { - throw new Error(str); - } - }, - setInput:function (input) { - this._input = input; - this._more = this._less = this.done = false; - this.yylineno = this.yyleng = 0; - this.yytext = this.matched = this.match = ''; - this.conditionStack = ['INITIAL']; - this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; - if (this.options.ranges) this.yylloc.range = [0,0]; - this.offset = 0; - return this; - }, - input:function () { - var ch = this._input[0]; - this.yytext += ch; - this.yyleng++; - this.offset++; - this.match += ch; - this.matched += ch; - var lines = ch.match(/(?:\r\n?|\n).*/g); - if (lines) { - this.yylineno++; - this.yylloc.last_line++; - } else { - this.yylloc.last_column++; + var finalParams = {}; + for (var key in queryParams) { + if (!queryParams.hasOwnProperty(key)) { continue; } + var inputValue = queryParams[key], + qp = qpCache.map[key]; + + if (!qp) { + throw new EmberError("Unrecognized query param " + key + " provided as transition argument"); } - if (this.options.ranges) this.yylloc.range[1]++; + finalParams[qp.urlKey] = qp.route.serializeQueryParam(inputValue, qp.urlKey, qp.type); + } - this._input = this._input.slice(1); - return ch; - }, - unput:function (ch) { - var len = ch.length; - var lines = ch.split(/(?:\r\n?|\n)/g); - - this._input = ch + this._input; - this.yytext = this.yytext.substr(0, this.yytext.length-len-1); - //this.yyleng -= len; - this.offset -= len; - var oldLines = this.match.split(/(?:\r\n?|\n)/g); - this.match = this.match.substr(0, this.match.length-1); - this.matched = this.matched.substr(0, this.matched.length-1); - - if (lines.length-1) this.yylineno -= lines.length-1; - var r = this.yylloc.range; - - this.yylloc = {first_line: this.yylloc.first_line, - last_line: this.yylineno+1, - first_column: this.yylloc.first_column, - last_column: lines ? - (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length: - this.yylloc.first_column - len - }; + // Perform any necessary serialization. + args[args.length-1].queryParams = finalParams; + } - if (this.options.ranges) { - this.yylloc.range = [r[0], r[0] + this.yyleng - len]; - } - return this; - }, - more:function () { - this._more = true; - return this; - }, - less:function (n) { - this.unput(this.match.slice(n)); - }, - pastInput:function () { - var past = this.matched.substr(0, this.matched.length - this.match.length); - return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); - }, - upcomingInput:function () { - var next = this.match; - if (next.length < 20) { - next += this._input.substr(0, 20-next.length); - } - return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); - }, - showPosition:function () { - var pre = this.pastInput(); - var c = new Array(pre.length + 1).join("-"); - return pre + this.upcomingInput() + "\n" + c+"^"; - }, - next:function () { - if (this.done) { - return this.EOF; - } - if (!this._input) this.done = true; - - var token, - match, - tempMatch, - index, - col, - lines; - if (!this._more) { - this.yytext = ''; - this.match = ''; - } - var rules = this._currentRules(); - for (var i=0;i < rules.length; i++) { - tempMatch = this._input.match(this.rules[rules[i]]); - if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { - match = tempMatch; - index = i; - if (!this.options.flex) break; - } - } - if (match) { - lines = match[0].match(/(?:\r\n?|\n).*/g); - if (lines) this.yylineno += lines.length; - this.yylloc = {first_line: this.yylloc.last_line, - last_line: this.yylineno+1, - first_column: this.yylloc.last_column, - last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length}; - this.yytext += match[0]; - this.match += match[0]; - this.matches = match; - this.yyleng = this.yytext.length; - if (this.options.ranges) { - this.yylloc.range = [this.offset, this.offset += this.yyleng]; - } - this._more = false; - this._input = this._input.slice(match[0].length); - this.matched += match[0]; - token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); - if (this.done && this._input) this.done = false; - if (token) return token; - else return; - } - if (this._input === "") { - return this.EOF; - } else { - return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), - {text: "", token: null, line: this.yylineno}); - } - }, - lex:function lex() { - var r = this.next(); - if (typeof r !== 'undefined') { - return r; - } else { - return this.lex(); - } - }, - begin:function begin(condition) { - this.conditionStack.push(condition); - }, - popState:function popState() { - return this.conditionStack.pop(); - }, - _currentRules:function _currentRules() { - return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; - }, - topState:function () { - return this.conditionStack[this.conditionStack.length-2]; - }, - pushState:function begin(condition) { - this.begin(condition); - }}); - lexer.options = {}; - lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { - - - function strip(start, end) { - return yy_.yytext = yy_.yytext.substr(start, yy_.yyleng-end); - } - - - var YYSTATE=YY_START - switch($avoiding_name_collisions) { - case 0: - if(yy_.yytext.slice(-2) === "\\\\") { - strip(0,1); - this.begin("mu"); - } else if(yy_.yytext.slice(-1) === "\\") { - strip(0,1); - this.begin("emu"); - } else { - this.begin("mu"); - } - if(yy_.yytext) return 14; - - break; - case 1:return 14; - break; - case 2: - this.popState(); - return 14; - - break; - case 3: - yy_.yytext = yy_.yytext.substr(5, yy_.yyleng-9); - this.popState(); - return 16; - - break; - case 4: return 14; - break; - case 5: - this.popState(); - return 13; - - break; - case 6:return 51; - break; - case 7:return 52; - break; - case 8: return 17; - break; - case 9: - this.popState(); - this.begin('raw'); - return 19; - - break; - case 10:return 42; - break; - case 11:return 25; - break; - case 12:return 37; - break; - case 13:this.popState(); return 34; - break; - case 14:this.popState(); return 34; - break; - case 15:return 28; - break; - case 16:return 31; - break; - case 17:return 40; - break; - case 18:return 39; - break; - case 19: - this.unput(yy_.yytext); - this.popState(); - this.begin('com'); - - break; - case 20: - this.popState(); - return 13; - - break; - case 21:return 39; - break; - case 22:return 57; - break; - case 23:return 56; - break; - case 24:return 56; - break; - case 25:return 64; - break; - case 26:// ignore whitespace - break; - case 27:this.popState(); return 41; - break; - case 28:this.popState(); return 27; - break; - case 29:yy_.yytext = strip(1,2).replace(/\\"/g,'"'); return 48; - break; - case 30:yy_.yytext = strip(1,2).replace(/\\'/g,"'"); return 48; - break; - case 31:return 62; - break; - case 32:return 50; - break; - case 33:return 50; - break; - case 34:return 49; - break; - case 35:return 59; - break; - case 36:return 61; - break; - case 37:return 56; - break; - case 38:yy_.yytext = strip(1,2); return 56; - break; - case 39:return 'INVALID'; - break; - case 40:return 5; - break; - } - }; - lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]*?(?=(\{\{\{\{\/)))/,/^(?:[\s\S]*?--(~)?\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{(~)?!--)/,/^(?:\{\{(~)?![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)|])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:as\s+\|)/,/^(?:\|)/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/]; - lexer.conditions = {"mu":{"rules":[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[5],"inclusive":false},"raw":{"rules":[3,4],"inclusive":false},"INITIAL":{"rules":[0,1,40],"inclusive":true}}; - return lexer;})() - parser.lexer = lexer; - function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; - return new Parser; - })();__exports__["default"] = handlebars; - /* jshint ignore:end */ - }); -enifed("htmlbars-syntax/handlebars/compiler/visitor", - ["exports"], - function(__exports__) { - "use strict"; - function Visitor() {} + var transitionPromise = this.router[method].apply(this.router, args); - Visitor.prototype = { - constructor: Visitor, + transitionPromise.then(null, function(error) { + if (!error || !error.name) { return; } - accept: function(object) { - return object && this[object.type](object); - }, + if (error.name === "UnrecognizedURLError") { + Ember.assert("The URL '" + error.message + "' did not match any routes in your application"); + } else if (error.name === 'TransitionAborted') { + // just ignore TransitionAborted here + } else { + logError(error); + } - Program: function(program) { - var body = program.body, - i, l; + return error; + }, 'Ember: Process errors from Router'); - for(i=0, l=body.length; i= 0; --i) { + var handlerInfo = handlerInfos[i], + route = handlerInfo.handler; - if (!strip) { + if (!originRouteFound) { + if (originRoute === route) { + originRouteFound = true; + } continue; } - var _isPrevWhitespace = isPrevWhitespace(body, i, isRoot), - _isNextWhitespace = isNextWhitespace(body, i, isRoot), + if (callback(route, handlerInfos[i + 1].handler) !== true) { + return false; + } + } + return true; + } + + // These get invoked when an action bubbles above ApplicationRoute + // and are not meant to be overridable. + var defaultActionHandlers = { - openStandalone = strip.openStandalone && _isPrevWhitespace, - closeStandalone = strip.closeStandalone && _isNextWhitespace, - inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace; + willResolveModel: function(transition, originRoute) { + originRoute.router._scheduleLoadingEvent(transition, originRoute); + }, - if (strip.close) { - omitRight(body, i, true); - } - if (strip.open) { - omitLeft(body, i, true); - } + error: function(error, transition, originRoute) { + // Attempt to find an appropriate error substate to enter. + var router = originRoute.router; - if (inlineStandalone) { - omitRight(body, i); + var tryTopLevel = forEachRouteAbove(originRoute, transition, function(route, childRoute) { + var childErrorRouteName = findChildRouteName(route, childRoute, 'error'); + if (childErrorRouteName) { + router.intermediateTransitionTo(childErrorRouteName, error); + return; + } + return true; + }); - if (omitLeft(body, i)) { - // If we are on a standalone node, save the indent info for partials - if (current.type === 'PartialStatement') { - // Pull out the whitespace from the final line - current.indent = (/([ \t]+$)/).exec(body[i-1].original)[1]; - } + if (tryTopLevel) { + // Check for top-level error state to enter. + if (routeHasBeenDefined(originRoute.router, 'application_error')) { + router.intermediateTransitionTo('application_error', error); + return; } + } else { + // Don't fire an assertion if we found an error substate. + return; } - if (openStandalone) { - omitRight((current.program || current.inverse).body); - // Strip out the previous content node if it's whitespace only - omitLeft(body, i); - } - if (closeStandalone) { - // Always strip the next node - omitRight(body, i); + logError(error, 'Error while processing route: ' + transition.targetName); + }, - omitLeft((current.inverse || current.program).body); - } - } + loading: function(transition, originRoute) { + // Attempt to find an appropriate loading substate to enter. + var router = originRoute.router; - return program; - }; - WhitespaceControl.prototype.BlockStatement = function(block) { - this.accept(block.program); - this.accept(block.inverse); + var tryTopLevel = forEachRouteAbove(originRoute, transition, function(route, childRoute) { + var childLoadingRouteName = findChildRouteName(route, childRoute, 'loading'); - // Find the inverse program that is involed with whitespace stripping. - var program = block.program || block.inverse, - inverse = block.program && block.inverse, - firstInverse = inverse, - lastInverse = inverse; + if (childLoadingRouteName) { + router.intermediateTransitionTo(childLoadingRouteName); + return; + } - if (inverse && inverse.chained) { - firstInverse = inverse.body[0].program; + // Don't bubble above pivot route. + if (transition.pivotHandler !== route) { + return true; + } + }); - // Walk the inverse chain to find the last inverse that is actually in the chain. - while (lastInverse.chained) { - lastInverse = lastInverse.body[lastInverse.body.length-1].program; + if (tryTopLevel) { + // Check for top-level loading state to enter. + if (routeHasBeenDefined(originRoute.router, 'application_loading')) { + router.intermediateTransitionTo('application_loading'); + return; + } } } + }; - var strip = { - open: block.openStrip.open, - close: block.closeStrip.close, - - // Determine the standalone candiacy. Basically flag our content as being possibly standalone - // so our parent can determine if we actually are standalone - openStandalone: isNextWhitespace(program.body), - closeStandalone: isPrevWhitespace((firstInverse || program).body) - }; + function logError(error, initialMessage) { + var errorArgs = []; - if (block.openStrip.close) { - omitRight(program.body, null, true); - } + if (initialMessage) { errorArgs.push(initialMessage); } - if (inverse) { - var inverseStrip = block.inverseStrip; + if (error) { + if (error.message) { errorArgs.push(error.message); } + if (error.stack) { errorArgs.push(error.stack); } - if (inverseStrip.open) { - omitLeft(program.body, null, true); - } + if (typeof error === "string") { errorArgs.push(error); } + } - if (inverseStrip.close) { - omitRight(firstInverse.body, null, true); - } - if (block.closeStrip.open) { - omitLeft(lastInverse.body, null, true); - } + Ember.Logger.error.apply(this, errorArgs); + } - // Find standalone else statments - if (isPrevWhitespace(program.body) - && isNextWhitespace(firstInverse.body)) { + function findChildRouteName(parentRoute, originatingChildRoute, name) { + var router = parentRoute.router, + childName, + targetChildRouteName = originatingChildRoute.routeName.split('.').pop(), + namespace = parentRoute.routeName === 'application' ? '' : parentRoute.routeName + '.'; - omitLeft(program.body); - omitRight(firstInverse.body); - } - } else { - if (block.closeStrip.open) { - omitLeft(program.body, null, true); - } + + // Second, try general loading state, e.g. 'loading' + childName = namespace + name; + if (routeHasBeenDefined(router, childName)) { + return childName; } + } - return strip; - }; + function routeHasBeenDefined(router, name) { + var container = router.container; + return router.hasRoute(name) && + (container.has('template:' + name) || container.has('route:' + name)); + } - WhitespaceControl.prototype.MustacheStatement = function(mustache) { - return mustache.strip; - }; + function triggerEvent(handlerInfos, ignoreFailure, args) { + var name = args.shift(); - WhitespaceControl.prototype.PartialStatement = - WhitespaceControl.prototype.CommentStatement = function(node) { - var strip = node.strip || {}; - return { - inlineStandalone: true, - open: strip.open, - close: strip.close - }; - }; + if (!handlerInfos) { + if (ignoreFailure) { return; } + throw new EmberError("Can't trigger action '" + name + "' because your app hasn't finished transitioning into its first route. To trigger an action on destination routes during a transition, you can call `.send()` on the `Transition` object passed to the `model/beforeModel/afterModel` hooks."); + } + + var eventWasHandled = false; + for (var i = handlerInfos.length - 1; i >= 0; i--) { + var handlerInfo = handlerInfos[i], + handler = handlerInfo.handler; - function isPrevWhitespace(body, i, isRoot) { - if (i === undefined) { - i = body.length; + if (handler._actions && handler._actions[name]) { + if (handler._actions[name].apply(handler, args) === true) { + eventWasHandled = true; + } else { + return; + } + } } - // Nodes that end with newlines are considered whitespace (but are special - // cased for strip operations) - var prev = body[i-1], - sibling = body[i-2]; - if (!prev) { - return isRoot; + if (defaultActionHandlers[name]) { + defaultActionHandlers[name].apply(null, args); + return; } - if (prev.type === 'ContentStatement') { - return (sibling || !isRoot ? (/\r?\n\s*?$/) : (/(^|\r?\n)\s*?$/)).test(prev.original); + if (!eventWasHandled && !ignoreFailure) { + throw new EmberError("Nothing handled the action '" + name + "'. If you did handle the action, this error can be caused by returning true from an action handler in a controller, causing the action to bubble."); } } - function isNextWhitespace(body, i, isRoot) { - if (i === undefined) { - i = -1; - } - var next = body[i+1], - sibling = body[i+2]; - if (!next) { - return isRoot; - } + function updatePaths(router) { + var appController = router.container.lookup('controller:application'); - if (next.type === 'ContentStatement') { - return (sibling || !isRoot ? (/^\s*?\r?\n/) : (/^\s*?(\r?\n|$)/)).test(next.original); + if (!appController) { + // appController might not exist when top-level loading/error + // substates have been entered since ApplicationRoute hasn't + // actually been entered at that point. + return; } - } - // Marks the node to the right of the position as omitted. - // I.e. {{foo}}' ' will mark the ' ' node as omitted. - // - // If i is undefined, then the first child will be marked as such. - // - // If mulitple is truthy then all whitespace will be stripped out until non-whitespace - // content is met. - function omitRight(body, i, multiple) { - var current = body[i == null ? 0 : i + 1]; - if (!current || current.type !== 'ContentStatement' || (!multiple && current.rightStripped)) { - return; + var infos = router.router.currentHandlerInfos, + path = EmberRouter._routePath(infos); + + if (!('currentPath' in appController)) { + defineProperty(appController, 'currentPath'); } - var original = current.value; - current.value = current.value.replace(multiple ? (/^\s+/) : (/^[ \t]*\r?\n?/), ''); - current.rightStripped = current.value !== original; - } + set(appController, 'currentPath', path); - // Marks the node to the left of the position as omitted. - // I.e. ' '{{foo}} will mark the ' ' node as omitted. - // - // If i is undefined then the last child will be marked as such. - // - // If mulitple is truthy then all whitespace will be stripped out until non-whitespace - // content is met. - function omitLeft(body, i, multiple) { - var current = body[i == null ? body.length - 1 : i - 1]; - if (!current || current.type !== 'ContentStatement' || (!multiple && current.leftStripped)) { - return; + if (!('currentRouteName' in appController)) { + defineProperty(appController, 'currentRouteName'); } - // We omit the last node if it's whitespace only and not preceeded by a non-content node. - var original = current.value; - current.value = current.value.replace(multiple ? (/\s+$/) : (/[ \t]+$/), ''); - current.leftStripped = current.value !== original; - return current.leftStripped; + set(appController, 'currentRouteName', infos[infos.length - 1].name); } - __exports__["default"] = WhitespaceControl; + EmberRouter.reopenClass({ + router: null, + map: function(callback) { + var router = this.router; + if (!router) { + router = new Router(); + router.callbacks = []; + router.triggerEvent = triggerEvent; + this.reopenClass({ router: router }); + } + + var dsl = EmberRouterDSL.map(function() { + this.resource('application', { path: "/" }, function() { + for (var i=0; i < router.callbacks.length; i++) { + router.callbacks[i].call(this); + } + callback.call(this); + }); + }); + + router.callbacks.push(callback); + router.map(dsl.generate()); + return router; + }, + + _routePath: function(handlerInfos) { + var path = []; + + // We have to handle coalescing resource names that + // are prefixed with their parent's names, e.g. + // ['foo', 'foo.bar.baz'] => 'foo.bar.baz', not 'foo.foo.bar.baz' + + function intersectionMatches(a1, a2) { + for (var i = 0, len = a1.length; i < len; ++i) { + if (a1[i] !== a2[i]) { + return false; + } + } + return true; + } + + for (var i=1, l=handlerInfos.length; i": ">", - '"': """, - "'": "'", - "`": "`" + generate: function() { + return this.string; + } }; - var badChars = /[&<>"'`]/g; - var possible = /[&<>"'`]/; + function DynamicSegment(name) { this.name = name; } + DynamicSegment.prototype = { + eachChar: function(callback) { + callback({ invalidChars: "/", repeat: true }); + }, - function escapeChar(chr) { - return escape[chr]; - } + regex: function() { + return "([^/]+)"; + }, - function extend(obj /* , ...source */) { - for (var i = 1; i < arguments.length; i++) { - for (var key in arguments[i]) { - if (Object.prototype.hasOwnProperty.call(arguments[i], key)) { - obj[key] = arguments[i][key]; - } - } + generate: function(params) { + return params[this.name]; } + }; - return obj; - } + function StarSegment(name) { this.name = name; } + StarSegment.prototype = { + eachChar: function(callback) { + callback({ invalidChars: "", repeat: true }); + }, - __exports__.extend = extend;var toString = Object.prototype.toString; - __exports__.toString = toString; - // Sourced from lodash - // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt - var isFunction = function(value) { - return typeof value === 'function'; + regex: function() { + return "(.+)"; + }, + + generate: function(params) { + return params[this.name]; + } }; - // fallback for older versions of Chrome and Safari - /* istanbul ignore next */ - if (isFunction(/x/)) { - isFunction = function(value) { - return typeof value === 'function' && toString.call(value) === '[object Function]'; - }; - } - var isFunction; - __exports__.isFunction = isFunction; - /* istanbul ignore next */ - var isArray = Array.isArray || function(value) { - return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false; + + function EpsilonSegment() {} + EpsilonSegment.prototype = { + eachChar: function() {}, + regex: function() { return ""; }, + generate: function() { return ""; } }; - __exports__.isArray = isArray; - function escapeExpression(string) { - // don't escape SafeStrings, since they're already safe - if (string && string.toHTML) { - return string.toHTML(); - } else if (string == null) { - return ""; - } else if (!string) { - return string + ''; - } + function parse(route, names, types) { + // normalize route as not starting with a "/". Recognition will + // also normalize. + if (route.charAt(0) === "/") { route = route.substr(1); } - // Force a string conversion as this will be done by the append regardless and - // the regex test will do this transparently behind the scenes, causing issues if - // an object's to string has escaped characters in it. - string = "" + string; + var segments = route.split("/"), results = []; - if(!possible.test(string)) { return string; } - return string.replace(badChars, escapeChar); - } + for (var i=0, l=segments.length; i " + n.nextStates.map(function(s) { return s.debug() }).join(" or ") + " )"; + }).join(", ") + } + END IF **/ - return path; - }, + // This is a somewhat naive strategy, but should work in a lot of cases + // A better strategy would properly resolve /posts/:id/new and /posts/edit/:id. + // + // This strategy generally prefers more static and less dynamic matching. + // Specifically, it + // + // * prefers fewer stars to more, then + // * prefers using stars for less of the match to more, then + // * prefers fewer dynamic segments to more, then + // * prefers more static segments to more + function sortSolutions(states) { + return states.sort(function(a, b) { + if (a.types.stars !== b.types.stars) { return a.types.stars - b.types.stars; } - Hash: function(hash) { - for (var i = 0; i < hash.pairs.length; i++) { - this.acceptNode(hash.pairs[i].value); + if (a.types.stars) { + if (a.types.statics !== b.types.statics) { return b.types.statics - a.types.statics; } + if (a.types.dynamics !== b.types.dynamics) { return b.types.dynamics - a.types.dynamics; } } - return hash; - }, + if (a.types.dynamics !== b.types.dynamics) { return a.types.dynamics - b.types.dynamics; } + if (a.types.statics !== b.types.statics) { return b.types.statics - a.types.statics; } - StringLiteral: function() {}, - BooleanLiteral: function() {}, - NumberLiteral: function() {} - }; + return 0; + }); + } - function switchToHandlebars(processor) { - var token = processor.tokenizer.token; + function recognizeChar(states, ch) { + var nextStates = []; - if (token && token.type === 'Chars') { - processor.acceptToken(token); - processor.tokenizer.token = null; - } - } + for (var i=0, l=states.length; i at the end. - var voidTagNames = "area base br col command embed hr img input keygen link meta param source track wbr"; - var voidMap = {}; - - forEach(voidTagNames.split(" "), function(tagName) { - voidMap[tagName] = true; - }); + for (var j=0, m=segments.length; j 0 && - (char.type === 'MustacheStatement' || value[0].type === 'MustacheStatement')) { - // Get the line number from a mustache, whether it's the one to add or the one already added - var mustache = char.type === 'MustacheStatement' ? char : value[0], - line = mustache.loc.start.line; - throw new Error("Unquoted attribute value must be a single string or mustache (line " + line + ")"); - } + return output; + }, - if (typeof char === 'object') { - if (char.type === 'MustacheStatement') { - value.push(char); - } else { - throw new Error("Unsupported node in attribute value: " + char.type); + generateQueryString: function(params, handlers) { + var pairs = []; + var keys = []; + for(var key in params) { + if (params.hasOwnProperty(key)) { + keys.push(key); + } } - } else { - if (value.length > 0 && value[value.length - 1].type === 'TextNode') { - value[value.length - 1].chars += char; - } else { - value.push(builders.text(char)); + keys.sort(); + for (var i = 0, len = keys.length; i < len; i++) { + key = keys[i]; + var value = params[key]; + if (value == null) { + continue; + } + var pair = key; + if (isArray(value)) { + for (var j = 0, l = value.length; j < l; j++) { + var arrayPair = key + '[]' + '=' + encodeURIComponent(value[j]); + pairs.push(arrayPair); + } + } else { + pair += "=" + encodeURIComponent(value); + pairs.push(pair); + } } - } - }; - - Tokenizer.prototype.finalizeAttributeValue = function() { - if (this.currentAttribute) { - this.currentAttribute.value = prepareAttributeValue(this.currentAttribute); - delete this.currentAttribute.quoted; - delete this.currentAttribute; - } - }; - - Tokenizer.prototype.addTagHelper = function(helper) { - var helpers = this.token.helpers = this.token.helpers || []; - helpers.push(helper); - }; - - function prepareAttributeValue(attr) { - var parts = attr.value; - if (parts.length === 0) { - return builders.text(''); - } else if (parts.length === 1 && parts[0].type === "TextNode") { - return parts[0]; - } else if (!attr.quoted) { - return parts[0]; - } else { - return builders.concat(parts.map(prepareConcatPart)); - } - } - - function prepareConcatPart(node) { - switch (node.type) { - case 'TextNode': return builders.string(node.chars); - case 'MustacheStatement': return unwrapMustache(node); - default: - throw new Error("Unsupported node in quoted attribute value: " + node.type); - } - } - function unwrapMustache(mustache) { - if (isHelper(mustache.sexpr)) { - return mustache.sexpr; - } else { - return mustache.sexpr.path; - } - } + if (pairs.length === 0) { return ''; } - __exports__.unwrapMustache = unwrapMustache;__exports__.Tokenizer = Tokenizer; - }); -enifed("htmlbars-syntax/utils", - ["./builders","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var buildText = __dependency1__.buildText; + return "?" + pairs.join("&"); + }, - // Regex to validate the identifier for block parameters. - // Based on the ID validation regex in Handlebars. + parseQueryString: function(queryString) { + var pairs = queryString.split("&"), queryParams = {}; + for(var i=0; i < pairs.length; i++) { + var pair = pairs[i].split('='), + key = decodeURIComponent(pair[0]), + keyLength = key.length, + isArray = false, + value; + if (pair.length === 1) { + value = 'true'; + } else { + //Handle arrays + if (keyLength > 2 && key.slice(keyLength -2) === '[]') { + isArray = true; + key = key.slice(0, keyLength - 2); + if(!queryParams[key]) { + queryParams[key] = []; + } + } + value = pair[1] ? decodeURIComponent(pair[1]) : ''; + } + if (isArray) { + queryParams[key].push(value); + } else { + queryParams[key] = decodeURIComponent(value); + } + } + return queryParams; + }, - var ID_INVERSE_PATTERN = /[!"#%-,\.\/;->@\[-\^`\{-~]/; + recognize: function(path) { + var states = [ this.rootState ], + pathLen, i, l, queryStart, queryParams = {}, + isSlashDropped = false; - // Checks the component's attributes to see if it uses block params. - // If it does, registers the block params with the program and - // removes the corresponding attributes from the element. + path = decodeURI(path); - function parseComponentBlockParams(element, program) { - var l = element.attributes.length; - var attrNames = []; + queryStart = path.indexOf('?'); + if (queryStart !== -1) { + var queryString = path.substr(queryStart + 1, path.length); + path = path.substr(0, queryStart); + queryParams = this.parseQueryString(queryString); + } - for (var i = 0; i < l; i++) { - attrNames.push(element.attributes[i].name); - } + // DEBUG GROUP path - var asIndex = attrNames.indexOf('as'); + if (path.charAt(0) !== "/") { path = "/" + path; } - if (asIndex !== -1 && l > asIndex && attrNames[asIndex + 1].charAt(0) === '|') { - // Some basic validation, since we're doing the parsing ourselves - var paramsString = attrNames.slice(asIndex).join(' '); - if (paramsString.charAt(paramsString.length - 1) !== '|' || paramsString.match(/\|/g).length !== 2) { - throw new Error('Invalid block parameters syntax: \'' + paramsString + '\''); + pathLen = path.length; + if (pathLen > 1 && path.charAt(pathLen - 1) === "/") { + path = path.substr(0, pathLen - 1); + isSlashDropped = true; } - var params = []; - for (i = asIndex + 1; i < l; i++) { - var param = attrNames[i].replace(/\|/g, ''); - if (param !== '') { - if (ID_INVERSE_PATTERN.test(param)) { - throw new Error('Invalid identifier for block parameters: \'' + param + '\' in \'' + paramsString + '\''); - } - params.push(param); - } + for (i=0, l=path.length; i 0) { - last = children[len-1]; - if (usesMorph(last) && usesMorph(node)) { - children.push(buildText('')); + if (callback) { + if (callback.length === 0) { throw new Error("You must have an argument in the function passed to `to`"); } + this.matcher.addChild(this.path, target, callback, this.delegate); } + return this; } - children.push(node); - } - - __exports__.appendChild = appendChild;function isHelper(sexpr) { - return (sexpr.params && sexpr.params.length > 0) || - (sexpr.hash && sexpr.hash.pairs.length > 0); - } + }; - __exports__.isHelper = isHelper; - }); -enifed("htmlbars-syntax/walker", - ["exports"], - function(__exports__) { - "use strict"; - function Walker(order) { - this.order = order; - this.stack = []; + function Matcher(target) { + this.routes = {}; + this.children = {}; + this.target = target; } - __exports__["default"] = Walker; - - Walker.prototype.visit = function(node, callback) { - if (!node) { - return; - } - - this.stack.push(node); - - if (this.order === 'post') { - this.children(node, callback); - callback(node, this); - } else { - callback(node, this); - this.children(node, callback); - } + Matcher.prototype = { + add: function(path, handler) { + this.routes[path] = handler; + }, - this.stack.pop(); - }; + addChild: function(path, target, callback, delegate) { + var matcher = new Matcher(target); + this.children[path] = matcher; - var visitors = { - Program: function(walker, node, callback) { - for (var i = 0; i < node.body.length; i++) { - walker.visit(node.body[i], callback); - } - }, + var match = generateMatch(path, matcher, delegate); - ElementNode: function(walker, node, callback) { - for (var i = 0; i < node.children.length; i++) { - walker.visit(node.children[i], callback); + if (delegate && delegate.contextEntered) { + delegate.contextEntered(target, match); } - }, - - BlockStatement: function(walker, node, callback) { - walker.visit(node.program, callback); - walker.visit(node.inverse, callback); - }, - ComponentNode: function(walker, node, callback) { - walker.visit(node.program, callback); + callback(match); } }; - Walker.prototype.children = function(node, callback) { - var visitor = visitors[node.type]; - if (visitor) { - visitor(this, node, callback); - } - }; - }); -enifed("htmlbars-test-helpers", - ["exports"], - function(__exports__) { - "use strict"; - function equalInnerHTML(fragment, html) { - var actualHTML = normalizeInnerHTML(fragment.innerHTML); - QUnit.push(actualHTML === html, actualHTML, html); - } + function generateMatch(startingPath, matcher, delegate) { + return function(path, nestedCallback) { + var fullPath = startingPath + path; - __exports__.equalInnerHTML = equalInnerHTML;function equalHTML(node, html) { - var fragment; - if (!node.nodeType && node.length) { - fragment = document.createDocumentFragment(); - while (node[0]) { - fragment.appendChild(node[0]); + if (nestedCallback) { + nestedCallback(generateMatch(fullPath, matcher, delegate)); + } else { + return new Target(startingPath + path, matcher, delegate); } - } else { - fragment = node; - } - - var div = document.createElement("div"); - div.appendChild(fragment.cloneNode(true)); - - equalInnerHTML(div, html); + }; } - __exports__.equalHTML = equalHTML;// detect weird IE8 html strings - var ie8InnerHTMLTestElement = document.createElement('div'); - ie8InnerHTMLTestElement.setAttribute('id', 'womp'); - var ie8InnerHTML = (ie8InnerHTMLTestElement.outerHTML.indexOf('id=womp') > -1); - function normalizeInnerHTML(actualHTML) { - if (ie8InnerHTML) { - // drop newlines in IE8 - actualHTML = actualHTML.replace(/\r\n/gm, ''); - // downcase ALLCAPS tags in IE8 - actualHTML = actualHTML.replace(/<\/?[A-Z]+/gi, function(tag){ - return tag.toLowerCase(); - }); - // quote ids in IE8 - actualHTML = actualHTML.replace(/id=([^ >]+)/gi, function(match, id){ - return 'id="'+id+'"'; - }); + function addRoute(routeArray, path, handler) { + var len = 0; + for (var i=0, l=routeArray.length; i": ">", - '"': """, - "'": "'", - "`": "`" + eachRoute([], matcher, function(route) { + if (addRouteCallback) { addRouteCallback(this, route); } + else { this.add(route); } + }, this); }; + }); - var badChars = /[&<>"'`]/g; - var possible = /[&<>"'`]/; +define("router/handler-info", + ["./utils","rsvp/promise","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var bind = __dependency1__.bind; + var merge = __dependency1__.merge; + var serialize = __dependency1__.serialize; + var promiseLabel = __dependency1__.promiseLabel; + var Promise = __dependency2__["default"]; - function escapeChar(chr) { - return escape[chr]; + function HandlerInfo(_props) { + var props = _props || {}; + merge(this, props); + this.initialize(props); } - function extend(obj /* , ...source */) { - for (var i = 1; i < arguments.length; i++) { - for (var key in arguments[i]) { - if (Object.prototype.hasOwnProperty.call(arguments[i], key)) { - obj[key] = arguments[i][key]; - } - } - } + HandlerInfo.prototype = { + name: null, + handler: null, + params: null, + context: null, - return obj; - } + // Injected by the handler info factory. + factory: null, - __exports__.extend = extend;var toString = Object.prototype.toString; - __exports__.toString = toString; - // Sourced from lodash - // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt - var isFunction = function(value) { - return typeof value === 'function'; - }; - // fallback for older versions of Chrome and Safari - /* istanbul ignore next */ - if (isFunction(/x/)) { - isFunction = function(value) { - return typeof value === 'function' && toString.call(value) === '[object Function]'; - }; - } - var isFunction; - __exports__.isFunction = isFunction; - /* istanbul ignore next */ - var isArray = Array.isArray || function(value) { - return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false; - }; - __exports__.isArray = isArray; + initialize: function() {}, - function escapeExpression(string) { - // don't escape SafeStrings, since they're already safe - if (string && string.toHTML) { - return string.toHTML(); - } else if (string == null) { - return ""; - } else if (!string) { - return string + ''; - } + log: function(payload, message) { + if (payload.log) { + payload.log(this.name + ': ' + message); + } + }, - // Force a string conversion as this will be done by the append regardless and - // the regex test will do this transparently behind the scenes, causing issues if - // an object's to string has escaped characters in it. - string = "" + string; + promiseLabel: function(label) { + return promiseLabel("'" + this.name + "' " + label); + }, - if(!possible.test(string)) { return string; } - return string.replace(badChars, escapeChar); - } + getUnresolved: function() { + return this; + }, - __exports__.escapeExpression = escapeExpression;function isEmpty(value) { - if (!value && value !== 0) { - return true; - } else if (isArray(value) && value.length === 0) { - return true; - } else { - return false; - } - } + serialize: function() { + return this.params || {}; + }, - __exports__.isEmpty = isEmpty;function appendContextPath(contextPath, id) { - return (contextPath ? contextPath + '.' : '') + id; - } + resolve: function(shouldContinue, payload) { + var checkForAbort = bind(this, this.checkForAbort, shouldContinue), + beforeModel = bind(this, this.runBeforeModelHook, payload), + model = bind(this, this.getModel, payload), + afterModel = bind(this, this.runAfterModelHook, payload), + becomeResolved = bind(this, this.becomeResolved, payload); - __exports__.appendContextPath = appendContextPath; - }); -enifed("htmlbars-util/object-utils", - ["exports"], - function(__exports__) { - "use strict"; - function merge(options, defaults) { - for (var prop in defaults) { - if (options.hasOwnProperty(prop)) { continue; } - options[prop] = defaults[prop]; - } - return options; - } + return Promise.resolve(undefined, this.promiseLabel("Start handler")) + .then(checkForAbort, null, this.promiseLabel("Check for abort")) + .then(beforeModel, null, this.promiseLabel("Before model")) + .then(checkForAbort, null, this.promiseLabel("Check if aborted during 'beforeModel' hook")) + .then(model, null, this.promiseLabel("Model")) + .then(checkForAbort, null, this.promiseLabel("Check if aborted in 'model' hook")) + .then(afterModel, null, this.promiseLabel("After model")) + .then(checkForAbort, null, this.promiseLabel("Check if aborted in 'afterModel' hook")) + .then(becomeResolved, null, this.promiseLabel("Become resolved")); + }, - __exports__.merge = merge; - }); -enifed("htmlbars-util/quoting", - ["exports"], - function(__exports__) { - "use strict"; - function escapeString(str) { - str = str.replace(/\\/g, "\\\\"); - str = str.replace(/"/g, '\\"'); - str = str.replace(/\n/g, "\\n"); - return str; - } + runBeforeModelHook: function(payload) { + if (payload.trigger) { + payload.trigger(true, 'willResolveModel', payload, this.handler); + } + return this.runSharedModelHook(payload, 'beforeModel', []); + }, - __exports__.escapeString = escapeString; + runAfterModelHook: function(payload, resolvedModel) { + // Stash the resolved model on the payload. + // This makes it possible for users to swap out + // the resolved model in afterModel. + var name = this.name; + this.stashResolvedModel(payload, resolvedModel); - function string(str) { - return '"' + escapeString(str) + '"'; - } + return this.runSharedModelHook(payload, 'afterModel', [resolvedModel]) + .then(function() { + // Ignore the fulfilled value returned from afterModel. + // Return the value stashed in resolvedModels, which + // might have been swapped out in afterModel. + return payload.resolvedModels[name]; + }, null, this.promiseLabel("Ignore fulfillment value and return model value")); + }, - __exports__.string = string; + runSharedModelHook: function(payload, hookName, args) { + this.log(payload, "calling " + hookName + " hook"); - function array(a) { - return "[" + a + "]"; - } + if (this.queryParams) { + args.push(this.queryParams); + } + args.push(payload); - __exports__.array = array; + var handler = this.handler; + var result = handler[hookName] && handler[hookName].apply(handler, args); - function hash(pairs) { - return "{" + pairs.join(", ") + "}"; - } + if (result && result.isTransition) { + result = null; + } - __exports__.hash = hash;function repeat(chars, times) { - var str = ""; - while (times--) { - str += chars; - } - return str; - } + return Promise.resolve(result, null, this.promiseLabel("Resolve value returned from one of the model hooks")); + }, - __exports__.repeat = repeat; - }); -enifed("htmlbars-util/safe-string", - ["./handlebars/safe-string","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var SafeString = __dependency1__["default"]; + // overridden by subclasses + getModel: null, - __exports__["default"] = SafeString; - }); -enifed("morph", - ["./morph/morph","./morph/attr-morph","./morph/dom-helper","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var Morph = __dependency1__["default"]; - var AttrMorph = __dependency2__["default"]; - var DOMHelper = __dependency3__["default"]; + checkForAbort: function(shouldContinue, promiseValue) { + return Promise.resolve(shouldContinue(), this.promiseLabel("Check for abort")).then(function() { + // We don't care about shouldContinue's resolve value; + // pass along the original value passed to this fn. + return promiseValue; + }, null, this.promiseLabel("Ignore fulfillment value and continue")); + }, - __exports__.Morph = Morph; - __exports__.AttrMorph = AttrMorph; - __exports__.DOMHelper = DOMHelper; - }); -enifed("morph/attr-morph", - ["./attr-morph/sanitize-attribute-value","./dom-helper/prop","./dom-helper/build-html-dom","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var sanitizeAttributeValue = __dependency1__.sanitizeAttributeValue; - var normalizeProperty = __dependency2__.normalizeProperty; - var svgNamespace = __dependency3__.svgNamespace; + stashResolvedModel: function(payload, resolvedModel) { + payload.resolvedModels = payload.resolvedModels || {}; + payload.resolvedModels[this.name] = resolvedModel; + }, - function updateProperty(value) { - this.domHelper.setPropertyStrict(this.element, this.attrName, value); - } + becomeResolved: function(payload, resolvedContext) { + var params = this.serialize(resolvedContext); - function updateAttribute(value) { - if (value === null) { - this.domHelper.removeAttribute(this.element, this.attrName); - } else { - this.domHelper.setAttribute(this.element, this.attrName, value); - } - } + if (payload) { + this.stashResolvedModel(payload, resolvedContext); + payload.params = payload.params || {}; + payload.params[this.name] = params; + } - function AttrMorph(element, attrName, domHelper) { - this.element = element; - this.domHelper = domHelper; - this.escaped = true; + return this.factory('resolved', { + context: resolvedContext, + name: this.name, + handler: this.handler, + params: params + }); + }, - var normalizedAttrName = normalizeProperty(this.element, attrName); - if (element.namespaceURI === svgNamespace || attrName === 'style' || !normalizedAttrName) { - this.attrName = attrName; - this._update = updateAttribute; - } else { - this.attrName = normalizedAttrName; - this._update = updateProperty; - } - } + shouldSupercede: function(other) { + // Prefer this newer handlerInfo over `other` if: + // 1) The other one doesn't exist + // 2) The names don't match + // 3) This handler has a context that doesn't match + // the other one (or the other one doesn't have one). + // 4) This handler has parameters that don't match the other. + if (!other) { return true; } - AttrMorph.prototype.setContent = function (value) { - if (this.escaped) { - var sanitized = sanitizeAttributeValue(this.element, this.attrName, value); - this._update(sanitized); - } else { - this._update(value); + var contextsMatch = (other.context === this.context); + return other.name !== this.name || + (this.hasOwnProperty('context') && !contextsMatch) || + (this.hasOwnProperty('params') && !paramsMatch(this.params, other.params)); } }; - __exports__["default"] = AttrMorph; - }); -enifed("morph/attr-morph/sanitize-attribute-value", - ["exports"], - function(__exports__) { - "use strict"; - /* jshint scripturl:true */ - - var parsingNode; - var badProtocols = { - 'javascript:': true, - 'vbscript:': true - }; - - var badTags = { - 'A': true, - 'BODY': true, - 'LINK': true, - 'IMG': true, - 'IFRAME': true - }; - - var badAttributes = { - 'href': true, - 'src': true, - 'background': true - }; - __exports__.badAttributes = badAttributes; - function sanitizeAttributeValue(element, attribute, value) { - var tagName; - - if (!parsingNode) { - parsingNode = document.createElement('a'); - } - - if (!element) { - tagName = null; - } else { - tagName = element.tagName; + function paramsMatch(a, b) { + if ((!a) ^ (!b)) { + // Only one is null. + return false; } - if (value && value.toHTML) { - return value.toHTML(); + if (!a) { + // Both must be null. + return true; } - if ((tagName === null || badTags[tagName]) && badAttributes[attribute]) { - parsingNode.href = value; - - if (badProtocols[parsingNode.protocol] === true) { - return 'unsafe:' + value; + // Note: this assumes that both params have the same + // number of keys, but since we're comparing the + // same handlers, they should. + for (var k in a) { + if (a.hasOwnProperty(k) && a[k] !== b[k]) { + return false; } } - - return value; + return true; } - __exports__.sanitizeAttributeValue = sanitizeAttributeValue; + __exports__["default"] = HandlerInfo; }); -enifed("morph/dom-helper", - ["../morph/morph","../morph/attr-morph","./dom-helper/build-html-dom","./dom-helper/classes","./dom-helper/prop","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { +define("router/handler-info/factory", + ["router/handler-info/resolved-handler-info","router/handler-info/unresolved-handler-info-by-object","router/handler-info/unresolved-handler-info-by-param","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; - var Morph = __dependency1__["default"]; - var AttrMorph = __dependency2__["default"]; - var buildHTMLDOM = __dependency3__.buildHTMLDOM; - var svgNamespace = __dependency3__.svgNamespace; - var svgHTMLIntegrationPoints = __dependency3__.svgHTMLIntegrationPoints; - var addClasses = __dependency4__.addClasses; - var removeClasses = __dependency4__.removeClasses; - var normalizeProperty = __dependency5__.normalizeProperty; - - var doc = typeof document === 'undefined' ? false : document; - - var deletesBlankTextNodes = doc && (function(document){ - var element = document.createElement('div'); - element.appendChild( document.createTextNode('') ); - var clonedElement = element.cloneNode(true); - return clonedElement.childNodes.length === 0; - })(doc); - - var ignoresCheckedAttribute = doc && (function(document){ - var element = document.createElement('input'); - element.setAttribute('checked', 'checked'); - var clonedElement = element.cloneNode(false); - return !clonedElement.checked; - })(doc); - - function isSVG(ns){ - return ns === svgNamespace; - } - - // This is not the namespace of the element, but of - // the elements inside that elements. - function interiorNamespace(element){ - if ( - element && - element.namespaceURI === svgNamespace && - !svgHTMLIntegrationPoints[element.tagName] - ) { - return svgNamespace; - } else { - return null; - } - } + var ResolvedHandlerInfo = __dependency1__["default"]; + var UnresolvedHandlerInfoByObject = __dependency2__["default"]; + var UnresolvedHandlerInfoByParam = __dependency3__["default"]; - // The HTML spec allows for "omitted start tags". These tags are optional - // when their intended child is the first thing in the parent tag. For - // example, this is a tbody start tag: - // - //
    - // - // - // - // The tbody may be omitted, and the browser will accept and render: - // - //
    - // - // - // However, the omitted start tag will still be added to the DOM. Here - // we test the string and context to see if the browser is about to - // perform this cleanup. - // - // http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#optional-tags - // describes which tags are omittable. The spec for tbody and colgroup - // explains this behavior: - // - // http://www.whatwg.org/specs/web-apps/current-work/multipage/tables.html#the-tbody-element - // http://www.whatwg.org/specs/web-apps/current-work/multipage/tables.html#the-colgroup-element - // + handlerInfoFactory.klasses = { + resolved: ResolvedHandlerInfo, + param: UnresolvedHandlerInfoByParam, + object: UnresolvedHandlerInfoByObject + }; - var omittedStartTagChildTest = /<([\w:]+)/; - function detectOmittedStartTag(string, contextualElement){ - // Omitted start tags are only inside table tags. - if (contextualElement.tagName === 'TABLE') { - var omittedStartTagChildMatch = omittedStartTagChildTest.exec(string); - if (omittedStartTagChildMatch) { - var omittedStartTagChild = omittedStartTagChildMatch[1]; - // It is already asserted that the contextual element is a table - // and not the proper start tag. Just see if a tag was omitted. - return omittedStartTagChild === 'tr' || - omittedStartTagChild === 'col'; - } - } + function handlerInfoFactory(name, props) { + var Ctor = handlerInfoFactory.klasses[name], + handlerInfo = new Ctor(props || {}); + handlerInfo.factory = handlerInfoFactory; + return handlerInfo; } - function buildSVGDOM(html, dom){ - var div = dom.document.createElement('div'); - div.innerHTML = ''+html+''; - return div.firstChild.childNodes; - } + __exports__["default"] = handlerInfoFactory; + }); +define("router/handler-info/resolved-handler-info", + ["../handler-info","router/utils","rsvp/promise","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var HandlerInfo = __dependency1__["default"]; + var subclass = __dependency2__.subclass; + var promiseLabel = __dependency2__.promiseLabel; + var Promise = __dependency3__["default"]; - /* - * A class wrapping DOM functions to address environment compatibility, - * namespaces, contextual elements for morph un-escaped content - * insertion. - * - * When entering a template, a DOMHelper should be passed: - * - * template(context, { hooks: hooks, dom: new DOMHelper() }); - * - * TODO: support foreignObject as a passed contextual element. It has - * a namespace (svg) that does not match its internal namespace - * (xhtml). - * - * @class DOMHelper - * @constructor - * @param {HTMLDocument} _document The document DOM methods are proxied to - */ - function DOMHelper(_document){ - this.document = _document || document; - if (!this.document) { - throw new Error("A document object must be passed to the DOMHelper, or available on the global scope"); - } - this.namespace = null; - } + var ResolvedHandlerInfo = subclass(HandlerInfo, { + resolve: function(shouldContinue, payload) { + // A ResolvedHandlerInfo just resolved with itself. + if (payload && payload.resolvedModels) { + payload.resolvedModels[this.name] = this.context; + } + return Promise.resolve(this, this.promiseLabel("Resolve")); + }, - var prototype = DOMHelper.prototype; - prototype.constructor = DOMHelper; + getUnresolved: function() { + return this.factory('param', { + name: this.name, + handler: this.handler, + params: this.params + }); + }, - prototype.getElementById = function(id, rootNode) { - rootNode = rootNode || this.document; - return rootNode.getElementById(id); - }; + isResolved: true + }); - prototype.insertBefore = function(element, childElement, referenceChild) { - return element.insertBefore(childElement, referenceChild); - }; + __exports__["default"] = ResolvedHandlerInfo; + }); +define("router/handler-info/unresolved-handler-info-by-object", + ["../handler-info","router/utils","rsvp/promise","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var HandlerInfo = __dependency1__["default"]; + var merge = __dependency2__.merge; + var subclass = __dependency2__.subclass; + var promiseLabel = __dependency2__.promiseLabel; + var isParam = __dependency2__.isParam; + var Promise = __dependency3__["default"]; - prototype.appendChild = function(element, childElement) { - return element.appendChild(childElement); - }; + var UnresolvedHandlerInfoByObject = subclass(HandlerInfo, { + getModel: function(payload) { + this.log(payload, this.name + ": resolving provided model"); + return Promise.resolve(this.context); + }, + + initialize: function(props) { + this.names = props.names || []; + this.context = props.context; + }, - prototype.childAt = function(element, indices) { - var child = element; + /** + @private - for (var i = 0; i < indices.length; i++) { - child = child.childNodes[indices[i]]; - } + Serializes a handler using its custom `serialize` method or + by a default that looks up the expected property name from + the dynamic segment. - return child; - }; + @param {Object} model the model to be serialized for this handler + */ + serialize: function(_model) { + var model = _model || this.context, + names = this.names, + handler = this.handler; - prototype.appendText = function(element, text) { - return element.appendChild(this.document.createTextNode(text)); - }; + var object = {}; + if (isParam(model)) { + object[names[0]] = model; + return object; + } - prototype.setAttribute = function(element, name, value) { - element.setAttribute(name, value); - }; + // Use custom serialize if it exists. + if (handler.serialize) { + return handler.serialize(model, names); + } - prototype.removeAttribute = function(element, name) { - element.removeAttribute(name); - }; + if (names.length !== 1) { return; } - prototype.setPropertyStrict = function(element, name, value) { - element[name] = value; - }; + var name = names[0]; - prototype.setProperty = function(element, name, value) { - var lowercaseName = name.toLowerCase(); - if (element.namespaceURI === svgNamespace || lowercaseName === 'style') { - if (value === null) { - element.removeAttribute(name); + if (/_id$/.test(name)) { + object[name] = model.id; } else { - element.setAttribute(name, value); + object[name] = model; } - } else { - var normalized = normalizeProperty(element, name); - if (normalized) { - element[normalized] = value; - } else { - if (value === null) { - element.removeAttribute(name); - } else { - element.setAttribute(name, value); - } + return object; + } + }); + + __exports__["default"] = UnresolvedHandlerInfoByObject; + }); +define("router/handler-info/unresolved-handler-info-by-param", + ["../handler-info","router/utils","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var HandlerInfo = __dependency1__["default"]; + var merge = __dependency2__.merge; + var subclass = __dependency2__.subclass; + var promiseLabel = __dependency2__.promiseLabel; + + // Generated by URL transitions and non-dynamic route segments in named Transitions. + var UnresolvedHandlerInfoByParam = subclass (HandlerInfo, { + initialize: function(props) { + this.params = props.params || {}; + }, + + getModel: function(payload) { + var fullParams = this.params; + if (payload && payload.queryParams) { + fullParams = {}; + merge(fullParams, this.params); + fullParams.queryParams = payload.queryParams; } + + var hookName = typeof this.handler.deserialize === 'function' ? + 'deserialize' : 'model'; + + return this.runSharedModelHook(payload, hookName, [fullParams]); } - }; + }); - if (doc && doc.createElementNS) { - // Only opt into namespace detection if a contextualElement - // is passed. - prototype.createElement = function(tagName, contextualElement) { - var namespace = this.namespace; - if (contextualElement) { - if (tagName === 'svg') { - namespace = svgNamespace; - } else { - namespace = interiorNamespace(contextualElement); - } - } - if (namespace) { - return this.document.createElementNS(namespace, tagName); - } else { - return this.document.createElement(tagName); - } - }; - } else { - prototype.createElement = function(tagName) { - return this.document.createElement(tagName); - }; - } + __exports__["default"] = UnresolvedHandlerInfoByParam; + }); +define("router/router", + ["route-recognizer","rsvp/promise","./utils","./transition-state","./transition","./transition-intent/named-transition-intent","./transition-intent/url-transition-intent","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; + var RouteRecognizer = __dependency1__["default"]; + var Promise = __dependency2__["default"]; + var trigger = __dependency3__.trigger; + var log = __dependency3__.log; + var slice = __dependency3__.slice; + var forEach = __dependency3__.forEach; + var merge = __dependency3__.merge; + var serialize = __dependency3__.serialize; + var extractQueryParams = __dependency3__.extractQueryParams; + var getChangelist = __dependency3__.getChangelist; + var promiseLabel = __dependency3__.promiseLabel; + var TransitionState = __dependency4__["default"]; + var logAbort = __dependency5__.logAbort; + var Transition = __dependency5__.Transition; + var TransitionAborted = __dependency5__.TransitionAborted; + var NamedTransitionIntent = __dependency6__["default"]; + var URLTransitionIntent = __dependency7__["default"]; - prototype.addClasses = addClasses; - prototype.removeClasses = removeClasses; + var pop = Array.prototype.pop; - prototype.setNamespace = function(ns) { - this.namespace = ns; - }; + function Router() { + this.recognizer = new RouteRecognizer(); + this.reset(); + } - prototype.detectNamespace = function(element) { - this.namespace = interiorNamespace(element); - }; + Router.prototype = { - prototype.createDocumentFragment = function(){ - return this.document.createDocumentFragment(); - }; + /** + The main entry point into the router. The API is essentially + the same as the `map` method in `route-recognizer`. - prototype.createTextNode = function(text){ - return this.document.createTextNode(text); - }; + This method extracts the String handler at the last `.to()` + call and uses it as the name of the whole route. - prototype.createComment = function(text){ - return this.document.createComment(text); - }; + @param {Function} callback + */ + map: function(callback) { + this.recognizer.delegate = this.delegate; - prototype.repairClonedNode = function(element, blankChildTextNodes, isChecked){ - if (deletesBlankTextNodes && blankChildTextNodes.length > 0) { - for (var i=0, len=blankChildTextNodes.length;i= 0 && proceed; --i) { + var route = routes[i]; + recognizer.add(routes, { as: route.handler }); + proceed = route.path === '/' || route.path === '' || route.handler.slice(-6) === '.index'; } - } - } - if (ignoresCheckedAttribute && isChecked) { - element.setAttribute('checked', 'checked'); - } - }; + }); + }, - prototype.cloneNode = function(element, deep){ - var clone = element.cloneNode(!!deep); - return clone; - }; + hasRoute: function(route) { + return this.recognizer.hasRoute(route); + }, - prototype.createAttrMorph = function(element, attrName){ - return new AttrMorph(element, attrName, this); - }; + // NOTE: this doesn't really belong here, but here + // it shall remain until our ES6 transpiler can + // handle cyclical deps. + transitionByIntent: function(intent, isIntermediate) { - prototype.createUnsafeAttrMorph = function(element, attrName){ - var morph = this.createAttrMorph(element, attrName); - morph.escaped = false; - return morph; - }; + var wasTransitioning = !!this.activeTransition; + var oldState = wasTransitioning ? this.activeTransition.state : this.state; + var newTransition; + var router = this; - prototype.createMorph = function(parent, start, end, contextualElement){ - if (!contextualElement && parent.nodeType === 1) { - contextualElement = parent; - } - return new Morph(parent, start, end, this, contextualElement); - }; + try { + var newState = intent.applyToState(oldState, this.recognizer, this.getHandler, isIntermediate); - prototype.createUnsafeMorph = function(parent, start, end, contextualElement){ - var morph = this.createMorph(parent, start, end, contextualElement); - morph.escaped = false; - return morph; - }; + if (handlerInfosEqual(newState.handlerInfos, oldState.handlerInfos)) { - // This helper is just to keep the templates good looking, - // passing integers instead of element references. - prototype.createMorphAt = function(parent, startIndex, endIndex, contextualElement){ - var childNodes = parent.childNodes, - start = startIndex === -1 ? null : childNodes[startIndex], - end = endIndex === -1 ? null : childNodes[endIndex]; - return this.createMorph(parent, start, end, contextualElement); - }; + // This is a no-op transition. See if query params changed. + var queryParamChangelist = getChangelist(oldState.queryParams, newState.queryParams); + if (queryParamChangelist) { - prototype.createUnsafeMorphAt = function(parent, startIndex, endIndex, contextualElement) { - var morph = this.createMorphAt(parent, startIndex, endIndex, contextualElement); - morph.escaped = false; - return morph; - }; + // This is a little hacky but we need some way of storing + // changed query params given that no activeTransition + // is guaranteed to have occurred. + this._changedQueryParams = queryParamChangelist.changed; + for (var k in queryParamChangelist.removed) { + if (queryParamChangelist.removed.hasOwnProperty(k)) { + this._changedQueryParams[k] = null; + } + } + trigger(this, newState.handlerInfos, true, ['queryParamsDidChange', queryParamChangelist.changed, queryParamChangelist.all, queryParamChangelist.removed]); + this._changedQueryParams = null; - prototype.insertMorphBefore = function(element, referenceChild, contextualElement) { - var start = this.document.createTextNode(''); - var end = this.document.createTextNode(''); - element.insertBefore(start, referenceChild); - element.insertBefore(end, referenceChild); - return this.createMorph(element, start, end, contextualElement); - }; + if (!wasTransitioning && this.activeTransition) { + // One of the handlers in queryParamsDidChange + // caused a transition. Just return that transition. + return this.activeTransition; + } else { + // Running queryParamsDidChange didn't change anything. + // Just update query params and be on our way. - prototype.appendMorph = function(element, contextualElement) { - var start = this.document.createTextNode(''); - var end = this.document.createTextNode(''); - element.appendChild(start); - element.appendChild(end); - return this.createMorph(element, start, end, contextualElement); - }; + // We have to return a noop transition that will + // perform a URL update at the end. This gives + // the user the ability to set the url update + // method (default is replaceState). + newTransition = new Transition(this); - prototype.parseHTML = function(html, contextualElement) { - var isSVGContent = ( - isSVG(this.namespace) && - !svgHTMLIntegrationPoints[contextualElement.tagName] - ); + oldState.queryParams = finalizeQueryParamChange(this, newState.handlerInfos, newState.queryParams, newTransition); - if (isSVGContent) { - return buildSVGDOM(html, this); - } else { - var nodes = buildHTMLDOM(html, contextualElement, this); - if (detectOmittedStartTag(html, contextualElement)) { - var node = nodes[0]; - while (node && node.nodeType !== 1) { - node = node.nextSibling; + newTransition.promise = newTransition.promise.then(function(result) { + updateURL(newTransition, oldState, true); + if (router.didTransition) { + router.didTransition(router.currentHandlerInfos); + } + return result; + }, null, promiseLabel("Transition complete")); + return newTransition; + } + } + + // No-op. No need to create a new transition. + return new Transition(this); } - return node.childNodes; - } else { - return nodes; - } - } - }; - __exports__["default"] = DOMHelper; - }); -enifed("morph/dom-helper/build-html-dom", - ["exports"], - function(__exports__) { - "use strict"; - /* global XMLSerializer:false */ - var svgHTMLIntegrationPoints = {foreignObject: 1, desc: 1, title: 1}; - __exports__.svgHTMLIntegrationPoints = svgHTMLIntegrationPoints;var svgNamespace = 'http://www.w3.org/2000/svg'; - __exports__.svgNamespace = svgNamespace; - var doc = typeof document === 'undefined' ? false : document; - - // Safari does not like using innerHTML on SVG HTML integration - // points (desc/title/foreignObject). - var needsIntegrationPointFix = doc && (function(document) { - if (document.createElementNS === undefined) { - return; - } - // In FF title will not accept innerHTML. - var testEl = document.createElementNS(svgNamespace, 'title'); - testEl.innerHTML = "
    "; - return testEl.childNodes.length === 0 || testEl.childNodes[0].nodeType !== 1; - })(doc); + if (isIntermediate) { + setupContexts(this, newState); + return; + } - // Internet Explorer prior to 9 does not allow setting innerHTML if the first element - // is a "zero-scope" element. This problem can be worked around by making - // the first node an invisible text node. We, like Modernizr, use ­ - var needsShy = doc && (function(document) { - var testEl = document.createElement('div'); - testEl.innerHTML = "
    "; - testEl.firstChild.innerHTML = " - - - - - - + + + + + + diff --git a/examples/emberjs/node_modules/todomvc-app-css/index.css b/examples/emberjs/node_modules/todomvc-app-css/index.css deleted file mode 100644 index 4308848800..0000000000 --- a/examples/emberjs/node_modules/todomvc-app-css/index.css +++ /dev/null @@ -1,394 +0,0 @@ -html, -body { - margin: 0; - padding: 0; -} - -button { - margin: 0; - padding: 0; - border: 0; - background: none; - font-size: 100%; - vertical-align: baseline; - font-family: inherit; - font-weight: inherit; - color: inherit; - -webkit-appearance: none; - -ms-appearance: none; - appearance: none; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; -} - -body { - font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; - line-height: 1.4em; - background: #f5f5f5; - color: #4d4d4d; - min-width: 230px; - max-width: 550px; - margin: 0 auto; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; - font-weight: 300; -} - -button, -input[type="checkbox"] { - outline: none; -} - -.hidden { - display: none; -} - -#todoapp { - background: #fff; - margin: 130px 0 40px 0; - position: relative; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), - 0 25px 50px 0 rgba(0, 0, 0, 0.1); -} - -#todoapp input::-webkit-input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp input::-moz-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp input::input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp h1 { - position: absolute; - top: -155px; - width: 100%; - font-size: 100px; - font-weight: 100; - text-align: center; - color: rgba(175, 47, 47, 0.15); - -webkit-text-rendering: optimizeLegibility; - -moz-text-rendering: optimizeLegibility; - -ms-text-rendering: optimizeLegibility; - text-rendering: optimizeLegibility; -} - -#new-todo, -.edit { - position: relative; - margin: 0; - width: 100%; - font-size: 24px; - font-family: inherit; - font-weight: inherit; - line-height: 1.4em; - border: 0; - outline: none; - color: inherit; - padding: 6px; - border: 1px solid #999; - box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); - -ms-box-sizing: border-box; - box-sizing: border-box; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; -} - -#new-todo { - padding: 16px 16px 16px 60px; - border: none; - background: rgba(0, 0, 0, 0.003); - box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); -} - -#main { - position: relative; - z-index: 2; - border-top: 1px solid #e6e6e6; -} - -label[for='toggle-all'] { - display: none; -} - -#toggle-all { - position: absolute; - top: -55px; - left: -12px; - width: 60px; - height: 34px; - text-align: center; - border: none; /* Mobile Safari */ -} - -#toggle-all:before { - content: '❯'; - font-size: 22px; - color: #e6e6e6; - padding: 10px 27px 10px 27px; -} - -#toggle-all:checked:before { - color: #737373; -} - -#todo-list { - margin: 0; - padding: 0; - list-style: none; -} - -#todo-list li { - position: relative; - font-size: 24px; - border-bottom: 1px solid #ededed; -} - -#todo-list li:last-child { - border-bottom: none; -} - -#todo-list li.editing { - border-bottom: none; - padding: 0; -} - -#todo-list li.editing .edit { - display: block; - width: 506px; - padding: 13px 17px 12px 17px; - margin: 0 0 0 43px; -} - -#todo-list li.editing .view { - display: none; -} - -#todo-list li .toggle { - text-align: center; - width: 40px; - /* auto, since non-WebKit browsers doesn't support input styling */ - height: auto; - position: absolute; - top: 0; - bottom: 0; - margin: auto 0; - border: none; /* Mobile Safari */ - -webkit-appearance: none; - -ms-appearance: none; - appearance: none; -} - -#todo-list li .toggle:after { - content: url('data:image/svg+xml;utf8,'); -} - -#todo-list li .toggle:checked:after { - content: url('data:image/svg+xml;utf8,'); -} - -#todo-list li label { - white-space: pre; - word-break: break-word; - padding: 15px 60px 15px 15px; - margin-left: 45px; - display: block; - line-height: 1.2; - transition: color 0.4s; -} - -#todo-list li.completed label { - color: #d9d9d9; - text-decoration: line-through; -} - -#todo-list li .destroy { - display: none; - position: absolute; - top: 0; - right: 10px; - bottom: 0; - width: 40px; - height: 40px; - margin: auto 0; - font-size: 30px; - color: #cc9a9a; - margin-bottom: 11px; - transition: color 0.2s ease-out; -} - -#todo-list li .destroy:hover { - color: #af5b5e; -} - -#todo-list li .destroy:after { - content: '×'; -} - -#todo-list li:hover .destroy { - display: block; -} - -#todo-list li .edit { - display: none; -} - -#todo-list li.editing:last-child { - margin-bottom: -1px; -} - -#footer { - color: #777; - padding: 10px 15px; - height: 20px; - text-align: center; - border-top: 1px solid #e6e6e6; -} - -#footer:before { - content: ''; - position: absolute; - right: 0; - bottom: 0; - left: 0; - height: 50px; - overflow: hidden; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), - 0 8px 0 -3px #f6f6f6, - 0 9px 1px -3px rgba(0, 0, 0, 0.2), - 0 16px 0 -6px #f6f6f6, - 0 17px 2px -6px rgba(0, 0, 0, 0.2); -} - -#todo-count { - float: left; - text-align: left; -} - -#todo-count strong { - font-weight: 300; -} - -#filters { - margin: 0; - padding: 0; - list-style: none; - position: absolute; - right: 0; - left: 0; -} - -#filters li { - display: inline; -} - -#filters li a { - color: inherit; - margin: 3px; - padding: 3px 7px; - text-decoration: none; - border: 1px solid transparent; - border-radius: 3px; -} - -#filters li a.selected, -#filters li a:hover { - border-color: rgba(175, 47, 47, 0.1); -} - -#filters li a.selected { - border-color: rgba(175, 47, 47, 0.2); -} - -#clear-completed, -html #clear-completed:active { - float: right; - position: relative; - line-height: 20px; - text-decoration: none; - cursor: pointer; - visibility: hidden; - position: relative; -} - -#clear-completed::after { - visibility: visible; - content: 'Clear completed'; - position: absolute; - right: 0; - white-space: nowrap; -} - -#clear-completed:hover::after { - text-decoration: underline; -} - -#info { - margin: 65px auto 0; - color: #bfbfbf; - font-size: 10px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - text-align: center; -} - -#info p { - line-height: 1; -} - -#info a { - color: inherit; - text-decoration: none; - font-weight: 400; -} - -#info a:hover { - text-decoration: underline; -} - -/* - Hack to remove background from Mobile Safari. - Can't use it globally since it destroys checkboxes in Firefox -*/ -@media screen and (-webkit-min-device-pixel-ratio:0) { - #toggle-all, - #todo-list li .toggle { - background: none; - } - - #todo-list li .toggle { - height: 40px; - } - - #toggle-all { - -webkit-transform: rotate(90deg); - transform: rotate(90deg); - -webkit-appearance: none; - appearance: none; - } -} - -@media (max-width: 430px) { - #footer { - height: 50px; - } - - #filters { - bottom: 10px; - } -} diff --git a/examples/emberjs/node_modules/todomvc-common/base.css b/examples/emberjs/node_modules/todomvc-common/base.css deleted file mode 100644 index da65968a73..0000000000 --- a/examples/emberjs/node_modules/todomvc-common/base.css +++ /dev/null @@ -1,141 +0,0 @@ -hr { - margin: 20px 0; - border: 0; - border-top: 1px dashed #c5c5c5; - border-bottom: 1px dashed #f7f7f7; -} - -.learn a { - font-weight: normal; - text-decoration: none; - color: #b83f45; -} - -.learn a:hover { - text-decoration: underline; - color: #787e7e; -} - -.learn h3, -.learn h4, -.learn h5 { - margin: 10px 0; - font-weight: 500; - line-height: 1.2; - color: #000; -} - -.learn h3 { - font-size: 24px; -} - -.learn h4 { - font-size: 18px; -} - -.learn h5 { - margin-bottom: 0; - font-size: 14px; -} - -.learn ul { - padding: 0; - margin: 0 0 30px 25px; -} - -.learn li { - line-height: 20px; -} - -.learn p { - font-size: 15px; - font-weight: 300; - line-height: 1.3; - margin-top: 0; - margin-bottom: 0; -} - -#issue-count { - display: none; -} - -.quote { - border: none; - margin: 20px 0 60px 0; -} - -.quote p { - font-style: italic; -} - -.quote p:before { - content: '“'; - font-size: 50px; - opacity: .15; - position: absolute; - top: -20px; - left: 3px; -} - -.quote p:after { - content: '”'; - font-size: 50px; - opacity: .15; - position: absolute; - bottom: -42px; - right: 3px; -} - -.quote footer { - position: absolute; - bottom: -40px; - right: 0; -} - -.quote footer img { - border-radius: 3px; -} - -.quote footer a { - margin-left: 5px; - vertical-align: middle; -} - -.speech-bubble { - position: relative; - padding: 10px; - background: rgba(0, 0, 0, .04); - border-radius: 5px; -} - -.speech-bubble:after { - content: ''; - position: absolute; - top: 100%; - right: 30px; - border: 13px solid transparent; - border-top-color: rgba(0, 0, 0, .04); -} - -.learn-bar > .learn { - position: absolute; - width: 272px; - top: 8px; - left: -300px; - padding: 10px; - border-radius: 5px; - background-color: rgba(255, 255, 255, .6); - transition-property: left; - transition-duration: 500ms; -} - -@media (min-width: 899px) { - .learn-bar { - width: auto; - padding-left: 300px; - } - - .learn-bar > .learn { - left: 8px; - } -} diff --git a/examples/emberjs/node_modules/todomvc-common/base.js b/examples/emberjs/node_modules/todomvc-common/base.js deleted file mode 100644 index 44fb50c613..0000000000 --- a/examples/emberjs/node_modules/todomvc-common/base.js +++ /dev/null @@ -1,244 +0,0 @@ -/* global _ */ -(function () { - 'use strict'; - - /* jshint ignore:start */ - // Underscore's Template Module - // Courtesy of underscorejs.org - var _ = (function (_) { - _.defaults = function (object) { - if (!object) { - return object; - } - for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { - var iterable = arguments[argsIndex]; - if (iterable) { - for (var key in iterable) { - if (object[key] == null) { - object[key] = iterable[key]; - } - } - } - } - return object; - } - - // By default, Underscore uses ERB-style template delimiters, change the - // following template settings to use alternative delimiters. - _.templateSettings = { - evaluate : /<%([\s\S]+?)%>/g, - interpolate : /<%=([\s\S]+?)%>/g, - escape : /<%-([\s\S]+?)%>/g - }; - - // When customizing `templateSettings`, if you don't want to define an - // interpolation, evaluation or escaping regex, we need one that is - // guaranteed not to match. - var noMatch = /(.)^/; - - // Certain characters need to be escaped so that they can be put into a - // string literal. - var escapes = { - "'": "'", - '\\': '\\', - '\r': 'r', - '\n': 'n', - '\t': 't', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; - - var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; - - // JavaScript micro-templating, similar to John Resig's implementation. - // Underscore templating handles arbitrary delimiters, preserves whitespace, - // and correctly escapes quotes within interpolated code. - _.template = function(text, data, settings) { - var render; - settings = _.defaults({}, settings, _.templateSettings); - - // Combine delimiters into one regular expression via alternation. - var matcher = new RegExp([ - (settings.escape || noMatch).source, - (settings.interpolate || noMatch).source, - (settings.evaluate || noMatch).source - ].join('|') + '|$', 'g'); - - // Compile the template source, escaping string literals appropriately. - var index = 0; - var source = "__p+='"; - text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { - source += text.slice(index, offset) - .replace(escaper, function(match) { return '\\' + escapes[match]; }); - - if (escape) { - source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; - } - if (interpolate) { - source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; - } - if (evaluate) { - source += "';\n" + evaluate + "\n__p+='"; - } - index = offset + match.length; - return match; - }); - source += "';\n"; - - // If a variable is not specified, place data values in local scope. - if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; - - source = "var __t,__p='',__j=Array.prototype.join," + - "print=function(){__p+=__j.call(arguments,'');};\n" + - source + "return __p;\n"; - - try { - render = new Function(settings.variable || 'obj', '_', source); - } catch (e) { - e.source = source; - throw e; - } - - if (data) return render(data, _); - var template = function(data) { - return render.call(this, data, _); - }; - - // Provide the compiled function source as a convenience for precompilation. - template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; - - return template; - }; - - return _; - })({}); - - if (location.hostname === 'todomvc.com') { - window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')); - } - /* jshint ignore:end */ - - function redirect() { - if (location.hostname === 'tastejs.github.io') { - location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com'); - } - } - - function findRoot() { - var base = location.href.indexOf('examples/'); - return location.href.substr(0, base); - } - - function getFile(file, callback) { - if (!location.host) { - return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.'); - } - - var xhr = new XMLHttpRequest(); - - xhr.open('GET', findRoot() + file, true); - xhr.send(); - - xhr.onload = function () { - if (xhr.status === 200 && callback) { - callback(xhr.responseText); - } - }; - } - - function Learn(learnJSON, config) { - if (!(this instanceof Learn)) { - return new Learn(learnJSON, config); - } - - var template, framework; - - if (typeof learnJSON !== 'object') { - try { - learnJSON = JSON.parse(learnJSON); - } catch (e) { - return; - } - } - - if (config) { - template = config.template; - framework = config.framework; - } - - if (!template && learnJSON.templates) { - template = learnJSON.templates.todomvc; - } - - if (!framework && document.querySelector('[data-framework]')) { - framework = document.querySelector('[data-framework]').dataset.framework; - } - - this.template = template; - - if (learnJSON.backend) { - this.frameworkJSON = learnJSON.backend; - this.frameworkJSON.issueLabel = framework; - this.append({ - backend: true - }); - } else if (learnJSON[framework]) { - this.frameworkJSON = learnJSON[framework]; - this.frameworkJSON.issueLabel = framework; - this.append(); - } - - this.fetchIssueCount(); - } - - Learn.prototype.append = function (opts) { - var aside = document.createElement('aside'); - aside.innerHTML = _.template(this.template, this.frameworkJSON); - aside.className = 'learn'; - - if (opts && opts.backend) { - // Remove demo link - var sourceLinks = aside.querySelector('.source-links'); - var heading = sourceLinks.firstElementChild; - var sourceLink = sourceLinks.lastElementChild; - // Correct link path - var href = sourceLink.getAttribute('href'); - sourceLink.setAttribute('href', href.substr(href.lastIndexOf('http'))); - sourceLinks.innerHTML = heading.outerHTML + sourceLink.outerHTML; - } else { - // Localize demo links - var demoLinks = aside.querySelectorAll('.demo-link'); - Array.prototype.forEach.call(demoLinks, function (demoLink) { - if (demoLink.getAttribute('href').substr(0, 4) !== 'http') { - demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href')); - } - }); - } - - document.body.className = (document.body.className + ' learn-bar').trim(); - document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); - }; - - Learn.prototype.fetchIssueCount = function () { - var issueLink = document.getElementById('issue-count-link'); - if (issueLink) { - var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos'); - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.onload = function (e) { - var parsedResponse = JSON.parse(e.target.responseText); - if (parsedResponse instanceof Array) { - var count = parsedResponse.length - if (count !== 0) { - issueLink.innerHTML = 'This app has ' + count + ' open issues'; - document.getElementById('issue-count').style.display = 'inline'; - } - } - }; - xhr.send(); - } - }; - - redirect(); - getFile('learn.json', Learn); -})(); diff --git a/examples/emberjs/package.json b/examples/emberjs/package.json deleted file mode 100644 index 06df428c91..0000000000 --- a/examples/emberjs/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "private": true, - "dependencies": { - "todomvc-app-css": "^1.0.0", - "todomvc-common": "^1.0.1", - "jquery": "^2.1.0", - "handlebars": "^2.0.0", - "ember": "components/ember#1.10.0-beta.3", - "ember-localstorage-adapter": "^0.5.0" - } -} diff --git a/examples/enyo_backbone/.gitignore b/examples/enyo_backbone/.gitignore deleted file mode 100644 index 4f8128cb95..0000000000 --- a/examples/enyo_backbone/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules/todomvc-common -!node_modules/todomvc-common/base.js -!node_modules/todomvc-common/base.css - -node_modules/todomvc-app-css -!node_modules/todomvc-app-css/index.css diff --git a/examples/enyo_backbone/bower.json b/examples/enyo_backbone/bower.json new file mode 100644 index 0000000000..c87b385c77 --- /dev/null +++ b/examples/enyo_backbone/bower.json @@ -0,0 +1,7 @@ +{ + "name": "todomvc-enyo-backbone", + "version": "0.0.0", + "dependencies": { + "todomvc-common": "~0.3.0" + } +} diff --git a/examples/enyo_backbone/bower_components/todomvc-common/base.css b/examples/enyo_backbone/bower_components/todomvc-common/base.css new file mode 100644 index 0000000000..285f531307 --- /dev/null +++ b/examples/enyo_backbone/bower_components/todomvc-common/base.css @@ -0,0 +1,554 @@ +html, +body { + margin: 0; + padding: 0; +} + +button { + margin: 0; + padding: 0; + border: 0; + background: none; + font-size: 100%; + vertical-align: baseline; + font-family: inherit; + color: inherit; + -webkit-appearance: none; + -ms-appearance: none; + -o-appearance: none; + appearance: none; +} + +body { + font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; + line-height: 1.4em; + background: #eaeaea url('bg.png'); + color: #4d4d4d; + width: 550px; + margin: 0 auto; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + -o-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +button, +input[type="checkbox"] { + outline: none; +} + +#todoapp { + background: #fff; + background: rgba(255, 255, 255, 0.9); + margin: 130px 0 40px 0; + border: 1px solid #ccc; + position: relative; + border-top-left-radius: 2px; + border-top-right-radius: 2px; + box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2), + 0 25px 50px 0 rgba(0, 0, 0, 0.15); +} + +#todoapp:before { + content: ''; + border-left: 1px solid #f5d6d6; + border-right: 1px solid #f5d6d6; + width: 2px; + position: absolute; + top: 0; + left: 40px; + height: 100%; +} + +#todoapp input::-webkit-input-placeholder { + font-style: italic; +} + +#todoapp input::-moz-placeholder { + font-style: italic; + color: #a9a9a9; +} + +#todoapp h1 { + position: absolute; + top: -120px; + width: 100%; + font-size: 70px; + font-weight: bold; + text-align: center; + color: #b3b3b3; + color: rgba(255, 255, 255, 0.3); + text-shadow: -1px -1px rgba(0, 0, 0, 0.2); + -webkit-text-rendering: optimizeLegibility; + -moz-text-rendering: optimizeLegibility; + -ms-text-rendering: optimizeLegibility; + -o-text-rendering: optimizeLegibility; + text-rendering: optimizeLegibility; +} + +#header { + padding-top: 15px; + border-radius: inherit; +} + +#header:before { + content: ''; + position: absolute; + top: 0; + right: 0; + left: 0; + height: 15px; + z-index: 2; + border-bottom: 1px solid #6c615c; + background: #8d7d77; + background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8))); + background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); + background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670'); + border-top-left-radius: 1px; + border-top-right-radius: 1px; +} + +#new-todo, +.edit { + position: relative; + margin: 0; + width: 100%; + font-size: 24px; + font-family: inherit; + line-height: 1.4em; + border: 0; + outline: none; + color: inherit; + padding: 6px; + border: 1px solid #999; + box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + -o-box-sizing: border-box; + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + -o-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +#new-todo { + padding: 16px 16px 16px 60px; + border: none; + background: rgba(0, 0, 0, 0.02); + z-index: 2; + box-shadow: none; +} + +#main { + position: relative; + z-index: 2; + border-top: 1px dotted #adadad; +} + +label[for='toggle-all'] { + display: none; +} + +#toggle-all { + position: absolute; + top: -42px; + left: -4px; + width: 40px; + text-align: center; + /* Mobile Safari */ + border: none; +} + +#toggle-all:before { + content: '»'; + font-size: 28px; + color: #d9d9d9; + padding: 0 25px 7px; +} + +#toggle-all:checked:before { + color: #737373; +} + +#todo-list { + margin: 0; + padding: 0; + list-style: none; +} + +#todo-list li { + position: relative; + font-size: 24px; + border-bottom: 1px dotted #ccc; +} + +#todo-list li:last-child { + border-bottom: none; +} + +#todo-list li.editing { + border-bottom: none; + padding: 0; +} + +#todo-list li.editing .edit { + display: block; + width: 506px; + padding: 13px 17px 12px 17px; + margin: 0 0 0 43px; +} + +#todo-list li.editing .view { + display: none; +} + +#todo-list li .toggle { + text-align: center; + width: 40px; + /* auto, since non-WebKit browsers doesn't support input styling */ + height: auto; + position: absolute; + top: 0; + bottom: 0; + margin: auto 0; + /* Mobile Safari */ + border: none; + -webkit-appearance: none; + -ms-appearance: none; + -o-appearance: none; + appearance: none; +} + +#todo-list li .toggle:after { + content: '✔'; + /* 40 + a couple of pixels visual adjustment */ + line-height: 43px; + font-size: 20px; + color: #d9d9d9; + text-shadow: 0 -1px 0 #bfbfbf; +} + +#todo-list li .toggle:checked:after { + color: #85ada7; + text-shadow: 0 1px 0 #669991; + bottom: 1px; + position: relative; +} + +#todo-list li label { + white-space: pre; + word-break: break-word; + padding: 15px 60px 15px 15px; + margin-left: 45px; + display: block; + line-height: 1.2; + -webkit-transition: color 0.4s; + transition: color 0.4s; +} + +#todo-list li.completed label { + color: #a9a9a9; + text-decoration: line-through; +} + +#todo-list li .destroy { + display: none; + position: absolute; + top: 0; + right: 10px; + bottom: 0; + width: 40px; + height: 40px; + margin: auto 0; + font-size: 22px; + color: #a88a8a; + -webkit-transition: all 0.2s; + transition: all 0.2s; +} + +#todo-list li .destroy:hover { + text-shadow: 0 0 1px #000, + 0 0 10px rgba(199, 107, 107, 0.8); + -webkit-transform: scale(1.3); + transform: scale(1.3); +} + +#todo-list li .destroy:after { + content: '✖'; +} + +#todo-list li:hover .destroy { + display: block; +} + +#todo-list li .edit { + display: none; +} + +#todo-list li.editing:last-child { + margin-bottom: -1px; +} + +#footer { + color: #777; + padding: 0 15px; + position: absolute; + right: 0; + bottom: -31px; + left: 0; + height: 20px; + z-index: 1; + text-align: center; +} + +#footer:before { + content: ''; + position: absolute; + right: 0; + bottom: 31px; + left: 0; + height: 50px; + z-index: -1; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), + 0 6px 0 -3px rgba(255, 255, 255, 0.8), + 0 7px 1px -3px rgba(0, 0, 0, 0.3), + 0 43px 0 -6px rgba(255, 255, 255, 0.8), + 0 44px 2px -6px rgba(0, 0, 0, 0.2); +} + +#todo-count { + float: left; + text-align: left; +} + +#filters { + margin: 0; + padding: 0; + list-style: none; + position: absolute; + right: 0; + left: 0; +} + +#filters li { + display: inline; +} + +#filters li a { + color: #83756f; + margin: 2px; + text-decoration: none; +} + +#filters li a.selected { + font-weight: bold; +} + +#clear-completed { + float: right; + position: relative; + line-height: 20px; + text-decoration: none; + background: rgba(0, 0, 0, 0.1); + font-size: 11px; + padding: 0 10px; + border-radius: 3px; + box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2); +} + +#clear-completed:hover { + background: rgba(0, 0, 0, 0.15); + box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3); +} + +#info { + margin: 65px auto 0; + color: #a6a6a6; + font-size: 12px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); + text-align: center; +} + +#info a { + color: inherit; +} + +/* + Hack to remove background from Mobile Safari. + Can't use it globally since it destroys checkboxes in Firefox and Opera +*/ + +@media screen and (-webkit-min-device-pixel-ratio:0) { + #toggle-all, + #todo-list li .toggle { + background: none; + } + + #todo-list li .toggle { + height: 40px; + } + + #toggle-all { + top: -56px; + left: -15px; + width: 65px; + height: 41px; + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + -webkit-appearance: none; + appearance: none; + } +} + +.hidden { + display: none; +} + +hr { + margin: 20px 0; + border: 0; + border-top: 1px dashed #C5C5C5; + border-bottom: 1px dashed #F7F7F7; +} + +.learn a { + font-weight: normal; + text-decoration: none; + color: #b83f45; +} + +.learn a:hover { + text-decoration: underline; + color: #787e7e; +} + +.learn h3, +.learn h4, +.learn h5 { + margin: 10px 0; + font-weight: 500; + line-height: 1.2; + color: #000; +} + +.learn h3 { + font-size: 24px; +} + +.learn h4 { + font-size: 18px; +} + +.learn h5 { + margin-bottom: 0; + font-size: 14px; +} + +.learn ul { + padding: 0; + margin: 0 0 30px 25px; +} + +.learn li { + line-height: 20px; +} + +.learn p { + font-size: 15px; + font-weight: 300; + line-height: 1.3; + margin-top: 0; + margin-bottom: 0; +} + +.quote { + border: none; + margin: 20px 0 60px 0; +} + +.quote p { + font-style: italic; +} + +.quote p:before { + content: '“'; + font-size: 50px; + opacity: .15; + position: absolute; + top: -20px; + left: 3px; +} + +.quote p:after { + content: '”'; + font-size: 50px; + opacity: .15; + position: absolute; + bottom: -42px; + right: 3px; +} + +.quote footer { + position: absolute; + bottom: -40px; + right: 0; +} + +.quote footer img { + border-radius: 3px; +} + +.quote footer a { + margin-left: 5px; + vertical-align: middle; +} + +.speech-bubble { + position: relative; + padding: 10px; + background: rgba(0, 0, 0, .04); + border-radius: 5px; +} + +.speech-bubble:after { + content: ''; + position: absolute; + top: 100%; + right: 30px; + border: 13px solid transparent; + border-top-color: rgba(0, 0, 0, .04); +} + +.learn-bar > .learn { + position: absolute; + width: 272px; + top: 8px; + left: -300px; + padding: 10px; + border-radius: 5px; + background-color: rgba(255, 255, 255, .6); + -webkit-transition-property: left; + transition-property: left; + -webkit-transition-duration: 500ms; + transition-duration: 500ms; +} + +@media (min-width: 899px) { + .learn-bar { + width: auto; + margin: 0 0 0 300px; + } + + .learn-bar > .learn { + left: 8px; + } + + .learn-bar #todoapp { + width: 550px; + margin: 130px auto 40px auto; + } +} diff --git a/examples/enyo_backbone/bower_components/todomvc-common/base.js b/examples/enyo_backbone/bower_components/todomvc-common/base.js new file mode 100644 index 0000000000..d3a15127d8 --- /dev/null +++ b/examples/enyo_backbone/bower_components/todomvc-common/base.js @@ -0,0 +1,217 @@ +(function () { + 'use strict'; + + // Underscore's Template Module + // Courtesy of underscorejs.org + var _ = (function (_) { + _.defaults = function (object) { + if (!object) { + return object; + } + for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { + var iterable = arguments[argsIndex]; + if (iterable) { + for (var key in iterable) { + if (object[key] == null) { + object[key] = iterable[key]; + } + } + } + } + return object; + } + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + _.template = function(text, data, settings) { + var render; + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = new RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset) + .replace(escaper, function(match) { return '\\' + escapes[match]; }); + + if (escape) { + source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; + } + if (interpolate) { + source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; + } + if (evaluate) { + source += "';\n" + evaluate + "\n__p+='"; + } + index = offset + match.length; + return match; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + "return __p;\n"; + + try { + render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + if (data) return render(data, _); + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled function source as a convenience for precompilation. + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + + return template; + }; + + return _; + })({}); + + if (location.hostname === 'todomvc.com') { + window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')); + } + + function redirect() { + if (location.hostname === 'tastejs.github.io') { + location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com'); + } + } + + function findRoot() { + var base = location.href.indexOf('examples/'); + return location.href.substr(0, base); + } + + function getFile(file, callback) { + if (!location.host) { + return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.'); + } + + var xhr = new XMLHttpRequest(); + + xhr.open('GET', findRoot() + file, true); + xhr.send(); + + xhr.onload = function () { + if (xhr.status === 200 && callback) { + callback(xhr.responseText); + } + }; + } + + function Learn(learnJSON, config) { + if (!(this instanceof Learn)) { + return new Learn(learnJSON, config); + } + + var template, framework; + + if (typeof learnJSON !== 'object') { + try { + learnJSON = JSON.parse(learnJSON); + } catch (e) { + return; + } + } + + if (config) { + template = config.template; + framework = config.framework; + } + + if (!template && learnJSON.templates) { + template = learnJSON.templates.todomvc; + } + + if (!framework && document.querySelector('[data-framework]')) { + framework = document.querySelector('[data-framework]').dataset.framework; + } + + this.template = template; + + if (learnJSON.backend) { + this.frameworkJSON = learnJSON.backend; + this.append({ + backend: true + }); + } else if (learnJSON[framework]) { + this.frameworkJSON = learnJSON[framework]; + this.append(); + } + } + + Learn.prototype.append = function (opts) { + var aside = document.createElement('aside'); + aside.innerHTML = _.template(this.template, this.frameworkJSON); + aside.className = 'learn'; + + if (opts && opts.backend) { + // Remove demo link + var sourceLinks = aside.querySelector('.source-links'); + var heading = sourceLinks.firstElementChild; + var sourceLink = sourceLinks.lastElementChild; + // Correct link path + var href = sourceLink.getAttribute('href'); + sourceLink.setAttribute('href', href.substr(href.lastIndexOf('http'))); + sourceLinks.innerHTML = heading.outerHTML + sourceLink.outerHTML; + } else { + // Localize demo links + var demoLinks = aside.querySelectorAll('.demo-link'); + Array.prototype.forEach.call(demoLinks, function (demoLink) { + if (demoLink.getAttribute('href').substr(0, 4) !== 'http') { + demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href')); + } + }); + } + + document.body.className = (document.body.className + ' learn-bar').trim(); + document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); + }; + + redirect(); + getFile('learn.json', Learn); +})(); diff --git a/examples/enyo_backbone/bower_components/todomvc-common/bg.png b/examples/enyo_backbone/bower_components/todomvc-common/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..b2a7600825ee11f21849ed475499c7d1ecbcc870 GIT binary patch literal 2126 zcmV-U2(kBxP)+9y`=HK7nt=~3t000O5Nklm=04hVa_lXl_bm=+M;=eI<%$rO2ta`suh*Sr#POw*?adq!O!CV z*0&b)rkCCEV)1nIuiLBntUH-MJI;&qYgz|d@Nhnn_abi2g`pu4NAMVid3hS%?quz~ zjlJf(x8cj{VS zUVEP1msg9`^OFUhSO5rtXHhRlyU2i>6$BT=glG`{JlctGHrwRIm)6Buu65jLQn^H8 zvl9lZqUBA+%qvM5XC+yY@mfA(YB~XP=e|6<{%$^eU8nrK?v2ccb@Jom5CG>rKAL7J zplHEIavVp|0p1&m!G`Ti?<7iZ;}@G7#Z)}Km%2!FHnQ0*&?PLR)G!HAaHgU#c|$dz zpj#0HmeadIe>`#)=Jjkb{B=FKnU3pc-&Y@j_j6(h4Y~+Uq!^J=LHunx1xi4QXK-Y{&MoT8ky6Vb9c|WhnOwF~c=3zOy8agktliS6# z`#8F`9H)D=bmk9B5MnW&_r#)f$c+;$LSr-@^An8dhc~Iquuv>jOK7pw7LJ;&X0i1C zGMsHdP1Os@ny$$j!>XAii%7bp5k>`pyNA!~epb)()p9zR4Yl6z=U}{CIdh1z(FpAo zQIW!;0zpCyC4*7YLkZCO@cul0R_&zghsA8Ek)z%jNpKa92{@NH+SstX%@}xB*Jk!l}PZ1cClIns~}5^!RncUk*rmA z%SIVgt58EQxLJ+OiFqKkBuwquyZLUp|gr zPUbUbFbBrPd1xO`^C*r-i3p9*F#(OBh!4Wy@aC&*!|O|GXcjDA&YhF{;cD7*o(GS^ z@pQSE7)x_81=Qyje6*LQ#TXu-7(o;S8u3tpDA0_ddj%hmCspeXdhYR}-i9A=C`EoChUYuH~^x!9+|&(Pgb*>Ck<=9j|)*@xyfT zpP#+Kt<}39b|3Wl4fxT(+G?aH>gG^d@MEaUOJRfy55TtFI7^Y)VuMU=7Pp0y55jS~ z+TJ)igMyrqkX;wU8j68iIDtqJYhS_D3_Oem-@g6me_RfiQ}uT_K-A-9qG%}gk3E9c z!8`KgsMNXm)beloPfMql*|&$M>1q`i!cY)RFYnjNYu*gSv@8XebV7%}xYL>6)GJPJ zJpA7K31lcJuIFKSw-{x4FX0K2Az)~Xg<`sOu$4|^-(^XJX4YzoiNRvL zyuY7~26w-P^Zw%6F}shBwV2$02c8RsSUy6dM92diCAxiX3IUqsq5ig6>U_!Vy*q5$ zjm}16tJr+QZ&T?HkkpeIwVX-r1EI=@%Zlt;6g*mj&E!A))%gXJ=x6u#l zcKEP>bx4rqV*k`BBrZ$Mqjt{TsHcxUH>;)iAK}B(Kila&VD%b?6m&^0WK4^&EZNAI z8AMYs_%$D%|M+@+cQqL-hi4yG>h*jwdii!W1{}p>ZhwFYt_4Su1kjY9<-5`!$VNOI1y(EDSH4WpCijE_>al!Nt=^eVER#uJ1=b z;BWF2IgyF5LexV+yec$Kin;Ai@myo;G>zJ&jrdW#ecXhIZph5OYqw_PEfcF56Z5Zu_ZZE#q3Mc+N&1O^7hoh9QR7`+L$cBP5pqG=fqA0uh zUBukaxTFmH)<|Xbvp2c=HSbNkpXw73a5lv7V$jb92#yZ141@$X?F}Jt8gIU7Wm6m9 z?e_;;zsnm6`LZeP>T=$)Kr>Z?kr*UmFqR7zx0C6^bmcsc@1AGtw_rNH>-Xm$d*|Q< zn&1Ln0u7=l&ILs>%CkJp`DiG9F18x4Ne+lg<#i?e7jL%x;4ZnRkN^Mx07*qoM6N<$ Ef(>0N!2kdN literal 0 HcmV?d00001 diff --git a/examples/enyo_backbone/index.html b/examples/enyo_backbone/index.html index df0aea9ab1..565dc4cbb4 100644 --- a/examples/enyo_backbone/index.html +++ b/examples/enyo_backbone/index.html @@ -3,13 +3,12 @@ Enyo & Backbone.js • TodoMVC - - +
    - +
    diff --git a/examples/enyo_backbone/node_modules/todomvc-app-css/index.css b/examples/enyo_backbone/node_modules/todomvc-app-css/index.css deleted file mode 100644 index 4308848800..0000000000 --- a/examples/enyo_backbone/node_modules/todomvc-app-css/index.css +++ /dev/null @@ -1,394 +0,0 @@ -html, -body { - margin: 0; - padding: 0; -} - -button { - margin: 0; - padding: 0; - border: 0; - background: none; - font-size: 100%; - vertical-align: baseline; - font-family: inherit; - font-weight: inherit; - color: inherit; - -webkit-appearance: none; - -ms-appearance: none; - appearance: none; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; -} - -body { - font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; - line-height: 1.4em; - background: #f5f5f5; - color: #4d4d4d; - min-width: 230px; - max-width: 550px; - margin: 0 auto; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; - font-weight: 300; -} - -button, -input[type="checkbox"] { - outline: none; -} - -.hidden { - display: none; -} - -#todoapp { - background: #fff; - margin: 130px 0 40px 0; - position: relative; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), - 0 25px 50px 0 rgba(0, 0, 0, 0.1); -} - -#todoapp input::-webkit-input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp input::-moz-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp input::input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp h1 { - position: absolute; - top: -155px; - width: 100%; - font-size: 100px; - font-weight: 100; - text-align: center; - color: rgba(175, 47, 47, 0.15); - -webkit-text-rendering: optimizeLegibility; - -moz-text-rendering: optimizeLegibility; - -ms-text-rendering: optimizeLegibility; - text-rendering: optimizeLegibility; -} - -#new-todo, -.edit { - position: relative; - margin: 0; - width: 100%; - font-size: 24px; - font-family: inherit; - font-weight: inherit; - line-height: 1.4em; - border: 0; - outline: none; - color: inherit; - padding: 6px; - border: 1px solid #999; - box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); - -ms-box-sizing: border-box; - box-sizing: border-box; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; -} - -#new-todo { - padding: 16px 16px 16px 60px; - border: none; - background: rgba(0, 0, 0, 0.003); - box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); -} - -#main { - position: relative; - z-index: 2; - border-top: 1px solid #e6e6e6; -} - -label[for='toggle-all'] { - display: none; -} - -#toggle-all { - position: absolute; - top: -55px; - left: -12px; - width: 60px; - height: 34px; - text-align: center; - border: none; /* Mobile Safari */ -} - -#toggle-all:before { - content: '❯'; - font-size: 22px; - color: #e6e6e6; - padding: 10px 27px 10px 27px; -} - -#toggle-all:checked:before { - color: #737373; -} - -#todo-list { - margin: 0; - padding: 0; - list-style: none; -} - -#todo-list li { - position: relative; - font-size: 24px; - border-bottom: 1px solid #ededed; -} - -#todo-list li:last-child { - border-bottom: none; -} - -#todo-list li.editing { - border-bottom: none; - padding: 0; -} - -#todo-list li.editing .edit { - display: block; - width: 506px; - padding: 13px 17px 12px 17px; - margin: 0 0 0 43px; -} - -#todo-list li.editing .view { - display: none; -} - -#todo-list li .toggle { - text-align: center; - width: 40px; - /* auto, since non-WebKit browsers doesn't support input styling */ - height: auto; - position: absolute; - top: 0; - bottom: 0; - margin: auto 0; - border: none; /* Mobile Safari */ - -webkit-appearance: none; - -ms-appearance: none; - appearance: none; -} - -#todo-list li .toggle:after { - content: url('data:image/svg+xml;utf8,'); -} - -#todo-list li .toggle:checked:after { - content: url('data:image/svg+xml;utf8,'); -} - -#todo-list li label { - white-space: pre; - word-break: break-word; - padding: 15px 60px 15px 15px; - margin-left: 45px; - display: block; - line-height: 1.2; - transition: color 0.4s; -} - -#todo-list li.completed label { - color: #d9d9d9; - text-decoration: line-through; -} - -#todo-list li .destroy { - display: none; - position: absolute; - top: 0; - right: 10px; - bottom: 0; - width: 40px; - height: 40px; - margin: auto 0; - font-size: 30px; - color: #cc9a9a; - margin-bottom: 11px; - transition: color 0.2s ease-out; -} - -#todo-list li .destroy:hover { - color: #af5b5e; -} - -#todo-list li .destroy:after { - content: '×'; -} - -#todo-list li:hover .destroy { - display: block; -} - -#todo-list li .edit { - display: none; -} - -#todo-list li.editing:last-child { - margin-bottom: -1px; -} - -#footer { - color: #777; - padding: 10px 15px; - height: 20px; - text-align: center; - border-top: 1px solid #e6e6e6; -} - -#footer:before { - content: ''; - position: absolute; - right: 0; - bottom: 0; - left: 0; - height: 50px; - overflow: hidden; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), - 0 8px 0 -3px #f6f6f6, - 0 9px 1px -3px rgba(0, 0, 0, 0.2), - 0 16px 0 -6px #f6f6f6, - 0 17px 2px -6px rgba(0, 0, 0, 0.2); -} - -#todo-count { - float: left; - text-align: left; -} - -#todo-count strong { - font-weight: 300; -} - -#filters { - margin: 0; - padding: 0; - list-style: none; - position: absolute; - right: 0; - left: 0; -} - -#filters li { - display: inline; -} - -#filters li a { - color: inherit; - margin: 3px; - padding: 3px 7px; - text-decoration: none; - border: 1px solid transparent; - border-radius: 3px; -} - -#filters li a.selected, -#filters li a:hover { - border-color: rgba(175, 47, 47, 0.1); -} - -#filters li a.selected { - border-color: rgba(175, 47, 47, 0.2); -} - -#clear-completed, -html #clear-completed:active { - float: right; - position: relative; - line-height: 20px; - text-decoration: none; - cursor: pointer; - visibility: hidden; - position: relative; -} - -#clear-completed::after { - visibility: visible; - content: 'Clear completed'; - position: absolute; - right: 0; - white-space: nowrap; -} - -#clear-completed:hover::after { - text-decoration: underline; -} - -#info { - margin: 65px auto 0; - color: #bfbfbf; - font-size: 10px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - text-align: center; -} - -#info p { - line-height: 1; -} - -#info a { - color: inherit; - text-decoration: none; - font-weight: 400; -} - -#info a:hover { - text-decoration: underline; -} - -/* - Hack to remove background from Mobile Safari. - Can't use it globally since it destroys checkboxes in Firefox -*/ -@media screen and (-webkit-min-device-pixel-ratio:0) { - #toggle-all, - #todo-list li .toggle { - background: none; - } - - #todo-list li .toggle { - height: 40px; - } - - #toggle-all { - -webkit-transform: rotate(90deg); - transform: rotate(90deg); - -webkit-appearance: none; - appearance: none; - } -} - -@media (max-width: 430px) { - #footer { - height: 50px; - } - - #filters { - bottom: 10px; - } -} diff --git a/examples/enyo_backbone/node_modules/todomvc-common/base.css b/examples/enyo_backbone/node_modules/todomvc-common/base.css deleted file mode 100644 index da65968a73..0000000000 --- a/examples/enyo_backbone/node_modules/todomvc-common/base.css +++ /dev/null @@ -1,141 +0,0 @@ -hr { - margin: 20px 0; - border: 0; - border-top: 1px dashed #c5c5c5; - border-bottom: 1px dashed #f7f7f7; -} - -.learn a { - font-weight: normal; - text-decoration: none; - color: #b83f45; -} - -.learn a:hover { - text-decoration: underline; - color: #787e7e; -} - -.learn h3, -.learn h4, -.learn h5 { - margin: 10px 0; - font-weight: 500; - line-height: 1.2; - color: #000; -} - -.learn h3 { - font-size: 24px; -} - -.learn h4 { - font-size: 18px; -} - -.learn h5 { - margin-bottom: 0; - font-size: 14px; -} - -.learn ul { - padding: 0; - margin: 0 0 30px 25px; -} - -.learn li { - line-height: 20px; -} - -.learn p { - font-size: 15px; - font-weight: 300; - line-height: 1.3; - margin-top: 0; - margin-bottom: 0; -} - -#issue-count { - display: none; -} - -.quote { - border: none; - margin: 20px 0 60px 0; -} - -.quote p { - font-style: italic; -} - -.quote p:before { - content: '“'; - font-size: 50px; - opacity: .15; - position: absolute; - top: -20px; - left: 3px; -} - -.quote p:after { - content: '”'; - font-size: 50px; - opacity: .15; - position: absolute; - bottom: -42px; - right: 3px; -} - -.quote footer { - position: absolute; - bottom: -40px; - right: 0; -} - -.quote footer img { - border-radius: 3px; -} - -.quote footer a { - margin-left: 5px; - vertical-align: middle; -} - -.speech-bubble { - position: relative; - padding: 10px; - background: rgba(0, 0, 0, .04); - border-radius: 5px; -} - -.speech-bubble:after { - content: ''; - position: absolute; - top: 100%; - right: 30px; - border: 13px solid transparent; - border-top-color: rgba(0, 0, 0, .04); -} - -.learn-bar > .learn { - position: absolute; - width: 272px; - top: 8px; - left: -300px; - padding: 10px; - border-radius: 5px; - background-color: rgba(255, 255, 255, .6); - transition-property: left; - transition-duration: 500ms; -} - -@media (min-width: 899px) { - .learn-bar { - width: auto; - padding-left: 300px; - } - - .learn-bar > .learn { - left: 8px; - } -} diff --git a/examples/enyo_backbone/node_modules/todomvc-common/base.js b/examples/enyo_backbone/node_modules/todomvc-common/base.js deleted file mode 100644 index 44fb50c613..0000000000 --- a/examples/enyo_backbone/node_modules/todomvc-common/base.js +++ /dev/null @@ -1,244 +0,0 @@ -/* global _ */ -(function () { - 'use strict'; - - /* jshint ignore:start */ - // Underscore's Template Module - // Courtesy of underscorejs.org - var _ = (function (_) { - _.defaults = function (object) { - if (!object) { - return object; - } - for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { - var iterable = arguments[argsIndex]; - if (iterable) { - for (var key in iterable) { - if (object[key] == null) { - object[key] = iterable[key]; - } - } - } - } - return object; - } - - // By default, Underscore uses ERB-style template delimiters, change the - // following template settings to use alternative delimiters. - _.templateSettings = { - evaluate : /<%([\s\S]+?)%>/g, - interpolate : /<%=([\s\S]+?)%>/g, - escape : /<%-([\s\S]+?)%>/g - }; - - // When customizing `templateSettings`, if you don't want to define an - // interpolation, evaluation or escaping regex, we need one that is - // guaranteed not to match. - var noMatch = /(.)^/; - - // Certain characters need to be escaped so that they can be put into a - // string literal. - var escapes = { - "'": "'", - '\\': '\\', - '\r': 'r', - '\n': 'n', - '\t': 't', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; - - var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; - - // JavaScript micro-templating, similar to John Resig's implementation. - // Underscore templating handles arbitrary delimiters, preserves whitespace, - // and correctly escapes quotes within interpolated code. - _.template = function(text, data, settings) { - var render; - settings = _.defaults({}, settings, _.templateSettings); - - // Combine delimiters into one regular expression via alternation. - var matcher = new RegExp([ - (settings.escape || noMatch).source, - (settings.interpolate || noMatch).source, - (settings.evaluate || noMatch).source - ].join('|') + '|$', 'g'); - - // Compile the template source, escaping string literals appropriately. - var index = 0; - var source = "__p+='"; - text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { - source += text.slice(index, offset) - .replace(escaper, function(match) { return '\\' + escapes[match]; }); - - if (escape) { - source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; - } - if (interpolate) { - source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; - } - if (evaluate) { - source += "';\n" + evaluate + "\n__p+='"; - } - index = offset + match.length; - return match; - }); - source += "';\n"; - - // If a variable is not specified, place data values in local scope. - if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; - - source = "var __t,__p='',__j=Array.prototype.join," + - "print=function(){__p+=__j.call(arguments,'');};\n" + - source + "return __p;\n"; - - try { - render = new Function(settings.variable || 'obj', '_', source); - } catch (e) { - e.source = source; - throw e; - } - - if (data) return render(data, _); - var template = function(data) { - return render.call(this, data, _); - }; - - // Provide the compiled function source as a convenience for precompilation. - template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; - - return template; - }; - - return _; - })({}); - - if (location.hostname === 'todomvc.com') { - window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')); - } - /* jshint ignore:end */ - - function redirect() { - if (location.hostname === 'tastejs.github.io') { - location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com'); - } - } - - function findRoot() { - var base = location.href.indexOf('examples/'); - return location.href.substr(0, base); - } - - function getFile(file, callback) { - if (!location.host) { - return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.'); - } - - var xhr = new XMLHttpRequest(); - - xhr.open('GET', findRoot() + file, true); - xhr.send(); - - xhr.onload = function () { - if (xhr.status === 200 && callback) { - callback(xhr.responseText); - } - }; - } - - function Learn(learnJSON, config) { - if (!(this instanceof Learn)) { - return new Learn(learnJSON, config); - } - - var template, framework; - - if (typeof learnJSON !== 'object') { - try { - learnJSON = JSON.parse(learnJSON); - } catch (e) { - return; - } - } - - if (config) { - template = config.template; - framework = config.framework; - } - - if (!template && learnJSON.templates) { - template = learnJSON.templates.todomvc; - } - - if (!framework && document.querySelector('[data-framework]')) { - framework = document.querySelector('[data-framework]').dataset.framework; - } - - this.template = template; - - if (learnJSON.backend) { - this.frameworkJSON = learnJSON.backend; - this.frameworkJSON.issueLabel = framework; - this.append({ - backend: true - }); - } else if (learnJSON[framework]) { - this.frameworkJSON = learnJSON[framework]; - this.frameworkJSON.issueLabel = framework; - this.append(); - } - - this.fetchIssueCount(); - } - - Learn.prototype.append = function (opts) { - var aside = document.createElement('aside'); - aside.innerHTML = _.template(this.template, this.frameworkJSON); - aside.className = 'learn'; - - if (opts && opts.backend) { - // Remove demo link - var sourceLinks = aside.querySelector('.source-links'); - var heading = sourceLinks.firstElementChild; - var sourceLink = sourceLinks.lastElementChild; - // Correct link path - var href = sourceLink.getAttribute('href'); - sourceLink.setAttribute('href', href.substr(href.lastIndexOf('http'))); - sourceLinks.innerHTML = heading.outerHTML + sourceLink.outerHTML; - } else { - // Localize demo links - var demoLinks = aside.querySelectorAll('.demo-link'); - Array.prototype.forEach.call(demoLinks, function (demoLink) { - if (demoLink.getAttribute('href').substr(0, 4) !== 'http') { - demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href')); - } - }); - } - - document.body.className = (document.body.className + ' learn-bar').trim(); - document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); - }; - - Learn.prototype.fetchIssueCount = function () { - var issueLink = document.getElementById('issue-count-link'); - if (issueLink) { - var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos'); - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.onload = function (e) { - var parsedResponse = JSON.parse(e.target.responseText); - if (parsedResponse instanceof Array) { - var count = parsedResponse.length - if (count !== 0) { - issueLink.innerHTML = 'This app has ' + count + ' open issues'; - document.getElementById('issue-count').style.display = 'inline'; - } - } - }; - xhr.send(); - } - }; - - redirect(); - getFile('learn.json', Learn); -})(); diff --git a/examples/enyo_backbone/package.json b/examples/enyo_backbone/package.json deleted file mode 100644 index 24bbd7840b..0000000000 --- a/examples/enyo_backbone/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "private": true, - "dependencies": { - "todomvc-common": "^1.0.1", - "todomvc-app-css": "^1.0.1" - } -} diff --git a/examples/extjs_deftjs/.gitignore b/examples/extjs_deftjs/.gitignore index ede5ab4b30..8ef4803eba 100644 --- a/examples/extjs_deftjs/.gitignore +++ b/examples/extjs_deftjs/.gitignore @@ -2,10 +2,5 @@ build spec coffee tools - -node_modules/todomvc-common -!node_modules/todomvc-common/base.js -!node_modules/todomvc-common/base.css - -node_modules/todomvc-app-css -!node_modules/todomvc-app-css/index.css +css/base.css +css/bg.png diff --git a/examples/extjs_deftjs/bower.json b/examples/extjs_deftjs/bower.json new file mode 100644 index 0000000000..2143df040d --- /dev/null +++ b/examples/extjs_deftjs/bower.json @@ -0,0 +1,7 @@ +{ + "name": "todomvc-extjs_deftjs", + "version": "0.0.0", + "dependencies": { + "todomvc-common": "~0.3.0" + } +} diff --git a/examples/extjs_deftjs/bower_components/todomvc-common/base.css b/examples/extjs_deftjs/bower_components/todomvc-common/base.css new file mode 100644 index 0000000000..285f531307 --- /dev/null +++ b/examples/extjs_deftjs/bower_components/todomvc-common/base.css @@ -0,0 +1,554 @@ +html, +body { + margin: 0; + padding: 0; +} + +button { + margin: 0; + padding: 0; + border: 0; + background: none; + font-size: 100%; + vertical-align: baseline; + font-family: inherit; + color: inherit; + -webkit-appearance: none; + -ms-appearance: none; + -o-appearance: none; + appearance: none; +} + +body { + font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; + line-height: 1.4em; + background: #eaeaea url('bg.png'); + color: #4d4d4d; + width: 550px; + margin: 0 auto; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + -o-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +button, +input[type="checkbox"] { + outline: none; +} + +#todoapp { + background: #fff; + background: rgba(255, 255, 255, 0.9); + margin: 130px 0 40px 0; + border: 1px solid #ccc; + position: relative; + border-top-left-radius: 2px; + border-top-right-radius: 2px; + box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2), + 0 25px 50px 0 rgba(0, 0, 0, 0.15); +} + +#todoapp:before { + content: ''; + border-left: 1px solid #f5d6d6; + border-right: 1px solid #f5d6d6; + width: 2px; + position: absolute; + top: 0; + left: 40px; + height: 100%; +} + +#todoapp input::-webkit-input-placeholder { + font-style: italic; +} + +#todoapp input::-moz-placeholder { + font-style: italic; + color: #a9a9a9; +} + +#todoapp h1 { + position: absolute; + top: -120px; + width: 100%; + font-size: 70px; + font-weight: bold; + text-align: center; + color: #b3b3b3; + color: rgba(255, 255, 255, 0.3); + text-shadow: -1px -1px rgba(0, 0, 0, 0.2); + -webkit-text-rendering: optimizeLegibility; + -moz-text-rendering: optimizeLegibility; + -ms-text-rendering: optimizeLegibility; + -o-text-rendering: optimizeLegibility; + text-rendering: optimizeLegibility; +} + +#header { + padding-top: 15px; + border-radius: inherit; +} + +#header:before { + content: ''; + position: absolute; + top: 0; + right: 0; + left: 0; + height: 15px; + z-index: 2; + border-bottom: 1px solid #6c615c; + background: #8d7d77; + background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8))); + background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); + background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670'); + border-top-left-radius: 1px; + border-top-right-radius: 1px; +} + +#new-todo, +.edit { + position: relative; + margin: 0; + width: 100%; + font-size: 24px; + font-family: inherit; + line-height: 1.4em; + border: 0; + outline: none; + color: inherit; + padding: 6px; + border: 1px solid #999; + box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + -o-box-sizing: border-box; + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + -o-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +#new-todo { + padding: 16px 16px 16px 60px; + border: none; + background: rgba(0, 0, 0, 0.02); + z-index: 2; + box-shadow: none; +} + +#main { + position: relative; + z-index: 2; + border-top: 1px dotted #adadad; +} + +label[for='toggle-all'] { + display: none; +} + +#toggle-all { + position: absolute; + top: -42px; + left: -4px; + width: 40px; + text-align: center; + /* Mobile Safari */ + border: none; +} + +#toggle-all:before { + content: '»'; + font-size: 28px; + color: #d9d9d9; + padding: 0 25px 7px; +} + +#toggle-all:checked:before { + color: #737373; +} + +#todo-list { + margin: 0; + padding: 0; + list-style: none; +} + +#todo-list li { + position: relative; + font-size: 24px; + border-bottom: 1px dotted #ccc; +} + +#todo-list li:last-child { + border-bottom: none; +} + +#todo-list li.editing { + border-bottom: none; + padding: 0; +} + +#todo-list li.editing .edit { + display: block; + width: 506px; + padding: 13px 17px 12px 17px; + margin: 0 0 0 43px; +} + +#todo-list li.editing .view { + display: none; +} + +#todo-list li .toggle { + text-align: center; + width: 40px; + /* auto, since non-WebKit browsers doesn't support input styling */ + height: auto; + position: absolute; + top: 0; + bottom: 0; + margin: auto 0; + /* Mobile Safari */ + border: none; + -webkit-appearance: none; + -ms-appearance: none; + -o-appearance: none; + appearance: none; +} + +#todo-list li .toggle:after { + content: '✔'; + /* 40 + a couple of pixels visual adjustment */ + line-height: 43px; + font-size: 20px; + color: #d9d9d9; + text-shadow: 0 -1px 0 #bfbfbf; +} + +#todo-list li .toggle:checked:after { + color: #85ada7; + text-shadow: 0 1px 0 #669991; + bottom: 1px; + position: relative; +} + +#todo-list li label { + white-space: pre; + word-break: break-word; + padding: 15px 60px 15px 15px; + margin-left: 45px; + display: block; + line-height: 1.2; + -webkit-transition: color 0.4s; + transition: color 0.4s; +} + +#todo-list li.completed label { + color: #a9a9a9; + text-decoration: line-through; +} + +#todo-list li .destroy { + display: none; + position: absolute; + top: 0; + right: 10px; + bottom: 0; + width: 40px; + height: 40px; + margin: auto 0; + font-size: 22px; + color: #a88a8a; + -webkit-transition: all 0.2s; + transition: all 0.2s; +} + +#todo-list li .destroy:hover { + text-shadow: 0 0 1px #000, + 0 0 10px rgba(199, 107, 107, 0.8); + -webkit-transform: scale(1.3); + transform: scale(1.3); +} + +#todo-list li .destroy:after { + content: '✖'; +} + +#todo-list li:hover .destroy { + display: block; +} + +#todo-list li .edit { + display: none; +} + +#todo-list li.editing:last-child { + margin-bottom: -1px; +} + +#footer { + color: #777; + padding: 0 15px; + position: absolute; + right: 0; + bottom: -31px; + left: 0; + height: 20px; + z-index: 1; + text-align: center; +} + +#footer:before { + content: ''; + position: absolute; + right: 0; + bottom: 31px; + left: 0; + height: 50px; + z-index: -1; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), + 0 6px 0 -3px rgba(255, 255, 255, 0.8), + 0 7px 1px -3px rgba(0, 0, 0, 0.3), + 0 43px 0 -6px rgba(255, 255, 255, 0.8), + 0 44px 2px -6px rgba(0, 0, 0, 0.2); +} + +#todo-count { + float: left; + text-align: left; +} + +#filters { + margin: 0; + padding: 0; + list-style: none; + position: absolute; + right: 0; + left: 0; +} + +#filters li { + display: inline; +} + +#filters li a { + color: #83756f; + margin: 2px; + text-decoration: none; +} + +#filters li a.selected { + font-weight: bold; +} + +#clear-completed { + float: right; + position: relative; + line-height: 20px; + text-decoration: none; + background: rgba(0, 0, 0, 0.1); + font-size: 11px; + padding: 0 10px; + border-radius: 3px; + box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2); +} + +#clear-completed:hover { + background: rgba(0, 0, 0, 0.15); + box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3); +} + +#info { + margin: 65px auto 0; + color: #a6a6a6; + font-size: 12px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); + text-align: center; +} + +#info a { + color: inherit; +} + +/* + Hack to remove background from Mobile Safari. + Can't use it globally since it destroys checkboxes in Firefox and Opera +*/ + +@media screen and (-webkit-min-device-pixel-ratio:0) { + #toggle-all, + #todo-list li .toggle { + background: none; + } + + #todo-list li .toggle { + height: 40px; + } + + #toggle-all { + top: -56px; + left: -15px; + width: 65px; + height: 41px; + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + -webkit-appearance: none; + appearance: none; + } +} + +.hidden { + display: none; +} + +hr { + margin: 20px 0; + border: 0; + border-top: 1px dashed #C5C5C5; + border-bottom: 1px dashed #F7F7F7; +} + +.learn a { + font-weight: normal; + text-decoration: none; + color: #b83f45; +} + +.learn a:hover { + text-decoration: underline; + color: #787e7e; +} + +.learn h3, +.learn h4, +.learn h5 { + margin: 10px 0; + font-weight: 500; + line-height: 1.2; + color: #000; +} + +.learn h3 { + font-size: 24px; +} + +.learn h4 { + font-size: 18px; +} + +.learn h5 { + margin-bottom: 0; + font-size: 14px; +} + +.learn ul { + padding: 0; + margin: 0 0 30px 25px; +} + +.learn li { + line-height: 20px; +} + +.learn p { + font-size: 15px; + font-weight: 300; + line-height: 1.3; + margin-top: 0; + margin-bottom: 0; +} + +.quote { + border: none; + margin: 20px 0 60px 0; +} + +.quote p { + font-style: italic; +} + +.quote p:before { + content: '“'; + font-size: 50px; + opacity: .15; + position: absolute; + top: -20px; + left: 3px; +} + +.quote p:after { + content: '”'; + font-size: 50px; + opacity: .15; + position: absolute; + bottom: -42px; + right: 3px; +} + +.quote footer { + position: absolute; + bottom: -40px; + right: 0; +} + +.quote footer img { + border-radius: 3px; +} + +.quote footer a { + margin-left: 5px; + vertical-align: middle; +} + +.speech-bubble { + position: relative; + padding: 10px; + background: rgba(0, 0, 0, .04); + border-radius: 5px; +} + +.speech-bubble:after { + content: ''; + position: absolute; + top: 100%; + right: 30px; + border: 13px solid transparent; + border-top-color: rgba(0, 0, 0, .04); +} + +.learn-bar > .learn { + position: absolute; + width: 272px; + top: 8px; + left: -300px; + padding: 10px; + border-radius: 5px; + background-color: rgba(255, 255, 255, .6); + -webkit-transition-property: left; + transition-property: left; + -webkit-transition-duration: 500ms; + transition-duration: 500ms; +} + +@media (min-width: 899px) { + .learn-bar { + width: auto; + margin: 0 0 0 300px; + } + + .learn-bar > .learn { + left: 8px; + } + + .learn-bar #todoapp { + width: 550px; + margin: 130px auto 40px auto; + } +} diff --git a/examples/extjs_deftjs/bower_components/todomvc-common/base.js b/examples/extjs_deftjs/bower_components/todomvc-common/base.js new file mode 100644 index 0000000000..d3a15127d8 --- /dev/null +++ b/examples/extjs_deftjs/bower_components/todomvc-common/base.js @@ -0,0 +1,217 @@ +(function () { + 'use strict'; + + // Underscore's Template Module + // Courtesy of underscorejs.org + var _ = (function (_) { + _.defaults = function (object) { + if (!object) { + return object; + } + for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { + var iterable = arguments[argsIndex]; + if (iterable) { + for (var key in iterable) { + if (object[key] == null) { + object[key] = iterable[key]; + } + } + } + } + return object; + } + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + _.template = function(text, data, settings) { + var render; + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = new RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset) + .replace(escaper, function(match) { return '\\' + escapes[match]; }); + + if (escape) { + source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; + } + if (interpolate) { + source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; + } + if (evaluate) { + source += "';\n" + evaluate + "\n__p+='"; + } + index = offset + match.length; + return match; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + "return __p;\n"; + + try { + render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + if (data) return render(data, _); + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled function source as a convenience for precompilation. + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + + return template; + }; + + return _; + })({}); + + if (location.hostname === 'todomvc.com') { + window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')); + } + + function redirect() { + if (location.hostname === 'tastejs.github.io') { + location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com'); + } + } + + function findRoot() { + var base = location.href.indexOf('examples/'); + return location.href.substr(0, base); + } + + function getFile(file, callback) { + if (!location.host) { + return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.'); + } + + var xhr = new XMLHttpRequest(); + + xhr.open('GET', findRoot() + file, true); + xhr.send(); + + xhr.onload = function () { + if (xhr.status === 200 && callback) { + callback(xhr.responseText); + } + }; + } + + function Learn(learnJSON, config) { + if (!(this instanceof Learn)) { + return new Learn(learnJSON, config); + } + + var template, framework; + + if (typeof learnJSON !== 'object') { + try { + learnJSON = JSON.parse(learnJSON); + } catch (e) { + return; + } + } + + if (config) { + template = config.template; + framework = config.framework; + } + + if (!template && learnJSON.templates) { + template = learnJSON.templates.todomvc; + } + + if (!framework && document.querySelector('[data-framework]')) { + framework = document.querySelector('[data-framework]').dataset.framework; + } + + this.template = template; + + if (learnJSON.backend) { + this.frameworkJSON = learnJSON.backend; + this.append({ + backend: true + }); + } else if (learnJSON[framework]) { + this.frameworkJSON = learnJSON[framework]; + this.append(); + } + } + + Learn.prototype.append = function (opts) { + var aside = document.createElement('aside'); + aside.innerHTML = _.template(this.template, this.frameworkJSON); + aside.className = 'learn'; + + if (opts && opts.backend) { + // Remove demo link + var sourceLinks = aside.querySelector('.source-links'); + var heading = sourceLinks.firstElementChild; + var sourceLink = sourceLinks.lastElementChild; + // Correct link path + var href = sourceLink.getAttribute('href'); + sourceLink.setAttribute('href', href.substr(href.lastIndexOf('http'))); + sourceLinks.innerHTML = heading.outerHTML + sourceLink.outerHTML; + } else { + // Localize demo links + var demoLinks = aside.querySelectorAll('.demo-link'); + Array.prototype.forEach.call(demoLinks, function (demoLink) { + if (demoLink.getAttribute('href').substr(0, 4) !== 'http') { + demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href')); + } + }); + } + + document.body.className = (document.body.className + ' learn-bar').trim(); + document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); + }; + + redirect(); + getFile('learn.json', Learn); +})(); diff --git a/examples/extjs_deftjs/bower_components/todomvc-common/bg.png b/examples/extjs_deftjs/bower_components/todomvc-common/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..b2a7600825ee11f21849ed475499c7d1ecbcc870 GIT binary patch literal 2126 zcmV-U2(kBxP)+9y`=HK7nt=~3t000O5Nklm=04hVa_lXl_bm=+M;=eI<%$rO2ta`suh*Sr#POw*?adq!O!CV z*0&b)rkCCEV)1nIuiLBntUH-MJI;&qYgz|d@Nhnn_abi2g`pu4NAMVid3hS%?quz~ zjlJf(x8cj{VS zUVEP1msg9`^OFUhSO5rtXHhRlyU2i>6$BT=glG`{JlctGHrwRIm)6Buu65jLQn^H8 zvl9lZqUBA+%qvM5XC+yY@mfA(YB~XP=e|6<{%$^eU8nrK?v2ccb@Jom5CG>rKAL7J zplHEIavVp|0p1&m!G`Ti?<7iZ;}@G7#Z)}Km%2!FHnQ0*&?PLR)G!HAaHgU#c|$dz zpj#0HmeadIe>`#)=Jjkb{B=FKnU3pc-&Y@j_j6(h4Y~+Uq!^J=LHunx1xi4QXK-Y{&MoT8ky6Vb9c|WhnOwF~c=3zOy8agktliS6# z`#8F`9H)D=bmk9B5MnW&_r#)f$c+;$LSr-@^An8dhc~Iquuv>jOK7pw7LJ;&X0i1C zGMsHdP1Os@ny$$j!>XAii%7bp5k>`pyNA!~epb)()p9zR4Yl6z=U}{CIdh1z(FpAo zQIW!;0zpCyC4*7YLkZCO@cul0R_&zghsA8Ek)z%jNpKa92{@NH+SstX%@}xB*Jk!l}PZ1cClIns~}5^!RncUk*rmA z%SIVgt58EQxLJ+OiFqKkBuwquyZLUp|gr zPUbUbFbBrPd1xO`^C*r-i3p9*F#(OBh!4Wy@aC&*!|O|GXcjDA&YhF{;cD7*o(GS^ z@pQSE7)x_81=Qyje6*LQ#TXu-7(o;S8u3tpDA0_ddj%hmCspeXdhYR}-i9A=C`EoChUYuH~^x!9+|&(Pgb*>Ck<=9j|)*@xyfT zpP#+Kt<}39b|3Wl4fxT(+G?aH>gG^d@MEaUOJRfy55TtFI7^Y)VuMU=7Pp0y55jS~ z+TJ)igMyrqkX;wU8j68iIDtqJYhS_D3_Oem-@g6me_RfiQ}uT_K-A-9qG%}gk3E9c z!8`KgsMNXm)beloPfMql*|&$M>1q`i!cY)RFYnjNYu*gSv@8XebV7%}xYL>6)GJPJ zJpA7K31lcJuIFKSw-{x4FX0K2Az)~Xg<`sOu$4|^-(^XJX4YzoiNRvL zyuY7~26w-P^Zw%6F}shBwV2$02c8RsSUy6dM92diCAxiX3IUqsq5ig6>U_!Vy*q5$ zjm}16tJr+QZ&T?HkkpeIwVX-r1EI=@%Zlt;6g*mj&E!A))%gXJ=x6u#l zcKEP>bx4rqV*k`BBrZ$Mqjt{TsHcxUH>;)iAK}B(Kila&VD%b?6m&^0WK4^&EZNAI z8AMYs_%$D%|M+@+cQqL-hi4yG>h*jwdii!W1{}p>ZhwFYt_4Su1kjY9<-5`!$VNOI1y(EDSH4WpCijE_>al!Nt=^eVER#uJ1=b z;BWF2IgyF5LexV+yec$Kin;Ai@myo;G>zJ&jrdW#ecXhIZph5OYqw_PEfcF56Z5Zu_ZZE#q3Mc+N&1O^7hoh9QR7`+L$cBP5pqG=fqA0uh zUBukaxTFmH)<|Xbvp2c=HSbNkpXw73a5lv7V$jb92#yZ141@$X?F}Jt8gIU7Wm6m9 z?e_;;zsnm6`LZeP>T=$)Kr>Z?kr*UmFqR7zx0C6^bmcsc@1AGtw_rNH>-Xm$d*|Q< zn&1Ln0u7=l&ILs>%CkJp`DiG9F18x4Ne+lg<#i?e7jL%x;4ZnRkN^Mx07*qoM6N<$ Ef(>0N!2kdN literal 0 HcmV?d00001 diff --git a/examples/extjs_deftjs/index.html b/examples/extjs_deftjs/index.html index 1bc3866ba6..06c68bbb9c 100644 --- a/examples/extjs_deftjs/index.html +++ b/examples/extjs_deftjs/index.html @@ -4,12 +4,11 @@ ExtJS with DeftJS • TodoMVC - - + - + diff --git a/examples/extjs_deftjs/node_modules/todomvc-app-css/index.css b/examples/extjs_deftjs/node_modules/todomvc-app-css/index.css deleted file mode 100644 index 4308848800..0000000000 --- a/examples/extjs_deftjs/node_modules/todomvc-app-css/index.css +++ /dev/null @@ -1,394 +0,0 @@ -html, -body { - margin: 0; - padding: 0; -} - -button { - margin: 0; - padding: 0; - border: 0; - background: none; - font-size: 100%; - vertical-align: baseline; - font-family: inherit; - font-weight: inherit; - color: inherit; - -webkit-appearance: none; - -ms-appearance: none; - appearance: none; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; -} - -body { - font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; - line-height: 1.4em; - background: #f5f5f5; - color: #4d4d4d; - min-width: 230px; - max-width: 550px; - margin: 0 auto; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; - font-weight: 300; -} - -button, -input[type="checkbox"] { - outline: none; -} - -.hidden { - display: none; -} - -#todoapp { - background: #fff; - margin: 130px 0 40px 0; - position: relative; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), - 0 25px 50px 0 rgba(0, 0, 0, 0.1); -} - -#todoapp input::-webkit-input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp input::-moz-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp input::input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp h1 { - position: absolute; - top: -155px; - width: 100%; - font-size: 100px; - font-weight: 100; - text-align: center; - color: rgba(175, 47, 47, 0.15); - -webkit-text-rendering: optimizeLegibility; - -moz-text-rendering: optimizeLegibility; - -ms-text-rendering: optimizeLegibility; - text-rendering: optimizeLegibility; -} - -#new-todo, -.edit { - position: relative; - margin: 0; - width: 100%; - font-size: 24px; - font-family: inherit; - font-weight: inherit; - line-height: 1.4em; - border: 0; - outline: none; - color: inherit; - padding: 6px; - border: 1px solid #999; - box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); - -ms-box-sizing: border-box; - box-sizing: border-box; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; -} - -#new-todo { - padding: 16px 16px 16px 60px; - border: none; - background: rgba(0, 0, 0, 0.003); - box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); -} - -#main { - position: relative; - z-index: 2; - border-top: 1px solid #e6e6e6; -} - -label[for='toggle-all'] { - display: none; -} - -#toggle-all { - position: absolute; - top: -55px; - left: -12px; - width: 60px; - height: 34px; - text-align: center; - border: none; /* Mobile Safari */ -} - -#toggle-all:before { - content: '❯'; - font-size: 22px; - color: #e6e6e6; - padding: 10px 27px 10px 27px; -} - -#toggle-all:checked:before { - color: #737373; -} - -#todo-list { - margin: 0; - padding: 0; - list-style: none; -} - -#todo-list li { - position: relative; - font-size: 24px; - border-bottom: 1px solid #ededed; -} - -#todo-list li:last-child { - border-bottom: none; -} - -#todo-list li.editing { - border-bottom: none; - padding: 0; -} - -#todo-list li.editing .edit { - display: block; - width: 506px; - padding: 13px 17px 12px 17px; - margin: 0 0 0 43px; -} - -#todo-list li.editing .view { - display: none; -} - -#todo-list li .toggle { - text-align: center; - width: 40px; - /* auto, since non-WebKit browsers doesn't support input styling */ - height: auto; - position: absolute; - top: 0; - bottom: 0; - margin: auto 0; - border: none; /* Mobile Safari */ - -webkit-appearance: none; - -ms-appearance: none; - appearance: none; -} - -#todo-list li .toggle:after { - content: url('data:image/svg+xml;utf8,'); -} - -#todo-list li .toggle:checked:after { - content: url('data:image/svg+xml;utf8,'); -} - -#todo-list li label { - white-space: pre; - word-break: break-word; - padding: 15px 60px 15px 15px; - margin-left: 45px; - display: block; - line-height: 1.2; - transition: color 0.4s; -} - -#todo-list li.completed label { - color: #d9d9d9; - text-decoration: line-through; -} - -#todo-list li .destroy { - display: none; - position: absolute; - top: 0; - right: 10px; - bottom: 0; - width: 40px; - height: 40px; - margin: auto 0; - font-size: 30px; - color: #cc9a9a; - margin-bottom: 11px; - transition: color 0.2s ease-out; -} - -#todo-list li .destroy:hover { - color: #af5b5e; -} - -#todo-list li .destroy:after { - content: '×'; -} - -#todo-list li:hover .destroy { - display: block; -} - -#todo-list li .edit { - display: none; -} - -#todo-list li.editing:last-child { - margin-bottom: -1px; -} - -#footer { - color: #777; - padding: 10px 15px; - height: 20px; - text-align: center; - border-top: 1px solid #e6e6e6; -} - -#footer:before { - content: ''; - position: absolute; - right: 0; - bottom: 0; - left: 0; - height: 50px; - overflow: hidden; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), - 0 8px 0 -3px #f6f6f6, - 0 9px 1px -3px rgba(0, 0, 0, 0.2), - 0 16px 0 -6px #f6f6f6, - 0 17px 2px -6px rgba(0, 0, 0, 0.2); -} - -#todo-count { - float: left; - text-align: left; -} - -#todo-count strong { - font-weight: 300; -} - -#filters { - margin: 0; - padding: 0; - list-style: none; - position: absolute; - right: 0; - left: 0; -} - -#filters li { - display: inline; -} - -#filters li a { - color: inherit; - margin: 3px; - padding: 3px 7px; - text-decoration: none; - border: 1px solid transparent; - border-radius: 3px; -} - -#filters li a.selected, -#filters li a:hover { - border-color: rgba(175, 47, 47, 0.1); -} - -#filters li a.selected { - border-color: rgba(175, 47, 47, 0.2); -} - -#clear-completed, -html #clear-completed:active { - float: right; - position: relative; - line-height: 20px; - text-decoration: none; - cursor: pointer; - visibility: hidden; - position: relative; -} - -#clear-completed::after { - visibility: visible; - content: 'Clear completed'; - position: absolute; - right: 0; - white-space: nowrap; -} - -#clear-completed:hover::after { - text-decoration: underline; -} - -#info { - margin: 65px auto 0; - color: #bfbfbf; - font-size: 10px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - text-align: center; -} - -#info p { - line-height: 1; -} - -#info a { - color: inherit; - text-decoration: none; - font-weight: 400; -} - -#info a:hover { - text-decoration: underline; -} - -/* - Hack to remove background from Mobile Safari. - Can't use it globally since it destroys checkboxes in Firefox -*/ -@media screen and (-webkit-min-device-pixel-ratio:0) { - #toggle-all, - #todo-list li .toggle { - background: none; - } - - #todo-list li .toggle { - height: 40px; - } - - #toggle-all { - -webkit-transform: rotate(90deg); - transform: rotate(90deg); - -webkit-appearance: none; - appearance: none; - } -} - -@media (max-width: 430px) { - #footer { - height: 50px; - } - - #filters { - bottom: 10px; - } -} diff --git a/examples/extjs_deftjs/node_modules/todomvc-common/base.css b/examples/extjs_deftjs/node_modules/todomvc-common/base.css deleted file mode 100644 index da65968a73..0000000000 --- a/examples/extjs_deftjs/node_modules/todomvc-common/base.css +++ /dev/null @@ -1,141 +0,0 @@ -hr { - margin: 20px 0; - border: 0; - border-top: 1px dashed #c5c5c5; - border-bottom: 1px dashed #f7f7f7; -} - -.learn a { - font-weight: normal; - text-decoration: none; - color: #b83f45; -} - -.learn a:hover { - text-decoration: underline; - color: #787e7e; -} - -.learn h3, -.learn h4, -.learn h5 { - margin: 10px 0; - font-weight: 500; - line-height: 1.2; - color: #000; -} - -.learn h3 { - font-size: 24px; -} - -.learn h4 { - font-size: 18px; -} - -.learn h5 { - margin-bottom: 0; - font-size: 14px; -} - -.learn ul { - padding: 0; - margin: 0 0 30px 25px; -} - -.learn li { - line-height: 20px; -} - -.learn p { - font-size: 15px; - font-weight: 300; - line-height: 1.3; - margin-top: 0; - margin-bottom: 0; -} - -#issue-count { - display: none; -} - -.quote { - border: none; - margin: 20px 0 60px 0; -} - -.quote p { - font-style: italic; -} - -.quote p:before { - content: '“'; - font-size: 50px; - opacity: .15; - position: absolute; - top: -20px; - left: 3px; -} - -.quote p:after { - content: '”'; - font-size: 50px; - opacity: .15; - position: absolute; - bottom: -42px; - right: 3px; -} - -.quote footer { - position: absolute; - bottom: -40px; - right: 0; -} - -.quote footer img { - border-radius: 3px; -} - -.quote footer a { - margin-left: 5px; - vertical-align: middle; -} - -.speech-bubble { - position: relative; - padding: 10px; - background: rgba(0, 0, 0, .04); - border-radius: 5px; -} - -.speech-bubble:after { - content: ''; - position: absolute; - top: 100%; - right: 30px; - border: 13px solid transparent; - border-top-color: rgba(0, 0, 0, .04); -} - -.learn-bar > .learn { - position: absolute; - width: 272px; - top: 8px; - left: -300px; - padding: 10px; - border-radius: 5px; - background-color: rgba(255, 255, 255, .6); - transition-property: left; - transition-duration: 500ms; -} - -@media (min-width: 899px) { - .learn-bar { - width: auto; - padding-left: 300px; - } - - .learn-bar > .learn { - left: 8px; - } -} diff --git a/examples/extjs_deftjs/node_modules/todomvc-common/base.js b/examples/extjs_deftjs/node_modules/todomvc-common/base.js deleted file mode 100644 index 44fb50c613..0000000000 --- a/examples/extjs_deftjs/node_modules/todomvc-common/base.js +++ /dev/null @@ -1,244 +0,0 @@ -/* global _ */ -(function () { - 'use strict'; - - /* jshint ignore:start */ - // Underscore's Template Module - // Courtesy of underscorejs.org - var _ = (function (_) { - _.defaults = function (object) { - if (!object) { - return object; - } - for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { - var iterable = arguments[argsIndex]; - if (iterable) { - for (var key in iterable) { - if (object[key] == null) { - object[key] = iterable[key]; - } - } - } - } - return object; - } - - // By default, Underscore uses ERB-style template delimiters, change the - // following template settings to use alternative delimiters. - _.templateSettings = { - evaluate : /<%([\s\S]+?)%>/g, - interpolate : /<%=([\s\S]+?)%>/g, - escape : /<%-([\s\S]+?)%>/g - }; - - // When customizing `templateSettings`, if you don't want to define an - // interpolation, evaluation or escaping regex, we need one that is - // guaranteed not to match. - var noMatch = /(.)^/; - - // Certain characters need to be escaped so that they can be put into a - // string literal. - var escapes = { - "'": "'", - '\\': '\\', - '\r': 'r', - '\n': 'n', - '\t': 't', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; - - var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; - - // JavaScript micro-templating, similar to John Resig's implementation. - // Underscore templating handles arbitrary delimiters, preserves whitespace, - // and correctly escapes quotes within interpolated code. - _.template = function(text, data, settings) { - var render; - settings = _.defaults({}, settings, _.templateSettings); - - // Combine delimiters into one regular expression via alternation. - var matcher = new RegExp([ - (settings.escape || noMatch).source, - (settings.interpolate || noMatch).source, - (settings.evaluate || noMatch).source - ].join('|') + '|$', 'g'); - - // Compile the template source, escaping string literals appropriately. - var index = 0; - var source = "__p+='"; - text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { - source += text.slice(index, offset) - .replace(escaper, function(match) { return '\\' + escapes[match]; }); - - if (escape) { - source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; - } - if (interpolate) { - source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; - } - if (evaluate) { - source += "';\n" + evaluate + "\n__p+='"; - } - index = offset + match.length; - return match; - }); - source += "';\n"; - - // If a variable is not specified, place data values in local scope. - if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; - - source = "var __t,__p='',__j=Array.prototype.join," + - "print=function(){__p+=__j.call(arguments,'');};\n" + - source + "return __p;\n"; - - try { - render = new Function(settings.variable || 'obj', '_', source); - } catch (e) { - e.source = source; - throw e; - } - - if (data) return render(data, _); - var template = function(data) { - return render.call(this, data, _); - }; - - // Provide the compiled function source as a convenience for precompilation. - template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; - - return template; - }; - - return _; - })({}); - - if (location.hostname === 'todomvc.com') { - window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')); - } - /* jshint ignore:end */ - - function redirect() { - if (location.hostname === 'tastejs.github.io') { - location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com'); - } - } - - function findRoot() { - var base = location.href.indexOf('examples/'); - return location.href.substr(0, base); - } - - function getFile(file, callback) { - if (!location.host) { - return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.'); - } - - var xhr = new XMLHttpRequest(); - - xhr.open('GET', findRoot() + file, true); - xhr.send(); - - xhr.onload = function () { - if (xhr.status === 200 && callback) { - callback(xhr.responseText); - } - }; - } - - function Learn(learnJSON, config) { - if (!(this instanceof Learn)) { - return new Learn(learnJSON, config); - } - - var template, framework; - - if (typeof learnJSON !== 'object') { - try { - learnJSON = JSON.parse(learnJSON); - } catch (e) { - return; - } - } - - if (config) { - template = config.template; - framework = config.framework; - } - - if (!template && learnJSON.templates) { - template = learnJSON.templates.todomvc; - } - - if (!framework && document.querySelector('[data-framework]')) { - framework = document.querySelector('[data-framework]').dataset.framework; - } - - this.template = template; - - if (learnJSON.backend) { - this.frameworkJSON = learnJSON.backend; - this.frameworkJSON.issueLabel = framework; - this.append({ - backend: true - }); - } else if (learnJSON[framework]) { - this.frameworkJSON = learnJSON[framework]; - this.frameworkJSON.issueLabel = framework; - this.append(); - } - - this.fetchIssueCount(); - } - - Learn.prototype.append = function (opts) { - var aside = document.createElement('aside'); - aside.innerHTML = _.template(this.template, this.frameworkJSON); - aside.className = 'learn'; - - if (opts && opts.backend) { - // Remove demo link - var sourceLinks = aside.querySelector('.source-links'); - var heading = sourceLinks.firstElementChild; - var sourceLink = sourceLinks.lastElementChild; - // Correct link path - var href = sourceLink.getAttribute('href'); - sourceLink.setAttribute('href', href.substr(href.lastIndexOf('http'))); - sourceLinks.innerHTML = heading.outerHTML + sourceLink.outerHTML; - } else { - // Localize demo links - var demoLinks = aside.querySelectorAll('.demo-link'); - Array.prototype.forEach.call(demoLinks, function (demoLink) { - if (demoLink.getAttribute('href').substr(0, 4) !== 'http') { - demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href')); - } - }); - } - - document.body.className = (document.body.className + ' learn-bar').trim(); - document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); - }; - - Learn.prototype.fetchIssueCount = function () { - var issueLink = document.getElementById('issue-count-link'); - if (issueLink) { - var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos'); - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.onload = function (e) { - var parsedResponse = JSON.parse(e.target.responseText); - if (parsedResponse instanceof Array) { - var count = parsedResponse.length - if (count !== 0) { - issueLink.innerHTML = 'This app has ' + count + ' open issues'; - document.getElementById('issue-count').style.display = 'inline'; - } - } - }; - xhr.send(); - } - }; - - redirect(); - getFile('learn.json', Learn); -})(); diff --git a/examples/extjs_deftjs/package.json b/examples/extjs_deftjs/package.json deleted file mode 100644 index 3b1e70fd73..0000000000 --- a/examples/extjs_deftjs/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "private": true, - "dependencies": { - "todomvc-common": "^1.0.1", - "todomvc-app-css": "^1.0.1" - } -} diff --git a/examples/flight/.gitignore b/examples/flight/.gitignore index 21d0fdfe11..c2658d7d1b 100644 --- a/examples/flight/.gitignore +++ b/examples/flight/.gitignore @@ -1,41 +1 @@ -node_modules/.bin/ - -node_modules/depot/ -!node_modules/depot/depot.js - -node_modules/es5-shim/ -!node_modules/es5-shim/es5-shim.js -!node_modules/es5-shim/es5-sham.js - -node_modules/jquery/ -!node_modules/jquery/dist -node_modules/jquery/dist/ -!node_modules/jquery/dist/jquery.js - -node_modules/flight/ -!node_modules/flight/lib - -node_modules/requirejs/ -!node_modules/requirejs/require.js - -node_modules/requirejs-text/ -!node_modules/requirejs-text/text.js - -node_modules/todomvc-app-css/ -!node_modules/todomvc-app-css/index.css - -node_modules/todomvc-common/ -!node_modules/todomvc-common/base.css -!node_modules/todomvc-common/base.js - - -node_modules/karma/ -node_modules/karma-chrome-launcher/ -node_modules/karma-firefox-launcher/ -node_modules/karma-ie-launcher/ -node_modules/karma-jasmine/ -node_modules/karma-phantomjs-launcher/ -node_modules/jasmine-jquery/ -node_modules/jasmine-flight/ -node_modules/karma-safari-launcher/ -node_modules/karma-requirejs/ +node_modules/ diff --git a/examples/flight/app/js/main.js b/examples/flight/app/js/main.js index 49f8fe6d19..4bd38d6906 100644 --- a/examples/flight/app/js/main.js +++ b/examples/flight/app/js/main.js @@ -4,12 +4,12 @@ require.config({ baseUrl: './', paths: { - jquery: 'node_modules/jquery/dist/jquery', - es5shim: 'node_modules/es5-shim/es5-shim', - es5sham: 'node_modules/es5-shim/es5-sham', - text: 'node_modules/requirejs-text/text', - flight: 'node_modules/flight', - depot: 'node_modules/depot/depot', + jquery: 'bower_components/jquery/dist/jquery', + es5shim: 'bower_components/es5-shim/es5-shim', + es5sham: 'bower_components/es5-shim/es5-sham', + text: 'bower_components/requirejs-text/text', + flight: 'bower_components/flight', + depot: 'bower_components/depot/depot', app: 'app/js', templates: 'app/templates', ui: 'app/js/ui', diff --git a/examples/flight/bower.json b/examples/flight/bower.json new file mode 100644 index 0000000000..a65d42fa17 --- /dev/null +++ b/examples/flight/bower.json @@ -0,0 +1,16 @@ +{ + "name": "flight-todomvc", + "version": "0.0.0", + "dependencies": { + "depot": "~0.1.6", + "flight": "~1.3.0", + "jquery": "2.1.0", + "requirejs": "~2.1.15", + "todomvc-common": "~0.3.0", + "requirejs-text": "~2.0.13" + }, + "devDependencies": { + "jasmine-flight": "~4.0.0", + "jasmine-jquery": "~2.0.5" + } +} diff --git a/examples/flight/node_modules/depot/depot.js b/examples/flight/bower_components/depot/depot.js similarity index 88% rename from examples/flight/node_modules/depot/depot.js rename to examples/flight/bower_components/depot/depot.js index 5f8382bb0f..45c0946f18 100644 --- a/examples/flight/node_modules/depot/depot.js +++ b/examples/flight/bower_components/depot/depot.js @@ -21,9 +21,7 @@ var api = { save: function (record) { - var id, ids; - - this.refresh(); + var id; if (!record[this.idAttribute]) { record[this.idAttribute] = guid(); @@ -33,9 +31,7 @@ if (this.ids.indexOf(id) < 0) { this.ids.push(id); - ids = this.ids.join(","); - this.storageAdaptor.setItem(this.name, ids); - this.store = ids; + this.storageAdaptor.setItem(this.name, this.ids.join(",")); } this.storageAdaptor.setItem(getKey(this.name, id), JSON.stringify(record)); @@ -77,8 +73,6 @@ if (!criteria) return this.all(); - this.refresh(); - return this.ids.reduce(function (memo, id) { record = jsonData(self.storageAdaptor.getItem(getKey(name, id))); match = findMatch(criteria, record); @@ -98,8 +92,6 @@ all: function () { var record, self = this, name = this.name; - this.refresh(); - return this.ids.reduce(function (memo, id) { record = self.storageAdaptor.getItem(getKey(name, id)); @@ -129,8 +121,6 @@ destroyAll: function (criteria) { var attr, id, match, record, key; - this.refresh(); - for (var i = this.ids.length - 1; i >= 0; i--) { id = this.ids[i]; key = getKey(this.name, id); @@ -161,20 +151,7 @@ }, size: function () { - this.refresh(); - return this.ids.length; - }, - - refresh: function () { - var store = this.storageAdaptor.getItem(this.name); - - if (this.store && this.store === store) { - return; - } - - this.ids = (store && store.split(",")) || []; - this.store = store; } }; @@ -225,7 +202,7 @@ } function depot(name, options) { - var instance; + var store, ids; options = extend({ idAttribute: '_id', @@ -234,15 +211,16 @@ if (!options.storageAdaptor) throw new Error("No storage adaptor was found"); - instance = Object.create(api, { + store = options.storageAdaptor.getItem(name); + ids = (store && store.split(",")) || []; + + return Object.create(api, { name: { value: name }, + store: { value: store }, + ids: { value: ids, writable: true }, idAttribute: { value: options.idAttribute }, storageAdaptor: { value: options.storageAdaptor } }); - - instance.refresh(); - - return instance; } return depot; diff --git a/examples/flight/node_modules/es5-shim/es5-sham.js b/examples/flight/bower_components/es5-shim/es5-sham.js similarity index 68% rename from examples/flight/node_modules/es5-shim/es5-sham.js rename to examples/flight/bower_components/es5-shim/es5-sham.js index 20c9845975..a1b46cbc1f 100644 --- a/examples/flight/node_modules/es5-shim/es5-sham.js +++ b/examples/flight/bower_components/es5-shim/es5-sham.js @@ -1,8 +1,6 @@ // Copyright 2009-2012 by contributors, MIT License // vim: ts=4 sts=4 sw=4 expandtab -//Add semicolon to prevent IIFE from being passed as argument to concated code. -; // Module systems magic dance (function (definition) { // RequireJS @@ -17,28 +15,10 @@ } })(function () { - -var call = Function.prototype.call; -var prototypeOfObject = Object.prototype; -var owns = call.bind(prototypeOfObject.hasOwnProperty); - -// If JS engine supports accessors creating shortcuts. -var defineGetter; -var defineSetter; -var lookupGetter; -var lookupSetter; -var supportsAccessors; -if ((supportsAccessors = owns(prototypeOfObject, "__defineGetter__"))) { - defineGetter = call.bind(prototypeOfObject.__defineGetter__); - defineSetter = call.bind(prototypeOfObject.__defineSetter__); - lookupGetter = call.bind(prototypeOfObject.__lookupGetter__); - lookupSetter = call.bind(prototypeOfObject.__lookupSetter__); -} - // ES5 15.2.3.2 // http://es5.github.com/#x15.2.3.2 if (!Object.getPrototypeOf) { - // https://github.com/es-shims/es5-shim/issues#issue/2 + // https://github.com/kriskowal/es5-shim/issues#issue/2 // http://ejohn.org/blog/objectgetprototypeof/ // recommended by fschaefer on github Object.getPrototypeOf = function getPrototypeOf(object) { @@ -50,53 +30,15 @@ if (!Object.getPrototypeOf) { }; } -//ES5 15.2.3.3 -//http://es5.github.com/#x15.2.3.3 - -function doesGetOwnPropertyDescriptorWork(object) { - try { - object.sentinel = 0; - return Object.getOwnPropertyDescriptor( - object, - "sentinel" - ).value === 0; - } catch (exception) { - // returns falsy - } -} - -//check whether getOwnPropertyDescriptor works if it's given. Otherwise, -//shim partially. -if (Object.defineProperty) { - var getOwnPropertyDescriptorWorksOnObject = - doesGetOwnPropertyDescriptorWork({}); - var getOwnPropertyDescriptorWorksOnDom = typeof document == "undefined" || - doesGetOwnPropertyDescriptorWork(document.createElement("div")); - if (!getOwnPropertyDescriptorWorksOnDom || - !getOwnPropertyDescriptorWorksOnObject - ) { - var getOwnPropertyDescriptorFallback = Object.getOwnPropertyDescriptor; - } -} - -if (!Object.getOwnPropertyDescriptor || getOwnPropertyDescriptorFallback) { +// ES5 15.2.3.3 +// http://es5.github.com/#x15.2.3.3 +if (!Object.getOwnPropertyDescriptor) { var ERR_NON_OBJECT = "Object.getOwnPropertyDescriptor called on a non-object: "; Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(object, property) { if ((typeof object != "object" && typeof object != "function") || object === null) { throw new TypeError(ERR_NON_OBJECT + object); } - - // make a valiant attempt to use the real getOwnPropertyDescriptor - // for I8's DOM elements. - if (getOwnPropertyDescriptorFallback) { - try { - return getOwnPropertyDescriptorFallback.call(Object, object, property); - } catch (exception) { - // try the shim if the real one doesn't work - } - } - // If object does not owns property return undefined immediately. if (!owns(object, property)) { return; @@ -139,7 +81,6 @@ if (!Object.getOwnPropertyDescriptor || getOwnPropertyDescriptorFallback) { // If we got this far we know that object has an own property that is // not an accessor so we set it as a value and return descriptor. descriptor.value = object[property]; - descriptor.writable = true; return descriptor; }; } @@ -155,64 +96,15 @@ if (!Object.getOwnPropertyNames) { // ES5 15.2.3.5 // http://es5.github.com/#x15.2.3.5 if (!Object.create) { - - // Contributed by Brandon Benvie, October, 2012 - var createEmpty; - var supportsProto = Object.prototype.__proto__ === null; - if (supportsProto || typeof document == 'undefined') { - createEmpty = function () { - return { "__proto__": null }; - }; - } else { - // In old IE __proto__ can't be used to manually set `null`, nor does - // any other method exist to make an object that inherits from nothing, - // aside from Object.prototype itself. Instead, create a new global - // object and *steal* its Object.prototype and strip it bare. This is - // used as the prototype to create nullary objects. - createEmpty = function () { - var iframe = document.createElement('iframe'); - var parent = document.body || document.documentElement; - iframe.style.display = 'none'; - parent.appendChild(iframe); - iframe.src = 'javascript:'; - var empty = iframe.contentWindow.Object.prototype; - parent.removeChild(iframe); - iframe = null; - delete empty.constructor; - delete empty.hasOwnProperty; - delete empty.propertyIsEnumerable; - delete empty.isPrototypeOf; - delete empty.toLocaleString; - delete empty.toString; - delete empty.valueOf; - empty.__proto__ = null; - - function Empty() {} - Empty.prototype = empty; - // short-circuit future calls - createEmpty = function () { - return new Empty(); - }; - return new Empty(); - }; - } - Object.create = function create(prototype, properties) { - var object; - function Type() {} // An empty constructor. - if (prototype === null) { - object = createEmpty(); + object = { "__proto__": null }; } else { - if (typeof prototype !== "object" && typeof prototype !== "function") { - // In the native implementation `parent` can be `null` - // OR *any* `instanceof Object` (Object|Function|Array|RegExp|etc) - // Use `typeof` tho, b/c in old IE, DOM elements are not `instanceof Object` - // like they are in modern browsers. Using `Object.create` on DOM elements - // is...err...probably inappropriate, but the native version allows for it. - throw new TypeError("Object prototype may only be an Object or null"); // same msg as Chrome + if (typeof prototype != "object") { + throw new TypeError("typeof prototype["+(typeof prototype)+"] != 'object'"); } + var Type = function () {}; Type.prototype = prototype; object = new Type(); // IE has no built-in implementation of `Object.getPrototypeOf` @@ -221,11 +113,9 @@ if (!Object.create) { // objects created using `Object.create` object.__proto__ = prototype; } - if (properties !== void 0) { Object.defineProperties(object, properties); } - return object; }; } @@ -235,7 +125,7 @@ if (!Object.create) { // Patch for WebKit and IE8 standard mode // Designed by hax -// related issue: https://github.com/es-shims/es5-shim/issues#issue/5 +// related issue: https://github.com/kriskowal/es5-shim/issues#issue/5 // IE8 Reference: // http://msdn.microsoft.com/en-us/library/dd282900.aspx // http://msdn.microsoft.com/en-us/library/dd229916.aspx @@ -258,8 +148,7 @@ if (Object.defineProperty) { var definePropertyWorksOnDom = typeof document == "undefined" || doesDefinePropertyWork(document.createElement("div")); if (!definePropertyWorksOnObject || !definePropertyWorksOnDom) { - var definePropertyFallback = Object.defineProperty, - definePropertiesFallback = Object.defineProperties; + var definePropertyFallback = Object.defineProperty; } } @@ -339,17 +228,8 @@ if (!Object.defineProperty || definePropertyFallback) { // ES5 15.2.3.7 // http://es5.github.com/#x15.2.3.7 -if (!Object.defineProperties || definePropertiesFallback) { +if (!Object.defineProperties) { Object.defineProperties = function defineProperties(object, properties) { - // make a valiant attempt to use the real defineProperties - if (definePropertiesFallback) { - try { - return definePropertiesFallback.call(Object, object, properties); - } catch (exception) { - // try the shim if the real one doesn't work - } - } - for (var property in properties) { if (owns(properties, property) && property != "__proto__") { Object.defineProperty(object, property, properties[property]); diff --git a/examples/flight/bower_components/es5-shim/es5-shim.js b/examples/flight/bower_components/es5-shim/es5-shim.js new file mode 100644 index 0000000000..d59197db45 --- /dev/null +++ b/examples/flight/bower_components/es5-shim/es5-shim.js @@ -0,0 +1,778 @@ +// Copyright 2009-2012 by contributors, MIT License +// vim: ts=4 sts=4 sw=4 expandtab + +// Module systems magic dance +(function (definition) { + // RequireJS + if (typeof define == "function") { + define(definition); + // YUI3 + } else if (typeof YUI == "function") { + YUI.add("es5", definition); + // CommonJS and - + + diff --git a/examples/flight/karma.conf.js b/examples/flight/karma.conf.js index 11ba3f040c..ac727a056b 100644 --- a/examples/flight/karma.conf.js +++ b/examples/flight/karma.conf.js @@ -15,22 +15,21 @@ module.exports = function (config) { // list of files / patterns to load in the browser files: [ // loaded without require - 'node_modules/es5-shim/es5-shim.js', - 'node_modules/es5-shim/es5-sham.js', + 'bower_components/es5-shim/es5-shim.js', + 'bower_components/es5-shim/es5-sham.js', - 'node_modules/jquery/dist/jquery.js', - 'node_modules/jasmine-jquery/lib/jasmine-jquery.js', - 'node_modules/jasmine-flight/lib/jasmine-flight.js', + 'bower_components/jquery/dist/jquery.js', + 'bower_components/jasmine-jquery/lib/jasmine-jquery.js', + 'bower_components/jasmine-flight/lib/jasmine-flight.js', // hack to load RequireJS after the shim libs 'node_modules/requirejs/require.js', 'node_modules/karma-requirejs/lib/adapter.js', // loaded with require - { pattern: 'node_modules/flight/lib/*.js', included: false }, - { pattern: 'node_modules/flight/index.js', included: false }, - { pattern: 'node_modules/depot/depot.js', included: false }, - { pattern: 'node_modules/requirejs-text/text.js', included: false }, + { pattern: 'bower_components/flight/**/*.js', included: false }, + { pattern: 'bower_components/depot/**/*.js', included: false }, + { pattern: 'bower_components/requirejs-text/text.js', included: false }, { pattern: 'app/**/*.js', included: false }, { pattern: 'app/**/*.html', included: false }, { pattern: 'test/spec/**/*_spec.js', included: false }, diff --git a/examples/flight/node_modules/es5-shim/es5-shim.js b/examples/flight/node_modules/es5-shim/es5-shim.js deleted file mode 100644 index 8a16d393a1..0000000000 --- a/examples/flight/node_modules/es5-shim/es5-shim.js +++ /dev/null @@ -1,1360 +0,0 @@ -// Copyright 2009-2012 by contributors, MIT License -// vim: ts=4 sts=4 sw=4 expandtab - -//Add semicolon to prevent IIFE from being passed as argument to concated code. -; -// Module systems magic dance -(function (definition) { - // RequireJS - if (typeof define == "function") { - define(definition); - // YUI3 - } else if (typeof YUI == "function") { - YUI.add("es5", definition); - // CommonJS and - - - - + + + + diff --git a/examples/jquery/node_modules/todomvc-app-css/index.css b/examples/jquery/node_modules/todomvc-app-css/index.css deleted file mode 100644 index 4308848800..0000000000 --- a/examples/jquery/node_modules/todomvc-app-css/index.css +++ /dev/null @@ -1,394 +0,0 @@ -html, -body { - margin: 0; - padding: 0; -} - -button { - margin: 0; - padding: 0; - border: 0; - background: none; - font-size: 100%; - vertical-align: baseline; - font-family: inherit; - font-weight: inherit; - color: inherit; - -webkit-appearance: none; - -ms-appearance: none; - appearance: none; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; -} - -body { - font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; - line-height: 1.4em; - background: #f5f5f5; - color: #4d4d4d; - min-width: 230px; - max-width: 550px; - margin: 0 auto; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; - font-weight: 300; -} - -button, -input[type="checkbox"] { - outline: none; -} - -.hidden { - display: none; -} - -#todoapp { - background: #fff; - margin: 130px 0 40px 0; - position: relative; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), - 0 25px 50px 0 rgba(0, 0, 0, 0.1); -} - -#todoapp input::-webkit-input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp input::-moz-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp input::input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp h1 { - position: absolute; - top: -155px; - width: 100%; - font-size: 100px; - font-weight: 100; - text-align: center; - color: rgba(175, 47, 47, 0.15); - -webkit-text-rendering: optimizeLegibility; - -moz-text-rendering: optimizeLegibility; - -ms-text-rendering: optimizeLegibility; - text-rendering: optimizeLegibility; -} - -#new-todo, -.edit { - position: relative; - margin: 0; - width: 100%; - font-size: 24px; - font-family: inherit; - font-weight: inherit; - line-height: 1.4em; - border: 0; - outline: none; - color: inherit; - padding: 6px; - border: 1px solid #999; - box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); - -ms-box-sizing: border-box; - box-sizing: border-box; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; -} - -#new-todo { - padding: 16px 16px 16px 60px; - border: none; - background: rgba(0, 0, 0, 0.003); - box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); -} - -#main { - position: relative; - z-index: 2; - border-top: 1px solid #e6e6e6; -} - -label[for='toggle-all'] { - display: none; -} - -#toggle-all { - position: absolute; - top: -55px; - left: -12px; - width: 60px; - height: 34px; - text-align: center; - border: none; /* Mobile Safari */ -} - -#toggle-all:before { - content: '❯'; - font-size: 22px; - color: #e6e6e6; - padding: 10px 27px 10px 27px; -} - -#toggle-all:checked:before { - color: #737373; -} - -#todo-list { - margin: 0; - padding: 0; - list-style: none; -} - -#todo-list li { - position: relative; - font-size: 24px; - border-bottom: 1px solid #ededed; -} - -#todo-list li:last-child { - border-bottom: none; -} - -#todo-list li.editing { - border-bottom: none; - padding: 0; -} - -#todo-list li.editing .edit { - display: block; - width: 506px; - padding: 13px 17px 12px 17px; - margin: 0 0 0 43px; -} - -#todo-list li.editing .view { - display: none; -} - -#todo-list li .toggle { - text-align: center; - width: 40px; - /* auto, since non-WebKit browsers doesn't support input styling */ - height: auto; - position: absolute; - top: 0; - bottom: 0; - margin: auto 0; - border: none; /* Mobile Safari */ - -webkit-appearance: none; - -ms-appearance: none; - appearance: none; -} - -#todo-list li .toggle:after { - content: url('data:image/svg+xml;utf8,'); -} - -#todo-list li .toggle:checked:after { - content: url('data:image/svg+xml;utf8,'); -} - -#todo-list li label { - white-space: pre; - word-break: break-word; - padding: 15px 60px 15px 15px; - margin-left: 45px; - display: block; - line-height: 1.2; - transition: color 0.4s; -} - -#todo-list li.completed label { - color: #d9d9d9; - text-decoration: line-through; -} - -#todo-list li .destroy { - display: none; - position: absolute; - top: 0; - right: 10px; - bottom: 0; - width: 40px; - height: 40px; - margin: auto 0; - font-size: 30px; - color: #cc9a9a; - margin-bottom: 11px; - transition: color 0.2s ease-out; -} - -#todo-list li .destroy:hover { - color: #af5b5e; -} - -#todo-list li .destroy:after { - content: '×'; -} - -#todo-list li:hover .destroy { - display: block; -} - -#todo-list li .edit { - display: none; -} - -#todo-list li.editing:last-child { - margin-bottom: -1px; -} - -#footer { - color: #777; - padding: 10px 15px; - height: 20px; - text-align: center; - border-top: 1px solid #e6e6e6; -} - -#footer:before { - content: ''; - position: absolute; - right: 0; - bottom: 0; - left: 0; - height: 50px; - overflow: hidden; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), - 0 8px 0 -3px #f6f6f6, - 0 9px 1px -3px rgba(0, 0, 0, 0.2), - 0 16px 0 -6px #f6f6f6, - 0 17px 2px -6px rgba(0, 0, 0, 0.2); -} - -#todo-count { - float: left; - text-align: left; -} - -#todo-count strong { - font-weight: 300; -} - -#filters { - margin: 0; - padding: 0; - list-style: none; - position: absolute; - right: 0; - left: 0; -} - -#filters li { - display: inline; -} - -#filters li a { - color: inherit; - margin: 3px; - padding: 3px 7px; - text-decoration: none; - border: 1px solid transparent; - border-radius: 3px; -} - -#filters li a.selected, -#filters li a:hover { - border-color: rgba(175, 47, 47, 0.1); -} - -#filters li a.selected { - border-color: rgba(175, 47, 47, 0.2); -} - -#clear-completed, -html #clear-completed:active { - float: right; - position: relative; - line-height: 20px; - text-decoration: none; - cursor: pointer; - visibility: hidden; - position: relative; -} - -#clear-completed::after { - visibility: visible; - content: 'Clear completed'; - position: absolute; - right: 0; - white-space: nowrap; -} - -#clear-completed:hover::after { - text-decoration: underline; -} - -#info { - margin: 65px auto 0; - color: #bfbfbf; - font-size: 10px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - text-align: center; -} - -#info p { - line-height: 1; -} - -#info a { - color: inherit; - text-decoration: none; - font-weight: 400; -} - -#info a:hover { - text-decoration: underline; -} - -/* - Hack to remove background from Mobile Safari. - Can't use it globally since it destroys checkboxes in Firefox -*/ -@media screen and (-webkit-min-device-pixel-ratio:0) { - #toggle-all, - #todo-list li .toggle { - background: none; - } - - #todo-list li .toggle { - height: 40px; - } - - #toggle-all { - -webkit-transform: rotate(90deg); - transform: rotate(90deg); - -webkit-appearance: none; - appearance: none; - } -} - -@media (max-width: 430px) { - #footer { - height: 50px; - } - - #filters { - bottom: 10px; - } -} diff --git a/examples/jquery/node_modules/todomvc-common/base.css b/examples/jquery/node_modules/todomvc-common/base.css deleted file mode 100644 index da65968a73..0000000000 --- a/examples/jquery/node_modules/todomvc-common/base.css +++ /dev/null @@ -1,141 +0,0 @@ -hr { - margin: 20px 0; - border: 0; - border-top: 1px dashed #c5c5c5; - border-bottom: 1px dashed #f7f7f7; -} - -.learn a { - font-weight: normal; - text-decoration: none; - color: #b83f45; -} - -.learn a:hover { - text-decoration: underline; - color: #787e7e; -} - -.learn h3, -.learn h4, -.learn h5 { - margin: 10px 0; - font-weight: 500; - line-height: 1.2; - color: #000; -} - -.learn h3 { - font-size: 24px; -} - -.learn h4 { - font-size: 18px; -} - -.learn h5 { - margin-bottom: 0; - font-size: 14px; -} - -.learn ul { - padding: 0; - margin: 0 0 30px 25px; -} - -.learn li { - line-height: 20px; -} - -.learn p { - font-size: 15px; - font-weight: 300; - line-height: 1.3; - margin-top: 0; - margin-bottom: 0; -} - -#issue-count { - display: none; -} - -.quote { - border: none; - margin: 20px 0 60px 0; -} - -.quote p { - font-style: italic; -} - -.quote p:before { - content: '“'; - font-size: 50px; - opacity: .15; - position: absolute; - top: -20px; - left: 3px; -} - -.quote p:after { - content: '”'; - font-size: 50px; - opacity: .15; - position: absolute; - bottom: -42px; - right: 3px; -} - -.quote footer { - position: absolute; - bottom: -40px; - right: 0; -} - -.quote footer img { - border-radius: 3px; -} - -.quote footer a { - margin-left: 5px; - vertical-align: middle; -} - -.speech-bubble { - position: relative; - padding: 10px; - background: rgba(0, 0, 0, .04); - border-radius: 5px; -} - -.speech-bubble:after { - content: ''; - position: absolute; - top: 100%; - right: 30px; - border: 13px solid transparent; - border-top-color: rgba(0, 0, 0, .04); -} - -.learn-bar > .learn { - position: absolute; - width: 272px; - top: 8px; - left: -300px; - padding: 10px; - border-radius: 5px; - background-color: rgba(255, 255, 255, .6); - transition-property: left; - transition-duration: 500ms; -} - -@media (min-width: 899px) { - .learn-bar { - width: auto; - padding-left: 300px; - } - - .learn-bar > .learn { - left: 8px; - } -} diff --git a/examples/jquery/node_modules/todomvc-common/base.js b/examples/jquery/node_modules/todomvc-common/base.js deleted file mode 100644 index 44fb50c613..0000000000 --- a/examples/jquery/node_modules/todomvc-common/base.js +++ /dev/null @@ -1,244 +0,0 @@ -/* global _ */ -(function () { - 'use strict'; - - /* jshint ignore:start */ - // Underscore's Template Module - // Courtesy of underscorejs.org - var _ = (function (_) { - _.defaults = function (object) { - if (!object) { - return object; - } - for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { - var iterable = arguments[argsIndex]; - if (iterable) { - for (var key in iterable) { - if (object[key] == null) { - object[key] = iterable[key]; - } - } - } - } - return object; - } - - // By default, Underscore uses ERB-style template delimiters, change the - // following template settings to use alternative delimiters. - _.templateSettings = { - evaluate : /<%([\s\S]+?)%>/g, - interpolate : /<%=([\s\S]+?)%>/g, - escape : /<%-([\s\S]+?)%>/g - }; - - // When customizing `templateSettings`, if you don't want to define an - // interpolation, evaluation or escaping regex, we need one that is - // guaranteed not to match. - var noMatch = /(.)^/; - - // Certain characters need to be escaped so that they can be put into a - // string literal. - var escapes = { - "'": "'", - '\\': '\\', - '\r': 'r', - '\n': 'n', - '\t': 't', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; - - var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; - - // JavaScript micro-templating, similar to John Resig's implementation. - // Underscore templating handles arbitrary delimiters, preserves whitespace, - // and correctly escapes quotes within interpolated code. - _.template = function(text, data, settings) { - var render; - settings = _.defaults({}, settings, _.templateSettings); - - // Combine delimiters into one regular expression via alternation. - var matcher = new RegExp([ - (settings.escape || noMatch).source, - (settings.interpolate || noMatch).source, - (settings.evaluate || noMatch).source - ].join('|') + '|$', 'g'); - - // Compile the template source, escaping string literals appropriately. - var index = 0; - var source = "__p+='"; - text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { - source += text.slice(index, offset) - .replace(escaper, function(match) { return '\\' + escapes[match]; }); - - if (escape) { - source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; - } - if (interpolate) { - source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; - } - if (evaluate) { - source += "';\n" + evaluate + "\n__p+='"; - } - index = offset + match.length; - return match; - }); - source += "';\n"; - - // If a variable is not specified, place data values in local scope. - if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; - - source = "var __t,__p='',__j=Array.prototype.join," + - "print=function(){__p+=__j.call(arguments,'');};\n" + - source + "return __p;\n"; - - try { - render = new Function(settings.variable || 'obj', '_', source); - } catch (e) { - e.source = source; - throw e; - } - - if (data) return render(data, _); - var template = function(data) { - return render.call(this, data, _); - }; - - // Provide the compiled function source as a convenience for precompilation. - template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; - - return template; - }; - - return _; - })({}); - - if (location.hostname === 'todomvc.com') { - window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')); - } - /* jshint ignore:end */ - - function redirect() { - if (location.hostname === 'tastejs.github.io') { - location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com'); - } - } - - function findRoot() { - var base = location.href.indexOf('examples/'); - return location.href.substr(0, base); - } - - function getFile(file, callback) { - if (!location.host) { - return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.'); - } - - var xhr = new XMLHttpRequest(); - - xhr.open('GET', findRoot() + file, true); - xhr.send(); - - xhr.onload = function () { - if (xhr.status === 200 && callback) { - callback(xhr.responseText); - } - }; - } - - function Learn(learnJSON, config) { - if (!(this instanceof Learn)) { - return new Learn(learnJSON, config); - } - - var template, framework; - - if (typeof learnJSON !== 'object') { - try { - learnJSON = JSON.parse(learnJSON); - } catch (e) { - return; - } - } - - if (config) { - template = config.template; - framework = config.framework; - } - - if (!template && learnJSON.templates) { - template = learnJSON.templates.todomvc; - } - - if (!framework && document.querySelector('[data-framework]')) { - framework = document.querySelector('[data-framework]').dataset.framework; - } - - this.template = template; - - if (learnJSON.backend) { - this.frameworkJSON = learnJSON.backend; - this.frameworkJSON.issueLabel = framework; - this.append({ - backend: true - }); - } else if (learnJSON[framework]) { - this.frameworkJSON = learnJSON[framework]; - this.frameworkJSON.issueLabel = framework; - this.append(); - } - - this.fetchIssueCount(); - } - - Learn.prototype.append = function (opts) { - var aside = document.createElement('aside'); - aside.innerHTML = _.template(this.template, this.frameworkJSON); - aside.className = 'learn'; - - if (opts && opts.backend) { - // Remove demo link - var sourceLinks = aside.querySelector('.source-links'); - var heading = sourceLinks.firstElementChild; - var sourceLink = sourceLinks.lastElementChild; - // Correct link path - var href = sourceLink.getAttribute('href'); - sourceLink.setAttribute('href', href.substr(href.lastIndexOf('http'))); - sourceLinks.innerHTML = heading.outerHTML + sourceLink.outerHTML; - } else { - // Localize demo links - var demoLinks = aside.querySelectorAll('.demo-link'); - Array.prototype.forEach.call(demoLinks, function (demoLink) { - if (demoLink.getAttribute('href').substr(0, 4) !== 'http') { - demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href')); - } - }); - } - - document.body.className = (document.body.className + ' learn-bar').trim(); - document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); - }; - - Learn.prototype.fetchIssueCount = function () { - var issueLink = document.getElementById('issue-count-link'); - if (issueLink) { - var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos'); - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.onload = function (e) { - var parsedResponse = JSON.parse(e.target.responseText); - if (parsedResponse instanceof Array) { - var count = parsedResponse.length - if (count !== 0) { - issueLink.innerHTML = 'This app has ' + count + ' open issues'; - document.getElementById('issue-count').style.display = 'inline'; - } - } - }; - xhr.send(); - } - }; - - redirect(); - getFile('learn.json', Learn); -})(); diff --git a/examples/jquery/package.json b/examples/jquery/package.json deleted file mode 100644 index da23cec9ef..0000000000 --- a/examples/jquery/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "todomvc-jquery", - "description": "TodoMVC implementation in Ampersand.js", - "private": true, - "dependencies": { - "todomvc-common": "~1.0.1", - "todomvc-app-css": "~1.0.1", - "jquery": "~2.1.1", - "handlebars": "~2.0.0", - "director": "~1.2.2" - } -} diff --git a/examples/meteor/.meteor/.id b/examples/meteor/.meteor/.id deleted file mode 100644 index d9665f1f41..0000000000 --- a/examples/meteor/.meteor/.id +++ /dev/null @@ -1,7 +0,0 @@ -# This file contains a token that is unique to your project. -# Check it into your repository along with the rest of this directory. -# It can be used for purposes such as: -# - ensuring you don't accidentally deploy one app on top of another -# - providing package authors with aggregated statistics - -181lbexnajpbf74ep8 diff --git a/examples/meteor/.meteor/platforms b/examples/meteor/.meteor/platforms deleted file mode 100644 index 8a3a35f9f6..0000000000 --- a/examples/meteor/.meteor/platforms +++ /dev/null @@ -1,2 +0,0 @@ -browser -server diff --git a/examples/meteor/.meteor/versions b/examples/meteor/.meteor/versions deleted file mode 100644 index 754058a245..0000000000 --- a/examples/meteor/.meteor/versions +++ /dev/null @@ -1,49 +0,0 @@ -application-configuration@1.0.4 -autoupdate@1.1.5 -base64@1.0.2 -binary-heap@1.0.2 -blaze@2.0.4 -blaze-tools@1.0.2 -boilerplate-generator@1.0.2 -callback-hook@1.0.2 -check@1.0.4 -ddp@1.0.14 -deps@1.0.6 -ejson@1.0.5 -fastclick@1.0.2 -follower-livedata@1.0.3 -geojson-utils@1.0.2 -html-tools@1.0.3 -htmljs@1.0.3 -http@1.0.10 -id-map@1.0.2 -jquery@1.11.3 -json@1.0.2 -launch-screen@1.0.1 -livedata@1.0.12 -logging@1.0.6 -meteor@1.1.4 -meteor-platform@1.2.1 -minifiers@1.1.3 -minimongo@1.0.6 -mobile-status-bar@1.0.2 -mongo@1.0.11 -observe-sequence@1.0.4 -ordered-dict@1.0.2 -random@1.0.2 -reactive-dict@1.0.5 -reactive-var@1.0.4 -reload@1.1.2 -retry@1.0.2 -routepolicy@1.0.4 -session@1.0.5 -spacebars@1.0.5 -spacebars-compiler@1.0.4 -standard-app-packages@1.0.4 -templating@1.0.11 -tracker@1.0.5 -ui@1.0.5 -underscore@1.0.2 -url@1.0.3 -webapp@1.1.6 -webapp-hashing@1.0.2 diff --git a/examples/plastronjs/.gitignore b/examples/plastronjs/.gitignore deleted file mode 100644 index c85575bcf7..0000000000 --- a/examples/plastronjs/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules/todomvc-app-css -!node_modules/todomvc-app-css/index.css - -node_modules/todomvc-common -!node_modules/todomvc-common/base.js -!node_modules/todomvc-common/base.css diff --git a/examples/plastronjs/bower.json b/examples/plastronjs/bower.json new file mode 100644 index 0000000000..bc9aa3a66d --- /dev/null +++ b/examples/plastronjs/bower.json @@ -0,0 +1,7 @@ +{ + "name": "todomvc-plastronjs", + "version": "0.0.0", + "dependencies": { + "todomvc-common": "~0.3.0" + } +} diff --git a/examples/plastronjs/bower_components/todomvc-common/base.css b/examples/plastronjs/bower_components/todomvc-common/base.css new file mode 100644 index 0000000000..285f531307 --- /dev/null +++ b/examples/plastronjs/bower_components/todomvc-common/base.css @@ -0,0 +1,554 @@ +html, +body { + margin: 0; + padding: 0; +} + +button { + margin: 0; + padding: 0; + border: 0; + background: none; + font-size: 100%; + vertical-align: baseline; + font-family: inherit; + color: inherit; + -webkit-appearance: none; + -ms-appearance: none; + -o-appearance: none; + appearance: none; +} + +body { + font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; + line-height: 1.4em; + background: #eaeaea url('bg.png'); + color: #4d4d4d; + width: 550px; + margin: 0 auto; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + -o-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +button, +input[type="checkbox"] { + outline: none; +} + +#todoapp { + background: #fff; + background: rgba(255, 255, 255, 0.9); + margin: 130px 0 40px 0; + border: 1px solid #ccc; + position: relative; + border-top-left-radius: 2px; + border-top-right-radius: 2px; + box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2), + 0 25px 50px 0 rgba(0, 0, 0, 0.15); +} + +#todoapp:before { + content: ''; + border-left: 1px solid #f5d6d6; + border-right: 1px solid #f5d6d6; + width: 2px; + position: absolute; + top: 0; + left: 40px; + height: 100%; +} + +#todoapp input::-webkit-input-placeholder { + font-style: italic; +} + +#todoapp input::-moz-placeholder { + font-style: italic; + color: #a9a9a9; +} + +#todoapp h1 { + position: absolute; + top: -120px; + width: 100%; + font-size: 70px; + font-weight: bold; + text-align: center; + color: #b3b3b3; + color: rgba(255, 255, 255, 0.3); + text-shadow: -1px -1px rgba(0, 0, 0, 0.2); + -webkit-text-rendering: optimizeLegibility; + -moz-text-rendering: optimizeLegibility; + -ms-text-rendering: optimizeLegibility; + -o-text-rendering: optimizeLegibility; + text-rendering: optimizeLegibility; +} + +#header { + padding-top: 15px; + border-radius: inherit; +} + +#header:before { + content: ''; + position: absolute; + top: 0; + right: 0; + left: 0; + height: 15px; + z-index: 2; + border-bottom: 1px solid #6c615c; + background: #8d7d77; + background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8))); + background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); + background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670'); + border-top-left-radius: 1px; + border-top-right-radius: 1px; +} + +#new-todo, +.edit { + position: relative; + margin: 0; + width: 100%; + font-size: 24px; + font-family: inherit; + line-height: 1.4em; + border: 0; + outline: none; + color: inherit; + padding: 6px; + border: 1px solid #999; + box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + -o-box-sizing: border-box; + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + -o-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +#new-todo { + padding: 16px 16px 16px 60px; + border: none; + background: rgba(0, 0, 0, 0.02); + z-index: 2; + box-shadow: none; +} + +#main { + position: relative; + z-index: 2; + border-top: 1px dotted #adadad; +} + +label[for='toggle-all'] { + display: none; +} + +#toggle-all { + position: absolute; + top: -42px; + left: -4px; + width: 40px; + text-align: center; + /* Mobile Safari */ + border: none; +} + +#toggle-all:before { + content: '»'; + font-size: 28px; + color: #d9d9d9; + padding: 0 25px 7px; +} + +#toggle-all:checked:before { + color: #737373; +} + +#todo-list { + margin: 0; + padding: 0; + list-style: none; +} + +#todo-list li { + position: relative; + font-size: 24px; + border-bottom: 1px dotted #ccc; +} + +#todo-list li:last-child { + border-bottom: none; +} + +#todo-list li.editing { + border-bottom: none; + padding: 0; +} + +#todo-list li.editing .edit { + display: block; + width: 506px; + padding: 13px 17px 12px 17px; + margin: 0 0 0 43px; +} + +#todo-list li.editing .view { + display: none; +} + +#todo-list li .toggle { + text-align: center; + width: 40px; + /* auto, since non-WebKit browsers doesn't support input styling */ + height: auto; + position: absolute; + top: 0; + bottom: 0; + margin: auto 0; + /* Mobile Safari */ + border: none; + -webkit-appearance: none; + -ms-appearance: none; + -o-appearance: none; + appearance: none; +} + +#todo-list li .toggle:after { + content: '✔'; + /* 40 + a couple of pixels visual adjustment */ + line-height: 43px; + font-size: 20px; + color: #d9d9d9; + text-shadow: 0 -1px 0 #bfbfbf; +} + +#todo-list li .toggle:checked:after { + color: #85ada7; + text-shadow: 0 1px 0 #669991; + bottom: 1px; + position: relative; +} + +#todo-list li label { + white-space: pre; + word-break: break-word; + padding: 15px 60px 15px 15px; + margin-left: 45px; + display: block; + line-height: 1.2; + -webkit-transition: color 0.4s; + transition: color 0.4s; +} + +#todo-list li.completed label { + color: #a9a9a9; + text-decoration: line-through; +} + +#todo-list li .destroy { + display: none; + position: absolute; + top: 0; + right: 10px; + bottom: 0; + width: 40px; + height: 40px; + margin: auto 0; + font-size: 22px; + color: #a88a8a; + -webkit-transition: all 0.2s; + transition: all 0.2s; +} + +#todo-list li .destroy:hover { + text-shadow: 0 0 1px #000, + 0 0 10px rgba(199, 107, 107, 0.8); + -webkit-transform: scale(1.3); + transform: scale(1.3); +} + +#todo-list li .destroy:after { + content: '✖'; +} + +#todo-list li:hover .destroy { + display: block; +} + +#todo-list li .edit { + display: none; +} + +#todo-list li.editing:last-child { + margin-bottom: -1px; +} + +#footer { + color: #777; + padding: 0 15px; + position: absolute; + right: 0; + bottom: -31px; + left: 0; + height: 20px; + z-index: 1; + text-align: center; +} + +#footer:before { + content: ''; + position: absolute; + right: 0; + bottom: 31px; + left: 0; + height: 50px; + z-index: -1; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), + 0 6px 0 -3px rgba(255, 255, 255, 0.8), + 0 7px 1px -3px rgba(0, 0, 0, 0.3), + 0 43px 0 -6px rgba(255, 255, 255, 0.8), + 0 44px 2px -6px rgba(0, 0, 0, 0.2); +} + +#todo-count { + float: left; + text-align: left; +} + +#filters { + margin: 0; + padding: 0; + list-style: none; + position: absolute; + right: 0; + left: 0; +} + +#filters li { + display: inline; +} + +#filters li a { + color: #83756f; + margin: 2px; + text-decoration: none; +} + +#filters li a.selected { + font-weight: bold; +} + +#clear-completed { + float: right; + position: relative; + line-height: 20px; + text-decoration: none; + background: rgba(0, 0, 0, 0.1); + font-size: 11px; + padding: 0 10px; + border-radius: 3px; + box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2); +} + +#clear-completed:hover { + background: rgba(0, 0, 0, 0.15); + box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3); +} + +#info { + margin: 65px auto 0; + color: #a6a6a6; + font-size: 12px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); + text-align: center; +} + +#info a { + color: inherit; +} + +/* + Hack to remove background from Mobile Safari. + Can't use it globally since it destroys checkboxes in Firefox and Opera +*/ + +@media screen and (-webkit-min-device-pixel-ratio:0) { + #toggle-all, + #todo-list li .toggle { + background: none; + } + + #todo-list li .toggle { + height: 40px; + } + + #toggle-all { + top: -56px; + left: -15px; + width: 65px; + height: 41px; + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + -webkit-appearance: none; + appearance: none; + } +} + +.hidden { + display: none; +} + +hr { + margin: 20px 0; + border: 0; + border-top: 1px dashed #C5C5C5; + border-bottom: 1px dashed #F7F7F7; +} + +.learn a { + font-weight: normal; + text-decoration: none; + color: #b83f45; +} + +.learn a:hover { + text-decoration: underline; + color: #787e7e; +} + +.learn h3, +.learn h4, +.learn h5 { + margin: 10px 0; + font-weight: 500; + line-height: 1.2; + color: #000; +} + +.learn h3 { + font-size: 24px; +} + +.learn h4 { + font-size: 18px; +} + +.learn h5 { + margin-bottom: 0; + font-size: 14px; +} + +.learn ul { + padding: 0; + margin: 0 0 30px 25px; +} + +.learn li { + line-height: 20px; +} + +.learn p { + font-size: 15px; + font-weight: 300; + line-height: 1.3; + margin-top: 0; + margin-bottom: 0; +} + +.quote { + border: none; + margin: 20px 0 60px 0; +} + +.quote p { + font-style: italic; +} + +.quote p:before { + content: '“'; + font-size: 50px; + opacity: .15; + position: absolute; + top: -20px; + left: 3px; +} + +.quote p:after { + content: '”'; + font-size: 50px; + opacity: .15; + position: absolute; + bottom: -42px; + right: 3px; +} + +.quote footer { + position: absolute; + bottom: -40px; + right: 0; +} + +.quote footer img { + border-radius: 3px; +} + +.quote footer a { + margin-left: 5px; + vertical-align: middle; +} + +.speech-bubble { + position: relative; + padding: 10px; + background: rgba(0, 0, 0, .04); + border-radius: 5px; +} + +.speech-bubble:after { + content: ''; + position: absolute; + top: 100%; + right: 30px; + border: 13px solid transparent; + border-top-color: rgba(0, 0, 0, .04); +} + +.learn-bar > .learn { + position: absolute; + width: 272px; + top: 8px; + left: -300px; + padding: 10px; + border-radius: 5px; + background-color: rgba(255, 255, 255, .6); + -webkit-transition-property: left; + transition-property: left; + -webkit-transition-duration: 500ms; + transition-duration: 500ms; +} + +@media (min-width: 899px) { + .learn-bar { + width: auto; + margin: 0 0 0 300px; + } + + .learn-bar > .learn { + left: 8px; + } + + .learn-bar #todoapp { + width: 550px; + margin: 130px auto 40px auto; + } +} diff --git a/examples/plastronjs/bower_components/todomvc-common/base.js b/examples/plastronjs/bower_components/todomvc-common/base.js new file mode 100644 index 0000000000..d3a15127d8 --- /dev/null +++ b/examples/plastronjs/bower_components/todomvc-common/base.js @@ -0,0 +1,217 @@ +(function () { + 'use strict'; + + // Underscore's Template Module + // Courtesy of underscorejs.org + var _ = (function (_) { + _.defaults = function (object) { + if (!object) { + return object; + } + for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { + var iterable = arguments[argsIndex]; + if (iterable) { + for (var key in iterable) { + if (object[key] == null) { + object[key] = iterable[key]; + } + } + } + } + return object; + } + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + _.template = function(text, data, settings) { + var render; + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = new RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset) + .replace(escaper, function(match) { return '\\' + escapes[match]; }); + + if (escape) { + source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; + } + if (interpolate) { + source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; + } + if (evaluate) { + source += "';\n" + evaluate + "\n__p+='"; + } + index = offset + match.length; + return match; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + "return __p;\n"; + + try { + render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + if (data) return render(data, _); + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled function source as a convenience for precompilation. + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + + return template; + }; + + return _; + })({}); + + if (location.hostname === 'todomvc.com') { + window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')); + } + + function redirect() { + if (location.hostname === 'tastejs.github.io') { + location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com'); + } + } + + function findRoot() { + var base = location.href.indexOf('examples/'); + return location.href.substr(0, base); + } + + function getFile(file, callback) { + if (!location.host) { + return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.'); + } + + var xhr = new XMLHttpRequest(); + + xhr.open('GET', findRoot() + file, true); + xhr.send(); + + xhr.onload = function () { + if (xhr.status === 200 && callback) { + callback(xhr.responseText); + } + }; + } + + function Learn(learnJSON, config) { + if (!(this instanceof Learn)) { + return new Learn(learnJSON, config); + } + + var template, framework; + + if (typeof learnJSON !== 'object') { + try { + learnJSON = JSON.parse(learnJSON); + } catch (e) { + return; + } + } + + if (config) { + template = config.template; + framework = config.framework; + } + + if (!template && learnJSON.templates) { + template = learnJSON.templates.todomvc; + } + + if (!framework && document.querySelector('[data-framework]')) { + framework = document.querySelector('[data-framework]').dataset.framework; + } + + this.template = template; + + if (learnJSON.backend) { + this.frameworkJSON = learnJSON.backend; + this.append({ + backend: true + }); + } else if (learnJSON[framework]) { + this.frameworkJSON = learnJSON[framework]; + this.append(); + } + } + + Learn.prototype.append = function (opts) { + var aside = document.createElement('aside'); + aside.innerHTML = _.template(this.template, this.frameworkJSON); + aside.className = 'learn'; + + if (opts && opts.backend) { + // Remove demo link + var sourceLinks = aside.querySelector('.source-links'); + var heading = sourceLinks.firstElementChild; + var sourceLink = sourceLinks.lastElementChild; + // Correct link path + var href = sourceLink.getAttribute('href'); + sourceLink.setAttribute('href', href.substr(href.lastIndexOf('http'))); + sourceLinks.innerHTML = heading.outerHTML + sourceLink.outerHTML; + } else { + // Localize demo links + var demoLinks = aside.querySelectorAll('.demo-link'); + Array.prototype.forEach.call(demoLinks, function (demoLink) { + if (demoLink.getAttribute('href').substr(0, 4) !== 'http') { + demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href')); + } + }); + } + + document.body.className = (document.body.className + ' learn-bar').trim(); + document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); + }; + + redirect(); + getFile('learn.json', Learn); +})(); diff --git a/examples/plastronjs/bower_components/todomvc-common/bg.png b/examples/plastronjs/bower_components/todomvc-common/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..b2a7600825ee11f21849ed475499c7d1ecbcc870 GIT binary patch literal 2126 zcmV-U2(kBxP)+9y`=HK7nt=~3t000O5Nklm=04hVa_lXl_bm=+M;=eI<%$rO2ta`suh*Sr#POw*?adq!O!CV z*0&b)rkCCEV)1nIuiLBntUH-MJI;&qYgz|d@Nhnn_abi2g`pu4NAMVid3hS%?quz~ zjlJf(x8cj{VS zUVEP1msg9`^OFUhSO5rtXHhRlyU2i>6$BT=glG`{JlctGHrwRIm)6Buu65jLQn^H8 zvl9lZqUBA+%qvM5XC+yY@mfA(YB~XP=e|6<{%$^eU8nrK?v2ccb@Jom5CG>rKAL7J zplHEIavVp|0p1&m!G`Ti?<7iZ;}@G7#Z)}Km%2!FHnQ0*&?PLR)G!HAaHgU#c|$dz zpj#0HmeadIe>`#)=Jjkb{B=FKnU3pc-&Y@j_j6(h4Y~+Uq!^J=LHunx1xi4QXK-Y{&MoT8ky6Vb9c|WhnOwF~c=3zOy8agktliS6# z`#8F`9H)D=bmk9B5MnW&_r#)f$c+;$LSr-@^An8dhc~Iquuv>jOK7pw7LJ;&X0i1C zGMsHdP1Os@ny$$j!>XAii%7bp5k>`pyNA!~epb)()p9zR4Yl6z=U}{CIdh1z(FpAo zQIW!;0zpCyC4*7YLkZCO@cul0R_&zghsA8Ek)z%jNpKa92{@NH+SstX%@}xB*Jk!l}PZ1cClIns~}5^!RncUk*rmA z%SIVgt58EQxLJ+OiFqKkBuwquyZLUp|gr zPUbUbFbBrPd1xO`^C*r-i3p9*F#(OBh!4Wy@aC&*!|O|GXcjDA&YhF{;cD7*o(GS^ z@pQSE7)x_81=Qyje6*LQ#TXu-7(o;S8u3tpDA0_ddj%hmCspeXdhYR}-i9A=C`EoChUYuH~^x!9+|&(Pgb*>Ck<=9j|)*@xyfT zpP#+Kt<}39b|3Wl4fxT(+G?aH>gG^d@MEaUOJRfy55TtFI7^Y)VuMU=7Pp0y55jS~ z+TJ)igMyrqkX;wU8j68iIDtqJYhS_D3_Oem-@g6me_RfiQ}uT_K-A-9qG%}gk3E9c z!8`KgsMNXm)beloPfMql*|&$M>1q`i!cY)RFYnjNYu*gSv@8XebV7%}xYL>6)GJPJ zJpA7K31lcJuIFKSw-{x4FX0K2Az)~Xg<`sOu$4|^-(^XJX4YzoiNRvL zyuY7~26w-P^Zw%6F}shBwV2$02c8RsSUy6dM92diCAxiX3IUqsq5ig6>U_!Vy*q5$ zjm}16tJr+QZ&T?HkkpeIwVX-r1EI=@%Zlt;6g*mj&E!A))%gXJ=x6u#l zcKEP>bx4rqV*k`BBrZ$Mqjt{TsHcxUH>;)iAK}B(Kila&VD%b?6m&^0WK4^&EZNAI z8AMYs_%$D%|M+@+cQqL-hi4yG>h*jwdii!W1{}p>ZhwFYt_4Su1kjY9<-5`!$VNOI1y(EDSH4WpCijE_>al!Nt=^eVER#uJ1=b z;BWF2IgyF5LexV+yec$Kin;Ai@myo;G>zJ&jrdW#ecXhIZph5OYqw_PEfcF56Z5Zu_ZZE#q3Mc+N&1O^7hoh9QR7`+L$cBP5pqG=fqA0uh zUBukaxTFmH)<|Xbvp2c=HSbNkpXw73a5lv7V$jb92#yZ141@$X?F}Jt8gIU7Wm6m9 z?e_;;zsnm6`LZeP>T=$)Kr>Z?kr*UmFqR7zx0C6^bmcsc@1AGtw_rNH>-Xm$d*|Q< zn&1Ln0u7=l&ILs>%CkJp`DiG9F18x4Ne+lg<#i?e7jL%x;4ZnRkN^Mx07*qoM6N<$ Ef(>0N!2kdN literal 0 HcmV?d00001 diff --git a/examples/plastronjs/index.html b/examples/plastronjs/index.html index 746ca16f86..f5a1d940aa 100644 --- a/examples/plastronjs/index.html +++ b/examples/plastronjs/index.html @@ -3,8 +3,7 @@ PlastronJS • TodoMVC - - + - + - - - + + + + diff --git a/examples/react/node_modules/todomvc-app-css/index.css b/examples/react/node_modules/todomvc-app-css/index.css deleted file mode 100644 index 4308848800..0000000000 --- a/examples/react/node_modules/todomvc-app-css/index.css +++ /dev/null @@ -1,394 +0,0 @@ -html, -body { - margin: 0; - padding: 0; -} - -button { - margin: 0; - padding: 0; - border: 0; - background: none; - font-size: 100%; - vertical-align: baseline; - font-family: inherit; - font-weight: inherit; - color: inherit; - -webkit-appearance: none; - -ms-appearance: none; - appearance: none; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; -} - -body { - font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; - line-height: 1.4em; - background: #f5f5f5; - color: #4d4d4d; - min-width: 230px; - max-width: 550px; - margin: 0 auto; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; - font-weight: 300; -} - -button, -input[type="checkbox"] { - outline: none; -} - -.hidden { - display: none; -} - -#todoapp { - background: #fff; - margin: 130px 0 40px 0; - position: relative; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), - 0 25px 50px 0 rgba(0, 0, 0, 0.1); -} - -#todoapp input::-webkit-input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp input::-moz-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp input::input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp h1 { - position: absolute; - top: -155px; - width: 100%; - font-size: 100px; - font-weight: 100; - text-align: center; - color: rgba(175, 47, 47, 0.15); - -webkit-text-rendering: optimizeLegibility; - -moz-text-rendering: optimizeLegibility; - -ms-text-rendering: optimizeLegibility; - text-rendering: optimizeLegibility; -} - -#new-todo, -.edit { - position: relative; - margin: 0; - width: 100%; - font-size: 24px; - font-family: inherit; - font-weight: inherit; - line-height: 1.4em; - border: 0; - outline: none; - color: inherit; - padding: 6px; - border: 1px solid #999; - box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); - -ms-box-sizing: border-box; - box-sizing: border-box; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; -} - -#new-todo { - padding: 16px 16px 16px 60px; - border: none; - background: rgba(0, 0, 0, 0.003); - box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); -} - -#main { - position: relative; - z-index: 2; - border-top: 1px solid #e6e6e6; -} - -label[for='toggle-all'] { - display: none; -} - -#toggle-all { - position: absolute; - top: -55px; - left: -12px; - width: 60px; - height: 34px; - text-align: center; - border: none; /* Mobile Safari */ -} - -#toggle-all:before { - content: '❯'; - font-size: 22px; - color: #e6e6e6; - padding: 10px 27px 10px 27px; -} - -#toggle-all:checked:before { - color: #737373; -} - -#todo-list { - margin: 0; - padding: 0; - list-style: none; -} - -#todo-list li { - position: relative; - font-size: 24px; - border-bottom: 1px solid #ededed; -} - -#todo-list li:last-child { - border-bottom: none; -} - -#todo-list li.editing { - border-bottom: none; - padding: 0; -} - -#todo-list li.editing .edit { - display: block; - width: 506px; - padding: 13px 17px 12px 17px; - margin: 0 0 0 43px; -} - -#todo-list li.editing .view { - display: none; -} - -#todo-list li .toggle { - text-align: center; - width: 40px; - /* auto, since non-WebKit browsers doesn't support input styling */ - height: auto; - position: absolute; - top: 0; - bottom: 0; - margin: auto 0; - border: none; /* Mobile Safari */ - -webkit-appearance: none; - -ms-appearance: none; - appearance: none; -} - -#todo-list li .toggle:after { - content: url('data:image/svg+xml;utf8,'); -} - -#todo-list li .toggle:checked:after { - content: url('data:image/svg+xml;utf8,'); -} - -#todo-list li label { - white-space: pre; - word-break: break-word; - padding: 15px 60px 15px 15px; - margin-left: 45px; - display: block; - line-height: 1.2; - transition: color 0.4s; -} - -#todo-list li.completed label { - color: #d9d9d9; - text-decoration: line-through; -} - -#todo-list li .destroy { - display: none; - position: absolute; - top: 0; - right: 10px; - bottom: 0; - width: 40px; - height: 40px; - margin: auto 0; - font-size: 30px; - color: #cc9a9a; - margin-bottom: 11px; - transition: color 0.2s ease-out; -} - -#todo-list li .destroy:hover { - color: #af5b5e; -} - -#todo-list li .destroy:after { - content: '×'; -} - -#todo-list li:hover .destroy { - display: block; -} - -#todo-list li .edit { - display: none; -} - -#todo-list li.editing:last-child { - margin-bottom: -1px; -} - -#footer { - color: #777; - padding: 10px 15px; - height: 20px; - text-align: center; - border-top: 1px solid #e6e6e6; -} - -#footer:before { - content: ''; - position: absolute; - right: 0; - bottom: 0; - left: 0; - height: 50px; - overflow: hidden; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), - 0 8px 0 -3px #f6f6f6, - 0 9px 1px -3px rgba(0, 0, 0, 0.2), - 0 16px 0 -6px #f6f6f6, - 0 17px 2px -6px rgba(0, 0, 0, 0.2); -} - -#todo-count { - float: left; - text-align: left; -} - -#todo-count strong { - font-weight: 300; -} - -#filters { - margin: 0; - padding: 0; - list-style: none; - position: absolute; - right: 0; - left: 0; -} - -#filters li { - display: inline; -} - -#filters li a { - color: inherit; - margin: 3px; - padding: 3px 7px; - text-decoration: none; - border: 1px solid transparent; - border-radius: 3px; -} - -#filters li a.selected, -#filters li a:hover { - border-color: rgba(175, 47, 47, 0.1); -} - -#filters li a.selected { - border-color: rgba(175, 47, 47, 0.2); -} - -#clear-completed, -html #clear-completed:active { - float: right; - position: relative; - line-height: 20px; - text-decoration: none; - cursor: pointer; - visibility: hidden; - position: relative; -} - -#clear-completed::after { - visibility: visible; - content: 'Clear completed'; - position: absolute; - right: 0; - white-space: nowrap; -} - -#clear-completed:hover::after { - text-decoration: underline; -} - -#info { - margin: 65px auto 0; - color: #bfbfbf; - font-size: 10px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - text-align: center; -} - -#info p { - line-height: 1; -} - -#info a { - color: inherit; - text-decoration: none; - font-weight: 400; -} - -#info a:hover { - text-decoration: underline; -} - -/* - Hack to remove background from Mobile Safari. - Can't use it globally since it destroys checkboxes in Firefox -*/ -@media screen and (-webkit-min-device-pixel-ratio:0) { - #toggle-all, - #todo-list li .toggle { - background: none; - } - - #todo-list li .toggle { - height: 40px; - } - - #toggle-all { - -webkit-transform: rotate(90deg); - transform: rotate(90deg); - -webkit-appearance: none; - appearance: none; - } -} - -@media (max-width: 430px) { - #footer { - height: 50px; - } - - #filters { - bottom: 10px; - } -} diff --git a/examples/react/node_modules/todomvc-common/base.css b/examples/react/node_modules/todomvc-common/base.css deleted file mode 100644 index da65968a73..0000000000 --- a/examples/react/node_modules/todomvc-common/base.css +++ /dev/null @@ -1,141 +0,0 @@ -hr { - margin: 20px 0; - border: 0; - border-top: 1px dashed #c5c5c5; - border-bottom: 1px dashed #f7f7f7; -} - -.learn a { - font-weight: normal; - text-decoration: none; - color: #b83f45; -} - -.learn a:hover { - text-decoration: underline; - color: #787e7e; -} - -.learn h3, -.learn h4, -.learn h5 { - margin: 10px 0; - font-weight: 500; - line-height: 1.2; - color: #000; -} - -.learn h3 { - font-size: 24px; -} - -.learn h4 { - font-size: 18px; -} - -.learn h5 { - margin-bottom: 0; - font-size: 14px; -} - -.learn ul { - padding: 0; - margin: 0 0 30px 25px; -} - -.learn li { - line-height: 20px; -} - -.learn p { - font-size: 15px; - font-weight: 300; - line-height: 1.3; - margin-top: 0; - margin-bottom: 0; -} - -#issue-count { - display: none; -} - -.quote { - border: none; - margin: 20px 0 60px 0; -} - -.quote p { - font-style: italic; -} - -.quote p:before { - content: '“'; - font-size: 50px; - opacity: .15; - position: absolute; - top: -20px; - left: 3px; -} - -.quote p:after { - content: '”'; - font-size: 50px; - opacity: .15; - position: absolute; - bottom: -42px; - right: 3px; -} - -.quote footer { - position: absolute; - bottom: -40px; - right: 0; -} - -.quote footer img { - border-radius: 3px; -} - -.quote footer a { - margin-left: 5px; - vertical-align: middle; -} - -.speech-bubble { - position: relative; - padding: 10px; - background: rgba(0, 0, 0, .04); - border-radius: 5px; -} - -.speech-bubble:after { - content: ''; - position: absolute; - top: 100%; - right: 30px; - border: 13px solid transparent; - border-top-color: rgba(0, 0, 0, .04); -} - -.learn-bar > .learn { - position: absolute; - width: 272px; - top: 8px; - left: -300px; - padding: 10px; - border-radius: 5px; - background-color: rgba(255, 255, 255, .6); - transition-property: left; - transition-duration: 500ms; -} - -@media (min-width: 899px) { - .learn-bar { - width: auto; - padding-left: 300px; - } - - .learn-bar > .learn { - left: 8px; - } -} diff --git a/examples/react/node_modules/todomvc-common/base.js b/examples/react/node_modules/todomvc-common/base.js deleted file mode 100644 index 44fb50c613..0000000000 --- a/examples/react/node_modules/todomvc-common/base.js +++ /dev/null @@ -1,244 +0,0 @@ -/* global _ */ -(function () { - 'use strict'; - - /* jshint ignore:start */ - // Underscore's Template Module - // Courtesy of underscorejs.org - var _ = (function (_) { - _.defaults = function (object) { - if (!object) { - return object; - } - for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { - var iterable = arguments[argsIndex]; - if (iterable) { - for (var key in iterable) { - if (object[key] == null) { - object[key] = iterable[key]; - } - } - } - } - return object; - } - - // By default, Underscore uses ERB-style template delimiters, change the - // following template settings to use alternative delimiters. - _.templateSettings = { - evaluate : /<%([\s\S]+?)%>/g, - interpolate : /<%=([\s\S]+?)%>/g, - escape : /<%-([\s\S]+?)%>/g - }; - - // When customizing `templateSettings`, if you don't want to define an - // interpolation, evaluation or escaping regex, we need one that is - // guaranteed not to match. - var noMatch = /(.)^/; - - // Certain characters need to be escaped so that they can be put into a - // string literal. - var escapes = { - "'": "'", - '\\': '\\', - '\r': 'r', - '\n': 'n', - '\t': 't', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; - - var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; - - // JavaScript micro-templating, similar to John Resig's implementation. - // Underscore templating handles arbitrary delimiters, preserves whitespace, - // and correctly escapes quotes within interpolated code. - _.template = function(text, data, settings) { - var render; - settings = _.defaults({}, settings, _.templateSettings); - - // Combine delimiters into one regular expression via alternation. - var matcher = new RegExp([ - (settings.escape || noMatch).source, - (settings.interpolate || noMatch).source, - (settings.evaluate || noMatch).source - ].join('|') + '|$', 'g'); - - // Compile the template source, escaping string literals appropriately. - var index = 0; - var source = "__p+='"; - text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { - source += text.slice(index, offset) - .replace(escaper, function(match) { return '\\' + escapes[match]; }); - - if (escape) { - source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; - } - if (interpolate) { - source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; - } - if (evaluate) { - source += "';\n" + evaluate + "\n__p+='"; - } - index = offset + match.length; - return match; - }); - source += "';\n"; - - // If a variable is not specified, place data values in local scope. - if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; - - source = "var __t,__p='',__j=Array.prototype.join," + - "print=function(){__p+=__j.call(arguments,'');};\n" + - source + "return __p;\n"; - - try { - render = new Function(settings.variable || 'obj', '_', source); - } catch (e) { - e.source = source; - throw e; - } - - if (data) return render(data, _); - var template = function(data) { - return render.call(this, data, _); - }; - - // Provide the compiled function source as a convenience for precompilation. - template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; - - return template; - }; - - return _; - })({}); - - if (location.hostname === 'todomvc.com') { - window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')); - } - /* jshint ignore:end */ - - function redirect() { - if (location.hostname === 'tastejs.github.io') { - location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com'); - } - } - - function findRoot() { - var base = location.href.indexOf('examples/'); - return location.href.substr(0, base); - } - - function getFile(file, callback) { - if (!location.host) { - return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.'); - } - - var xhr = new XMLHttpRequest(); - - xhr.open('GET', findRoot() + file, true); - xhr.send(); - - xhr.onload = function () { - if (xhr.status === 200 && callback) { - callback(xhr.responseText); - } - }; - } - - function Learn(learnJSON, config) { - if (!(this instanceof Learn)) { - return new Learn(learnJSON, config); - } - - var template, framework; - - if (typeof learnJSON !== 'object') { - try { - learnJSON = JSON.parse(learnJSON); - } catch (e) { - return; - } - } - - if (config) { - template = config.template; - framework = config.framework; - } - - if (!template && learnJSON.templates) { - template = learnJSON.templates.todomvc; - } - - if (!framework && document.querySelector('[data-framework]')) { - framework = document.querySelector('[data-framework]').dataset.framework; - } - - this.template = template; - - if (learnJSON.backend) { - this.frameworkJSON = learnJSON.backend; - this.frameworkJSON.issueLabel = framework; - this.append({ - backend: true - }); - } else if (learnJSON[framework]) { - this.frameworkJSON = learnJSON[framework]; - this.frameworkJSON.issueLabel = framework; - this.append(); - } - - this.fetchIssueCount(); - } - - Learn.prototype.append = function (opts) { - var aside = document.createElement('aside'); - aside.innerHTML = _.template(this.template, this.frameworkJSON); - aside.className = 'learn'; - - if (opts && opts.backend) { - // Remove demo link - var sourceLinks = aside.querySelector('.source-links'); - var heading = sourceLinks.firstElementChild; - var sourceLink = sourceLinks.lastElementChild; - // Correct link path - var href = sourceLink.getAttribute('href'); - sourceLink.setAttribute('href', href.substr(href.lastIndexOf('http'))); - sourceLinks.innerHTML = heading.outerHTML + sourceLink.outerHTML; - } else { - // Localize demo links - var demoLinks = aside.querySelectorAll('.demo-link'); - Array.prototype.forEach.call(demoLinks, function (demoLink) { - if (demoLink.getAttribute('href').substr(0, 4) !== 'http') { - demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href')); - } - }); - } - - document.body.className = (document.body.className + ' learn-bar').trim(); - document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); - }; - - Learn.prototype.fetchIssueCount = function () { - var issueLink = document.getElementById('issue-count-link'); - if (issueLink) { - var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos'); - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.onload = function (e) { - var parsedResponse = JSON.parse(e.target.responseText); - if (parsedResponse instanceof Array) { - var count = parsedResponse.length - if (count !== 0) { - issueLink.innerHTML = 'This app has ' + count + ' open issues'; - document.getElementById('issue-count').style.display = 'inline'; - } - } - }; - xhr.send(); - } - }; - - redirect(); - getFile('learn.json', Learn); -})(); diff --git a/examples/react/package.json b/examples/react/package.json deleted file mode 100644 index 75c1b65a91..0000000000 --- a/examples/react/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "private": true, - "dependencies": { - "director": "^1.2.0", - "react": "^0.12.0", - "todomvc-app-css": "^1.0.0", - "todomvc-common": "^1.0.1" - } -} diff --git a/examples/vanillajs/.gitignore b/examples/vanillajs/.gitignore deleted file mode 100644 index c85575bcf7..0000000000 --- a/examples/vanillajs/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules/todomvc-app-css -!node_modules/todomvc-app-css/index.css - -node_modules/todomvc-common -!node_modules/todomvc-common/base.js -!node_modules/todomvc-common/base.css diff --git a/examples/vanillajs/bower.json b/examples/vanillajs/bower.json new file mode 100644 index 0000000000..6cf22d2f6d --- /dev/null +++ b/examples/vanillajs/bower.json @@ -0,0 +1,10 @@ +{ + "name": "todomvc-vanillajs", + "version": "0.0.0", + "dependencies": { + "todomvc-common": "~0.3.0" + }, + "devDependencies": { + "jasmine": "~1.3.1" + } +} diff --git a/examples/vanillajs/bower_components/jasmine/lib/jasmine-core/jasmine-html.js b/examples/vanillajs/bower_components/jasmine/lib/jasmine-core/jasmine-html.js new file mode 100644 index 0000000000..543d56963e --- /dev/null +++ b/examples/vanillajs/bower_components/jasmine/lib/jasmine-core/jasmine-html.js @@ -0,0 +1,681 @@ +jasmine.HtmlReporterHelpers = {}; + +jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) { + var el = document.createElement(type); + + for (var i = 2; i < arguments.length; i++) { + var child = arguments[i]; + + if (typeof child === 'string') { + el.appendChild(document.createTextNode(child)); + } else { + if (child) { + el.appendChild(child); + } + } + } + + for (var attr in attrs) { + if (attr == "className") { + el[attr] = attrs[attr]; + } else { + el.setAttribute(attr, attrs[attr]); + } + } + + return el; +}; + +jasmine.HtmlReporterHelpers.getSpecStatus = function(child) { + var results = child.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.skipped) { + status = 'skipped'; + } + + return status; +}; + +jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) { + var parentDiv = this.dom.summary; + var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite'; + var parent = child[parentSuite]; + + if (parent) { + if (typeof this.views.suites[parent.id] == 'undefined') { + this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views); + } + parentDiv = this.views.suites[parent.id].element; + } + + parentDiv.appendChild(childElement); +}; + + +jasmine.HtmlReporterHelpers.addHelpers = function(ctor) { + for(var fn in jasmine.HtmlReporterHelpers) { + ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn]; + } +}; + +jasmine.HtmlReporter = function(_doc) { + var self = this; + var doc = _doc || window.document; + + var reporterView; + + var dom = {}; + + // Jasmine Reporter Public Interface + self.logRunningSpecs = false; + + self.reportRunnerStarting = function(runner) { + var specs = runner.specs() || []; + + if (specs.length == 0) { + return; + } + + createReporterDom(runner.env.versionString()); + doc.body.appendChild(dom.reporter); + setExceptionHandling(); + + reporterView = new jasmine.HtmlReporter.ReporterView(dom); + reporterView.addSpecs(specs, self.specFilter); + }; + + self.reportRunnerResults = function(runner) { + reporterView && reporterView.complete(); + }; + + self.reportSuiteResults = function(suite) { + reporterView.suiteComplete(suite); + }; + + self.reportSpecStarting = function(spec) { + if (self.logRunningSpecs) { + self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); + } + }; + + self.reportSpecResults = function(spec) { + reporterView.specComplete(spec); + }; + + self.log = function() { + var console = jasmine.getGlobal().console; + if (console && console.log) { + if (console.log.apply) { + console.log.apply(console, arguments); + } else { + console.log(arguments); // ie fix: console.log.apply doesn't exist on ie + } + } + }; + + self.specFilter = function(spec) { + if (!focusedSpecName()) { + return true; + } + + return spec.getFullName().indexOf(focusedSpecName()) === 0; + }; + + return self; + + function focusedSpecName() { + var specName; + + (function memoizeFocusedSpec() { + if (specName) { + return; + } + + var paramMap = []; + var params = jasmine.HtmlReporter.parameters(doc); + + for (var i = 0; i < params.length; i++) { + var p = params[i].split('='); + paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); + } + + specName = paramMap.spec; + })(); + + return specName; + } + + function createReporterDom(version) { + dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' }, + dom.banner = self.createDom('div', { className: 'banner' }, + self.createDom('span', { className: 'title' }, "Jasmine "), + self.createDom('span', { className: 'version' }, version)), + + dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}), + dom.alert = self.createDom('div', {className: 'alert'}, + self.createDom('span', { className: 'exceptions' }, + self.createDom('label', { className: 'label', 'for': 'no_try_catch' }, 'No try/catch'), + self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))), + dom.results = self.createDom('div', {className: 'results'}, + dom.summary = self.createDom('div', { className: 'summary' }), + dom.details = self.createDom('div', { id: 'details' })) + ); + } + + function noTryCatch() { + return window.location.search.match(/catch=false/); + } + + function searchWithCatch() { + var params = jasmine.HtmlReporter.parameters(window.document); + var removed = false; + var i = 0; + + while (!removed && i < params.length) { + if (params[i].match(/catch=/)) { + params.splice(i, 1); + removed = true; + } + i++; + } + if (jasmine.CATCH_EXCEPTIONS) { + params.push("catch=false"); + } + + return params.join("&"); + } + + function setExceptionHandling() { + var chxCatch = document.getElementById('no_try_catch'); + + if (noTryCatch()) { + chxCatch.setAttribute('checked', true); + jasmine.CATCH_EXCEPTIONS = false; + } + chxCatch.onclick = function() { + window.location.search = searchWithCatch(); + }; + } +}; +jasmine.HtmlReporter.parameters = function(doc) { + var paramStr = doc.location.search.substring(1); + var params = []; + + if (paramStr.length > 0) { + params = paramStr.split('&'); + } + return params; +} +jasmine.HtmlReporter.sectionLink = function(sectionName) { + var link = '?'; + var params = []; + + if (sectionName) { + params.push('spec=' + encodeURIComponent(sectionName)); + } + if (!jasmine.CATCH_EXCEPTIONS) { + params.push("catch=false"); + } + if (params.length > 0) { + link += params.join("&"); + } + + return link; +}; +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter); +jasmine.HtmlReporter.ReporterView = function(dom) { + this.startedAt = new Date(); + this.runningSpecCount = 0; + this.completeSpecCount = 0; + this.passedCount = 0; + this.failedCount = 0; + this.skippedCount = 0; + + this.createResultsMenu = function() { + this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'}, + this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'), + ' | ', + this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing')); + + this.summaryMenuItem.onclick = function() { + dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, ''); + }; + + this.detailsMenuItem.onclick = function() { + showDetails(); + }; + }; + + this.addSpecs = function(specs, specFilter) { + this.totalSpecCount = specs.length; + + this.views = { + specs: {}, + suites: {} + }; + + for (var i = 0; i < specs.length; i++) { + var spec = specs[i]; + this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views); + if (specFilter(spec)) { + this.runningSpecCount++; + } + } + }; + + this.specComplete = function(spec) { + this.completeSpecCount++; + + if (isUndefined(this.views.specs[spec.id])) { + this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom); + } + + var specView = this.views.specs[spec.id]; + + switch (specView.status()) { + case 'passed': + this.passedCount++; + break; + + case 'failed': + this.failedCount++; + break; + + case 'skipped': + this.skippedCount++; + break; + } + + specView.refresh(); + this.refresh(); + }; + + this.suiteComplete = function(suite) { + var suiteView = this.views.suites[suite.id]; + if (isUndefined(suiteView)) { + return; + } + suiteView.refresh(); + }; + + this.refresh = function() { + + if (isUndefined(this.resultsMenu)) { + this.createResultsMenu(); + } + + // currently running UI + if (isUndefined(this.runningAlert)) { + this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" }); + dom.alert.appendChild(this.runningAlert); + } + this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount); + + // skipped specs UI + if (isUndefined(this.skippedAlert)) { + this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" }); + } + + this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; + + if (this.skippedCount === 1 && isDefined(dom.alert)) { + dom.alert.appendChild(this.skippedAlert); + } + + // passing specs UI + if (isUndefined(this.passedAlert)) { + this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" }); + } + this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount); + + // failing specs UI + if (isUndefined(this.failedAlert)) { + this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"}); + } + this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount); + + if (this.failedCount === 1 && isDefined(dom.alert)) { + dom.alert.appendChild(this.failedAlert); + dom.alert.appendChild(this.resultsMenu); + } + + // summary info + this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount); + this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing"; + }; + + this.complete = function() { + dom.alert.removeChild(this.runningAlert); + + this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; + + if (this.failedCount === 0) { + dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount))); + } else { + showDetails(); + } + + dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s")); + }; + + return this; + + function showDetails() { + if (dom.reporter.className.search(/showDetails/) === -1) { + dom.reporter.className += " showDetails"; + } + } + + function isUndefined(obj) { + return typeof obj === 'undefined'; + } + + function isDefined(obj) { + return !isUndefined(obj); + } + + function specPluralizedFor(count) { + var str = count + " spec"; + if (count > 1) { + str += "s" + } + return str; + } + +}; + +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView); + + +jasmine.HtmlReporter.SpecView = function(spec, dom, views) { + this.spec = spec; + this.dom = dom; + this.views = views; + + this.symbol = this.createDom('li', { className: 'pending' }); + this.dom.symbolSummary.appendChild(this.symbol); + + this.summary = this.createDom('div', { className: 'specSummary' }, + this.createDom('a', { + className: 'description', + href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()), + title: this.spec.getFullName() + }, this.spec.description) + ); + + this.detail = this.createDom('div', { className: 'specDetail' }, + this.createDom('a', { + className: 'description', + href: '?spec=' + encodeURIComponent(this.spec.getFullName()), + title: this.spec.getFullName() + }, this.spec.getFullName()) + ); +}; + +jasmine.HtmlReporter.SpecView.prototype.status = function() { + return this.getSpecStatus(this.spec); +}; + +jasmine.HtmlReporter.SpecView.prototype.refresh = function() { + this.symbol.className = this.status(); + + switch (this.status()) { + case 'skipped': + break; + + case 'passed': + this.appendSummaryToSuiteDiv(); + break; + + case 'failed': + this.appendSummaryToSuiteDiv(); + this.appendFailureDetail(); + break; + } +}; + +jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() { + this.summary.className += ' ' + this.status(); + this.appendToSummary(this.spec, this.summary); +}; + +jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() { + this.detail.className += ' ' + this.status(); + + var resultItems = this.spec.results().getItems(); + var messagesDiv = this.createDom('div', { className: 'messages' }); + + for (var i = 0; i < resultItems.length; i++) { + var result = resultItems[i]; + + if (result.type == 'log') { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); + } else if (result.type == 'expect' && result.passed && !result.passed()) { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); + + if (result.trace.stack) { + messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); + } + } + } + + if (messagesDiv.childNodes.length > 0) { + this.detail.appendChild(messagesDiv); + this.dom.details.appendChild(this.detail); + } +}; + +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) { + this.suite = suite; + this.dom = dom; + this.views = views; + + this.element = this.createDom('div', { className: 'suite' }, + this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description) + ); + + this.appendToSummary(this.suite, this.element); +}; + +jasmine.HtmlReporter.SuiteView.prototype.status = function() { + return this.getSpecStatus(this.suite); +}; + +jasmine.HtmlReporter.SuiteView.prototype.refresh = function() { + this.element.className += " " + this.status(); +}; + +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView); + +/* @deprecated Use jasmine.HtmlReporter instead + */ +jasmine.TrivialReporter = function(doc) { + this.document = doc || document; + this.suiteDivs = {}; + this.logRunningSpecs = false; +}; + +jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) { + var el = document.createElement(type); + + for (var i = 2; i < arguments.length; i++) { + var child = arguments[i]; + + if (typeof child === 'string') { + el.appendChild(document.createTextNode(child)); + } else { + if (child) { el.appendChild(child); } + } + } + + for (var attr in attrs) { + if (attr == "className") { + el[attr] = attrs[attr]; + } else { + el.setAttribute(attr, attrs[attr]); + } + } + + return el; +}; + +jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { + var showPassed, showSkipped; + + this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' }, + this.createDom('div', { className: 'banner' }, + this.createDom('div', { className: 'logo' }, + this.createDom('span', { className: 'title' }, "Jasmine"), + this.createDom('span', { className: 'version' }, runner.env.versionString())), + this.createDom('div', { className: 'options' }, + "Show ", + showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }), + this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "), + showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }), + this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped") + ) + ), + + this.runnerDiv = this.createDom('div', { className: 'runner running' }, + this.createDom('a', { className: 'run_spec', href: '?' }, "run all"), + this.runnerMessageSpan = this.createDom('span', {}, "Running..."), + this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, "")) + ); + + this.document.body.appendChild(this.outerDiv); + + var suites = runner.suites(); + for (var i = 0; i < suites.length; i++) { + var suite = suites[i]; + var suiteDiv = this.createDom('div', { className: 'suite' }, + this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"), + this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description)); + this.suiteDivs[suite.id] = suiteDiv; + var parentDiv = this.outerDiv; + if (suite.parentSuite) { + parentDiv = this.suiteDivs[suite.parentSuite.id]; + } + parentDiv.appendChild(suiteDiv); + } + + this.startedAt = new Date(); + + var self = this; + showPassed.onclick = function(evt) { + if (showPassed.checked) { + self.outerDiv.className += ' show-passed'; + } else { + self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, ''); + } + }; + + showSkipped.onclick = function(evt) { + if (showSkipped.checked) { + self.outerDiv.className += ' show-skipped'; + } else { + self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, ''); + } + }; +}; + +jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) { + var results = runner.results(); + var className = (results.failedCount > 0) ? "runner failed" : "runner passed"; + this.runnerDiv.setAttribute("class", className); + //do it twice for IE + this.runnerDiv.setAttribute("className", className); + var specs = runner.specs(); + var specCount = 0; + for (var i = 0; i < specs.length; i++) { + if (this.specFilter(specs[i])) { + specCount++; + } + } + var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s"); + message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"; + this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild); + + this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString())); +}; + +jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) { + var results = suite.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.totalCount === 0) { // todo: change this to check results.skipped + status = 'skipped'; + } + this.suiteDivs[suite.id].className += " " + status; +}; + +jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) { + if (this.logRunningSpecs) { + this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); + } +}; + +jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) { + var results = spec.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.skipped) { + status = 'skipped'; + } + var specDiv = this.createDom('div', { className: 'spec ' + status }, + this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"), + this.createDom('a', { + className: 'description', + href: '?spec=' + encodeURIComponent(spec.getFullName()), + title: spec.getFullName() + }, spec.description)); + + + var resultItems = results.getItems(); + var messagesDiv = this.createDom('div', { className: 'messages' }); + for (var i = 0; i < resultItems.length; i++) { + var result = resultItems[i]; + + if (result.type == 'log') { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); + } else if (result.type == 'expect' && result.passed && !result.passed()) { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); + + if (result.trace.stack) { + messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); + } + } + } + + if (messagesDiv.childNodes.length > 0) { + specDiv.appendChild(messagesDiv); + } + + this.suiteDivs[spec.suite.id].appendChild(specDiv); +}; + +jasmine.TrivialReporter.prototype.log = function() { + var console = jasmine.getGlobal().console; + if (console && console.log) { + if (console.log.apply) { + console.log.apply(console, arguments); + } else { + console.log(arguments); // ie fix: console.log.apply doesn't exist on ie + } + } +}; + +jasmine.TrivialReporter.prototype.getLocation = function() { + return this.document.location; +}; + +jasmine.TrivialReporter.prototype.specFilter = function(spec) { + var paramMap = {}; + var params = this.getLocation().search.substring(1).split('&'); + for (var i = 0; i < params.length; i++) { + var p = params[i].split('='); + paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); + } + + if (!paramMap.spec) { + return true; + } + return spec.getFullName().indexOf(paramMap.spec) === 0; +}; diff --git a/examples/vanillajs/bower_components/jasmine/lib/jasmine-core/jasmine.css b/examples/vanillajs/bower_components/jasmine/lib/jasmine-core/jasmine.css new file mode 100644 index 0000000000..8c008dc722 --- /dev/null +++ b/examples/vanillajs/bower_components/jasmine/lib/jasmine-core/jasmine.css @@ -0,0 +1,82 @@ +body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } + +#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } +#HTMLReporter a { text-decoration: none; } +#HTMLReporter a:hover { text-decoration: underline; } +#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } +#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } +#HTMLReporter #jasmine_content { position: fixed; right: 100%; } +#HTMLReporter .version { color: #aaaaaa; } +#HTMLReporter .banner { margin-top: 14px; } +#HTMLReporter .duration { color: #aaaaaa; float: right; } +#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } +#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } +#HTMLReporter .symbolSummary li.passed { font-size: 14px; } +#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } +#HTMLReporter .symbolSummary li.failed { line-height: 9px; } +#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } +#HTMLReporter .symbolSummary li.skipped { font-size: 14px; } +#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } +#HTMLReporter .symbolSummary li.pending { line-height: 11px; } +#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } +#HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } +#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } +#HTMLReporter .runningAlert { background-color: #666666; } +#HTMLReporter .skippedAlert { background-color: #aaaaaa; } +#HTMLReporter .skippedAlert:first-child { background-color: #333333; } +#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } +#HTMLReporter .passingAlert { background-color: #a6b779; } +#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } +#HTMLReporter .failingAlert { background-color: #cf867e; } +#HTMLReporter .failingAlert:first-child { background-color: #b03911; } +#HTMLReporter .results { margin-top: 14px; } +#HTMLReporter #details { display: none; } +#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } +#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } +#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } +#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } +#HTMLReporter.showDetails .summary { display: none; } +#HTMLReporter.showDetails #details { display: block; } +#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } +#HTMLReporter .summary { margin-top: 14px; } +#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } +#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } +#HTMLReporter .summary .specSummary.failed a { color: #b03911; } +#HTMLReporter .description + .suite { margin-top: 0; } +#HTMLReporter .suite { margin-top: 14px; } +#HTMLReporter .suite a { color: #333333; } +#HTMLReporter #details .specDetail { margin-bottom: 28px; } +#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } +#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } +#HTMLReporter .resultMessage span.result { display: block; } +#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } + +#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } +#TrivialReporter a:visited, #TrivialReporter a { color: #303; } +#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } +#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } +#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } +#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } +#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } +#TrivialReporter .runner.running { background-color: yellow; } +#TrivialReporter .options { text-align: right; font-size: .8em; } +#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } +#TrivialReporter .suite .suite { margin: 5px; } +#TrivialReporter .suite.passed { background-color: #dfd; } +#TrivialReporter .suite.failed { background-color: #fdd; } +#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } +#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } +#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } +#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } +#TrivialReporter .spec.skipped { background-color: #bbb; } +#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } +#TrivialReporter .passed { background-color: #cfc; display: none; } +#TrivialReporter .failed { background-color: #fbb; } +#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } +#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } +#TrivialReporter .resultMessage .mismatch { color: black; } +#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } +#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } +#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } +#TrivialReporter #jasmine_content { position: fixed; right: 100%; } +#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } diff --git a/examples/vanillajs/bower_components/jasmine/lib/jasmine-core/jasmine.js b/examples/vanillajs/bower_components/jasmine/lib/jasmine-core/jasmine.js new file mode 100644 index 0000000000..6b5d7b86ac --- /dev/null +++ b/examples/vanillajs/bower_components/jasmine/lib/jasmine-core/jasmine.js @@ -0,0 +1,2600 @@ +var isCommonJS = typeof window == "undefined" && typeof exports == "object"; + +/** + * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. + * + * @namespace + */ +var jasmine = {}; +if (isCommonJS) exports.jasmine = jasmine; +/** + * @private + */ +jasmine.unimplementedMethod_ = function() { + throw new Error("unimplemented method"); +}; + +/** + * Use jasmine.undefined instead of undefined, since undefined is just + * a plain old variable and may be redefined by somebody else. + * + * @private + */ +jasmine.undefined = jasmine.___undefined___; + +/** + * Show diagnostic messages in the console if set to true + * + */ +jasmine.VERBOSE = false; + +/** + * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed. + * + */ +jasmine.DEFAULT_UPDATE_INTERVAL = 250; + +/** + * Maximum levels of nesting that will be included when an object is pretty-printed + */ +jasmine.MAX_PRETTY_PRINT_DEPTH = 40; + +/** + * Default timeout interval in milliseconds for waitsFor() blocks. + */ +jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; + +/** + * By default exceptions thrown in the context of a test are caught by jasmine so that it can run the remaining tests in the suite. + * Set to false to let the exception bubble up in the browser. + * + */ +jasmine.CATCH_EXCEPTIONS = true; + +jasmine.getGlobal = function() { + function getGlobal() { + return this; + } + + return getGlobal(); +}; + +/** + * Allows for bound functions to be compared. Internal use only. + * + * @ignore + * @private + * @param base {Object} bound 'this' for the function + * @param name {Function} function to find + */ +jasmine.bindOriginal_ = function(base, name) { + var original = base[name]; + if (original.apply) { + return function() { + return original.apply(base, arguments); + }; + } else { + // IE support + return jasmine.getGlobal()[name]; + } +}; + +jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); +jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); +jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); +jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); + +jasmine.MessageResult = function(values) { + this.type = 'log'; + this.values = values; + this.trace = new Error(); // todo: test better +}; + +jasmine.MessageResult.prototype.toString = function() { + var text = ""; + for (var i = 0; i < this.values.length; i++) { + if (i > 0) text += " "; + if (jasmine.isString_(this.values[i])) { + text += this.values[i]; + } else { + text += jasmine.pp(this.values[i]); + } + } + return text; +}; + +jasmine.ExpectationResult = function(params) { + this.type = 'expect'; + this.matcherName = params.matcherName; + this.passed_ = params.passed; + this.expected = params.expected; + this.actual = params.actual; + this.message = this.passed_ ? 'Passed.' : params.message; + + var trace = (params.trace || new Error(this.message)); + this.trace = this.passed_ ? '' : trace; +}; + +jasmine.ExpectationResult.prototype.toString = function () { + return this.message; +}; + +jasmine.ExpectationResult.prototype.passed = function () { + return this.passed_; +}; + +/** + * Getter for the Jasmine environment. Ensures one gets created + */ +jasmine.getEnv = function() { + var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); + return env; +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isArray_ = function(value) { + return jasmine.isA_("Array", value); +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isString_ = function(value) { + return jasmine.isA_("String", value); +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isNumber_ = function(value) { + return jasmine.isA_("Number", value); +}; + +/** + * @ignore + * @private + * @param {String} typeName + * @param value + * @returns {Boolean} + */ +jasmine.isA_ = function(typeName, value) { + return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; +}; + +/** + * Pretty printer for expecations. Takes any object and turns it into a human-readable string. + * + * @param value {Object} an object to be outputted + * @returns {String} + */ +jasmine.pp = function(value) { + var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); + stringPrettyPrinter.format(value); + return stringPrettyPrinter.string; +}; + +/** + * Returns true if the object is a DOM Node. + * + * @param {Object} obj object to check + * @returns {Boolean} + */ +jasmine.isDomNode = function(obj) { + return obj.nodeType > 0; +}; + +/** + * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. + * + * @example + * // don't care about which function is passed in, as long as it's a function + * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function)); + * + * @param {Class} clazz + * @returns matchable object of the type clazz + */ +jasmine.any = function(clazz) { + return new jasmine.Matchers.Any(clazz); +}; + +/** + * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the + * attributes on the object. + * + * @example + * // don't care about any other attributes than foo. + * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"}); + * + * @param sample {Object} sample + * @returns matchable object for the sample + */ +jasmine.objectContaining = function (sample) { + return new jasmine.Matchers.ObjectContaining(sample); +}; + +/** + * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. + * + * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine + * expectation syntax. Spies can be checked if they were called or not and what the calling params were. + * + * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs). + * + * Spies are torn down at the end of every spec. + * + * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. + * + * @example + * // a stub + * var myStub = jasmine.createSpy('myStub'); // can be used anywhere + * + * // spy example + * var foo = { + * not: function(bool) { return !bool; } + * } + * + * // actual foo.not will not be called, execution stops + * spyOn(foo, 'not'); + + // foo.not spied upon, execution will continue to implementation + * spyOn(foo, 'not').andCallThrough(); + * + * // fake example + * var foo = { + * not: function(bool) { return !bool; } + * } + * + * // foo.not(val) will return val + * spyOn(foo, 'not').andCallFake(function(value) {return value;}); + * + * // mock example + * foo.not(7 == 7); + * expect(foo.not).toHaveBeenCalled(); + * expect(foo.not).toHaveBeenCalledWith(true); + * + * @constructor + * @see spyOn, jasmine.createSpy, jasmine.createSpyObj + * @param {String} name + */ +jasmine.Spy = function(name) { + /** + * The name of the spy, if provided. + */ + this.identity = name || 'unknown'; + /** + * Is this Object a spy? + */ + this.isSpy = true; + /** + * The actual function this spy stubs. + */ + this.plan = function() { + }; + /** + * Tracking of the most recent call to the spy. + * @example + * var mySpy = jasmine.createSpy('foo'); + * mySpy(1, 2); + * mySpy.mostRecentCall.args = [1, 2]; + */ + this.mostRecentCall = {}; + + /** + * Holds arguments for each call to the spy, indexed by call count + * @example + * var mySpy = jasmine.createSpy('foo'); + * mySpy(1, 2); + * mySpy(7, 8); + * mySpy.mostRecentCall.args = [7, 8]; + * mySpy.argsForCall[0] = [1, 2]; + * mySpy.argsForCall[1] = [7, 8]; + */ + this.argsForCall = []; + this.calls = []; +}; + +/** + * Tells a spy to call through to the actual implemenatation. + * + * @example + * var foo = { + * bar: function() { // do some stuff } + * } + * + * // defining a spy on an existing property: foo.bar + * spyOn(foo, 'bar').andCallThrough(); + */ +jasmine.Spy.prototype.andCallThrough = function() { + this.plan = this.originalValue; + return this; +}; + +/** + * For setting the return value of a spy. + * + * @example + * // defining a spy from scratch: foo() returns 'baz' + * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); + * + * // defining a spy on an existing property: foo.bar() returns 'baz' + * spyOn(foo, 'bar').andReturn('baz'); + * + * @param {Object} value + */ +jasmine.Spy.prototype.andReturn = function(value) { + this.plan = function() { + return value; + }; + return this; +}; + +/** + * For throwing an exception when a spy is called. + * + * @example + * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' + * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); + * + * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' + * spyOn(foo, 'bar').andThrow('baz'); + * + * @param {String} exceptionMsg + */ +jasmine.Spy.prototype.andThrow = function(exceptionMsg) { + this.plan = function() { + throw exceptionMsg; + }; + return this; +}; + +/** + * Calls an alternate implementation when a spy is called. + * + * @example + * var baz = function() { + * // do some stuff, return something + * } + * // defining a spy from scratch: foo() calls the function baz + * var foo = jasmine.createSpy('spy on foo').andCall(baz); + * + * // defining a spy on an existing property: foo.bar() calls an anonymnous function + * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); + * + * @param {Function} fakeFunc + */ +jasmine.Spy.prototype.andCallFake = function(fakeFunc) { + this.plan = fakeFunc; + return this; +}; + +/** + * Resets all of a spy's the tracking variables so that it can be used again. + * + * @example + * spyOn(foo, 'bar'); + * + * foo.bar(); + * + * expect(foo.bar.callCount).toEqual(1); + * + * foo.bar.reset(); + * + * expect(foo.bar.callCount).toEqual(0); + */ +jasmine.Spy.prototype.reset = function() { + this.wasCalled = false; + this.callCount = 0; + this.argsForCall = []; + this.calls = []; + this.mostRecentCall = {}; +}; + +jasmine.createSpy = function(name) { + + var spyObj = function() { + spyObj.wasCalled = true; + spyObj.callCount++; + var args = jasmine.util.argsToArray(arguments); + spyObj.mostRecentCall.object = this; + spyObj.mostRecentCall.args = args; + spyObj.argsForCall.push(args); + spyObj.calls.push({object: this, args: args}); + return spyObj.plan.apply(this, arguments); + }; + + var spy = new jasmine.Spy(name); + + for (var prop in spy) { + spyObj[prop] = spy[prop]; + } + + spyObj.reset(); + + return spyObj; +}; + +/** + * Determines whether an object is a spy. + * + * @param {jasmine.Spy|Object} putativeSpy + * @returns {Boolean} + */ +jasmine.isSpy = function(putativeSpy) { + return putativeSpy && putativeSpy.isSpy; +}; + +/** + * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something + * large in one call. + * + * @param {String} baseName name of spy class + * @param {Array} methodNames array of names of methods to make spies + */ +jasmine.createSpyObj = function(baseName, methodNames) { + if (!jasmine.isArray_(methodNames) || methodNames.length === 0) { + throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); + } + var obj = {}; + for (var i = 0; i < methodNames.length; i++) { + obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); + } + return obj; +}; + +/** + * All parameters are pretty-printed and concatenated together, then written to the current spec's output. + * + * Be careful not to leave calls to jasmine.log in production code. + */ +jasmine.log = function() { + var spec = jasmine.getEnv().currentSpec; + spec.log.apply(spec, arguments); +}; + +/** + * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. + * + * @example + * // spy example + * var foo = { + * not: function(bool) { return !bool; } + * } + * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops + * + * @see jasmine.createSpy + * @param obj + * @param methodName + * @return {jasmine.Spy} a Jasmine spy that can be chained with all spy methods + */ +var spyOn = function(obj, methodName) { + return jasmine.getEnv().currentSpec.spyOn(obj, methodName); +}; +if (isCommonJS) exports.spyOn = spyOn; + +/** + * Creates a Jasmine spec that will be added to the current suite. + * + * // TODO: pending tests + * + * @example + * it('should be true', function() { + * expect(true).toEqual(true); + * }); + * + * @param {String} desc description of this specification + * @param {Function} func defines the preconditions and expectations of the spec + */ +var it = function(desc, func) { + return jasmine.getEnv().it(desc, func); +}; +if (isCommonJS) exports.it = it; + +/** + * Creates a disabled Jasmine spec. + * + * A convenience method that allows existing specs to be disabled temporarily during development. + * + * @param {String} desc description of this specification + * @param {Function} func defines the preconditions and expectations of the spec + */ +var xit = function(desc, func) { + return jasmine.getEnv().xit(desc, func); +}; +if (isCommonJS) exports.xit = xit; + +/** + * Starts a chain for a Jasmine expectation. + * + * It is passed an Object that is the actual value and should chain to one of the many + * jasmine.Matchers functions. + * + * @param {Object} actual Actual value to test against and expected value + * @return {jasmine.Matchers} + */ +var expect = function(actual) { + return jasmine.getEnv().currentSpec.expect(actual); +}; +if (isCommonJS) exports.expect = expect; + +/** + * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. + * + * @param {Function} func Function that defines part of a jasmine spec. + */ +var runs = function(func) { + jasmine.getEnv().currentSpec.runs(func); +}; +if (isCommonJS) exports.runs = runs; + +/** + * Waits a fixed time period before moving to the next block. + * + * @deprecated Use waitsFor() instead + * @param {Number} timeout milliseconds to wait + */ +var waits = function(timeout) { + jasmine.getEnv().currentSpec.waits(timeout); +}; +if (isCommonJS) exports.waits = waits; + +/** + * Waits for the latchFunction to return true before proceeding to the next block. + * + * @param {Function} latchFunction + * @param {String} optional_timeoutMessage + * @param {Number} optional_timeout + */ +var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { + jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); +}; +if (isCommonJS) exports.waitsFor = waitsFor; + +/** + * A function that is called before each spec in a suite. + * + * Used for spec setup, including validating assumptions. + * + * @param {Function} beforeEachFunction + */ +var beforeEach = function(beforeEachFunction) { + jasmine.getEnv().beforeEach(beforeEachFunction); +}; +if (isCommonJS) exports.beforeEach = beforeEach; + +/** + * A function that is called after each spec in a suite. + * + * Used for restoring any state that is hijacked during spec execution. + * + * @param {Function} afterEachFunction + */ +var afterEach = function(afterEachFunction) { + jasmine.getEnv().afterEach(afterEachFunction); +}; +if (isCommonJS) exports.afterEach = afterEach; + +/** + * Defines a suite of specifications. + * + * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared + * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization + * of setup in some tests. + * + * @example + * // TODO: a simple suite + * + * // TODO: a simple suite with a nested describe block + * + * @param {String} description A string, usually the class under test. + * @param {Function} specDefinitions function that defines several specs. + */ +var describe = function(description, specDefinitions) { + return jasmine.getEnv().describe(description, specDefinitions); +}; +if (isCommonJS) exports.describe = describe; + +/** + * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. + * + * @param {String} description A string, usually the class under test. + * @param {Function} specDefinitions function that defines several specs. + */ +var xdescribe = function(description, specDefinitions) { + return jasmine.getEnv().xdescribe(description, specDefinitions); +}; +if (isCommonJS) exports.xdescribe = xdescribe; + + +// Provide the XMLHttpRequest class for IE 5.x-6.x: +jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { + function tryIt(f) { + try { + return f(); + } catch(e) { + } + return null; + } + + var xhr = tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP.6.0"); + }) || + tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP.3.0"); + }) || + tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP"); + }) || + tryIt(function() { + return new ActiveXObject("Microsoft.XMLHTTP"); + }); + + if (!xhr) throw new Error("This browser does not support XMLHttpRequest."); + + return xhr; +} : XMLHttpRequest; +/** + * @namespace + */ +jasmine.util = {}; + +/** + * Declare that a child class inherit it's prototype from the parent class. + * + * @private + * @param {Function} childClass + * @param {Function} parentClass + */ +jasmine.util.inherit = function(childClass, parentClass) { + /** + * @private + */ + var subclass = function() { + }; + subclass.prototype = parentClass.prototype; + childClass.prototype = new subclass(); +}; + +jasmine.util.formatException = function(e) { + var lineNumber; + if (e.line) { + lineNumber = e.line; + } + else if (e.lineNumber) { + lineNumber = e.lineNumber; + } + + var file; + + if (e.sourceURL) { + file = e.sourceURL; + } + else if (e.fileName) { + file = e.fileName; + } + + var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); + + if (file && lineNumber) { + message += ' in ' + file + ' (line ' + lineNumber + ')'; + } + + return message; +}; + +jasmine.util.htmlEscape = function(str) { + if (!str) return str; + return str.replace(/&/g, '&') + .replace(//g, '>'); +}; + +jasmine.util.argsToArray = function(args) { + var arrayOfArgs = []; + for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); + return arrayOfArgs; +}; + +jasmine.util.extend = function(destination, source) { + for (var property in source) destination[property] = source[property]; + return destination; +}; + +/** + * Environment for Jasmine + * + * @constructor + */ +jasmine.Env = function() { + this.currentSpec = null; + this.currentSuite = null; + this.currentRunner_ = new jasmine.Runner(this); + + this.reporter = new jasmine.MultiReporter(); + + this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; + this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; + this.lastUpdate = 0; + this.specFilter = function() { + return true; + }; + + this.nextSpecId_ = 0; + this.nextSuiteId_ = 0; + this.equalityTesters_ = []; + + // wrap matchers + this.matchersClass = function() { + jasmine.Matchers.apply(this, arguments); + }; + jasmine.util.inherit(this.matchersClass, jasmine.Matchers); + + jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); +}; + + +jasmine.Env.prototype.setTimeout = jasmine.setTimeout; +jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; +jasmine.Env.prototype.setInterval = jasmine.setInterval; +jasmine.Env.prototype.clearInterval = jasmine.clearInterval; + +/** + * @returns an object containing jasmine version build info, if set. + */ +jasmine.Env.prototype.version = function () { + if (jasmine.version_) { + return jasmine.version_; + } else { + throw new Error('Version not set'); + } +}; + +/** + * @returns string containing jasmine version build info, if set. + */ +jasmine.Env.prototype.versionString = function() { + if (!jasmine.version_) { + return "version unknown"; + } + + var version = this.version(); + var versionString = version.major + "." + version.minor + "." + version.build; + if (version.release_candidate) { + versionString += ".rc" + version.release_candidate; + } + versionString += " revision " + version.revision; + return versionString; +}; + +/** + * @returns a sequential integer starting at 0 + */ +jasmine.Env.prototype.nextSpecId = function () { + return this.nextSpecId_++; +}; + +/** + * @returns a sequential integer starting at 0 + */ +jasmine.Env.prototype.nextSuiteId = function () { + return this.nextSuiteId_++; +}; + +/** + * Register a reporter to receive status updates from Jasmine. + * @param {jasmine.Reporter} reporter An object which will receive status updates. + */ +jasmine.Env.prototype.addReporter = function(reporter) { + this.reporter.addReporter(reporter); +}; + +jasmine.Env.prototype.execute = function() { + this.currentRunner_.execute(); +}; + +jasmine.Env.prototype.describe = function(description, specDefinitions) { + var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); + + var parentSuite = this.currentSuite; + if (parentSuite) { + parentSuite.add(suite); + } else { + this.currentRunner_.add(suite); + } + + this.currentSuite = suite; + + var declarationError = null; + try { + specDefinitions.call(suite); + } catch(e) { + declarationError = e; + } + + if (declarationError) { + this.it("encountered a declaration exception", function() { + throw declarationError; + }); + } + + this.currentSuite = parentSuite; + + return suite; +}; + +jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { + if (this.currentSuite) { + this.currentSuite.beforeEach(beforeEachFunction); + } else { + this.currentRunner_.beforeEach(beforeEachFunction); + } +}; + +jasmine.Env.prototype.currentRunner = function () { + return this.currentRunner_; +}; + +jasmine.Env.prototype.afterEach = function(afterEachFunction) { + if (this.currentSuite) { + this.currentSuite.afterEach(afterEachFunction); + } else { + this.currentRunner_.afterEach(afterEachFunction); + } + +}; + +jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { + return { + execute: function() { + } + }; +}; + +jasmine.Env.prototype.it = function(description, func) { + var spec = new jasmine.Spec(this, this.currentSuite, description); + this.currentSuite.add(spec); + this.currentSpec = spec; + + if (func) { + spec.runs(func); + } + + return spec; +}; + +jasmine.Env.prototype.xit = function(desc, func) { + return { + id: this.nextSpecId(), + runs: function() { + } + }; +}; + +jasmine.Env.prototype.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) { + if (a.source != b.source) + mismatchValues.push("expected pattern /" + b.source + "/ is not equal to the pattern /" + a.source + "/"); + + if (a.ignoreCase != b.ignoreCase) + mismatchValues.push("expected modifier i was" + (b.ignoreCase ? " " : " not ") + "set and does not equal the origin modifier"); + + if (a.global != b.global) + mismatchValues.push("expected modifier g was" + (b.global ? " " : " not ") + "set and does not equal the origin modifier"); + + if (a.multiline != b.multiline) + mismatchValues.push("expected modifier m was" + (b.multiline ? " " : " not ") + "set and does not equal the origin modifier"); + + if (a.sticky != b.sticky) + mismatchValues.push("expected modifier y was" + (b.sticky ? " " : " not ") + "set and does not equal the origin modifier"); + + return (mismatchValues.length === 0); +}; + +jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { + if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { + return true; + } + + a.__Jasmine_been_here_before__ = b; + b.__Jasmine_been_here_before__ = a; + + var hasKey = function(obj, keyName) { + return obj !== null && obj[keyName] !== jasmine.undefined; + }; + + for (var property in b) { + if (!hasKey(a, property) && hasKey(b, property)) { + mismatchKeys.push("expected has key '" + property + "', but missing from actual."); + } + } + for (property in a) { + if (!hasKey(b, property) && hasKey(a, property)) { + mismatchKeys.push("expected missing key '" + property + "', but present in actual."); + } + } + for (property in b) { + if (property == '__Jasmine_been_here_before__') continue; + if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { + mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); + } + } + + if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { + mismatchValues.push("arrays were not the same length"); + } + + delete a.__Jasmine_been_here_before__; + delete b.__Jasmine_been_here_before__; + return (mismatchKeys.length === 0 && mismatchValues.length === 0); +}; + +jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { + mismatchKeys = mismatchKeys || []; + mismatchValues = mismatchValues || []; + + for (var i = 0; i < this.equalityTesters_.length; i++) { + var equalityTester = this.equalityTesters_[i]; + var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); + if (result !== jasmine.undefined) return result; + } + + if (a === b) return true; + + if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { + return (a == jasmine.undefined && b == jasmine.undefined); + } + + if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { + return a === b; + } + + if (a instanceof Date && b instanceof Date) { + return a.getTime() == b.getTime(); + } + + if (a.jasmineMatches) { + return a.jasmineMatches(b); + } + + if (b.jasmineMatches) { + return b.jasmineMatches(a); + } + + if (a instanceof jasmine.Matchers.ObjectContaining) { + return a.matches(b); + } + + if (b instanceof jasmine.Matchers.ObjectContaining) { + return b.matches(a); + } + + if (jasmine.isString_(a) && jasmine.isString_(b)) { + return (a == b); + } + + if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { + return (a == b); + } + + if (a instanceof RegExp && b instanceof RegExp) { + return this.compareRegExps_(a, b, mismatchKeys, mismatchValues); + } + + if (typeof a === "object" && typeof b === "object") { + return this.compareObjects_(a, b, mismatchKeys, mismatchValues); + } + + //Straight check + return (a === b); +}; + +jasmine.Env.prototype.contains_ = function(haystack, needle) { + if (jasmine.isArray_(haystack)) { + for (var i = 0; i < haystack.length; i++) { + if (this.equals_(haystack[i], needle)) return true; + } + return false; + } + return haystack.indexOf(needle) >= 0; +}; + +jasmine.Env.prototype.addEqualityTester = function(equalityTester) { + this.equalityTesters_.push(equalityTester); +}; +/** No-op base class for Jasmine reporters. + * + * @constructor + */ +jasmine.Reporter = function() { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportRunnerResults = function(runner) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSuiteResults = function(suite) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSpecStarting = function(spec) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSpecResults = function(spec) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.log = function(str) { +}; + +/** + * Blocks are functions with executable code that make up a spec. + * + * @constructor + * @param {jasmine.Env} env + * @param {Function} func + * @param {jasmine.Spec} spec + */ +jasmine.Block = function(env, func, spec) { + this.env = env; + this.func = func; + this.spec = spec; +}; + +jasmine.Block.prototype.execute = function(onComplete) { + if (!jasmine.CATCH_EXCEPTIONS) { + this.func.apply(this.spec); + } + else { + try { + this.func.apply(this.spec); + } catch (e) { + this.spec.fail(e); + } + } + onComplete(); +}; +/** JavaScript API reporter. + * + * @constructor + */ +jasmine.JsApiReporter = function() { + this.started = false; + this.finished = false; + this.suites_ = []; + this.results_ = {}; +}; + +jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { + this.started = true; + var suites = runner.topLevelSuites(); + for (var i = 0; i < suites.length; i++) { + var suite = suites[i]; + this.suites_.push(this.summarize_(suite)); + } +}; + +jasmine.JsApiReporter.prototype.suites = function() { + return this.suites_; +}; + +jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { + var isSuite = suiteOrSpec instanceof jasmine.Suite; + var summary = { + id: suiteOrSpec.id, + name: suiteOrSpec.description, + type: isSuite ? 'suite' : 'spec', + children: [] + }; + + if (isSuite) { + var children = suiteOrSpec.children(); + for (var i = 0; i < children.length; i++) { + summary.children.push(this.summarize_(children[i])); + } + } + return summary; +}; + +jasmine.JsApiReporter.prototype.results = function() { + return this.results_; +}; + +jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { + return this.results_[specId]; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { + this.finished = true; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { + this.results_[spec.id] = { + messages: spec.results().getItems(), + result: spec.results().failedCount > 0 ? "failed" : "passed" + }; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.log = function(str) { +}; + +jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ + var results = {}; + for (var i = 0; i < specIds.length; i++) { + var specId = specIds[i]; + results[specId] = this.summarizeResult_(this.results_[specId]); + } + return results; +}; + +jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ + var summaryMessages = []; + var messagesLength = result.messages.length; + for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { + var resultMessage = result.messages[messageIndex]; + summaryMessages.push({ + text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, + passed: resultMessage.passed ? resultMessage.passed() : true, + type: resultMessage.type, + message: resultMessage.message, + trace: { + stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined + } + }); + } + + return { + result : result.result, + messages : summaryMessages + }; +}; + +/** + * @constructor + * @param {jasmine.Env} env + * @param actual + * @param {jasmine.Spec} spec + */ +jasmine.Matchers = function(env, actual, spec, opt_isNot) { + this.env = env; + this.actual = actual; + this.spec = spec; + this.isNot = opt_isNot || false; + this.reportWasCalled_ = false; +}; + +// todo: @deprecated as of Jasmine 0.11, remove soon [xw] +jasmine.Matchers.pp = function(str) { + throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); +}; + +// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] +jasmine.Matchers.prototype.report = function(result, failing_message, details) { + throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); +}; + +jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { + for (var methodName in prototype) { + if (methodName == 'report') continue; + var orig = prototype[methodName]; + matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); + } +}; + +jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { + return function() { + var matcherArgs = jasmine.util.argsToArray(arguments); + var result = matcherFunction.apply(this, arguments); + + if (this.isNot) { + result = !result; + } + + if (this.reportWasCalled_) return result; + + var message; + if (!result) { + if (this.message) { + message = this.message.apply(this, arguments); + if (jasmine.isArray_(message)) { + message = message[this.isNot ? 1 : 0]; + } + } else { + var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); + message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; + if (matcherArgs.length > 0) { + for (var i = 0; i < matcherArgs.length; i++) { + if (i > 0) message += ","; + message += " " + jasmine.pp(matcherArgs[i]); + } + } + message += "."; + } + } + var expectationResult = new jasmine.ExpectationResult({ + matcherName: matcherName, + passed: result, + expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], + actual: this.actual, + message: message + }); + this.spec.addMatcherResult(expectationResult); + return jasmine.undefined; + }; +}; + + + + +/** + * toBe: compares the actual to the expected using === + * @param expected + */ +jasmine.Matchers.prototype.toBe = function(expected) { + return this.actual === expected; +}; + +/** + * toNotBe: compares the actual to the expected using !== + * @param expected + * @deprecated as of 1.0. Use not.toBe() instead. + */ +jasmine.Matchers.prototype.toNotBe = function(expected) { + return this.actual !== expected; +}; + +/** + * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. + * + * @param expected + */ +jasmine.Matchers.prototype.toEqual = function(expected) { + return this.env.equals_(this.actual, expected); +}; + +/** + * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual + * @param expected + * @deprecated as of 1.0. Use not.toEqual() instead. + */ +jasmine.Matchers.prototype.toNotEqual = function(expected) { + return !this.env.equals_(this.actual, expected); +}; + +/** + * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes + * a pattern or a String. + * + * @param expected + */ +jasmine.Matchers.prototype.toMatch = function(expected) { + return new RegExp(expected).test(this.actual); +}; + +/** + * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch + * @param expected + * @deprecated as of 1.0. Use not.toMatch() instead. + */ +jasmine.Matchers.prototype.toNotMatch = function(expected) { + return !(new RegExp(expected).test(this.actual)); +}; + +/** + * Matcher that compares the actual to jasmine.undefined. + */ +jasmine.Matchers.prototype.toBeDefined = function() { + return (this.actual !== jasmine.undefined); +}; + +/** + * Matcher that compares the actual to jasmine.undefined. + */ +jasmine.Matchers.prototype.toBeUndefined = function() { + return (this.actual === jasmine.undefined); +}; + +/** + * Matcher that compares the actual to null. + */ +jasmine.Matchers.prototype.toBeNull = function() { + return (this.actual === null); +}; + +/** + * Matcher that compares the actual to NaN. + */ +jasmine.Matchers.prototype.toBeNaN = function() { + this.message = function() { + return [ "Expected " + jasmine.pp(this.actual) + " to be NaN." ]; + }; + + return (this.actual !== this.actual); +}; + +/** + * Matcher that boolean not-nots the actual. + */ +jasmine.Matchers.prototype.toBeTruthy = function() { + return !!this.actual; +}; + + +/** + * Matcher that boolean nots the actual. + */ +jasmine.Matchers.prototype.toBeFalsy = function() { + return !this.actual; +}; + + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was called. + */ +jasmine.Matchers.prototype.toHaveBeenCalled = function() { + if (arguments.length > 0) { + throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); + } + + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy " + this.actual.identity + " to have been called.", + "Expected spy " + this.actual.identity + " not to have been called." + ]; + }; + + return this.actual.wasCalled; +}; + +/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ +jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was not called. + * + * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead + */ +jasmine.Matchers.prototype.wasNotCalled = function() { + if (arguments.length > 0) { + throw new Error('wasNotCalled does not take arguments'); + } + + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy " + this.actual.identity + " to not have been called.", + "Expected spy " + this.actual.identity + " to have been called." + ]; + }; + + return !this.actual.wasCalled; +}; + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. + * + * @example + * + */ +jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { + var expectedArgs = jasmine.util.argsToArray(arguments); + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + this.message = function() { + var invertedMessage = "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was."; + var positiveMessage = ""; + if (this.actual.callCount === 0) { + positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called."; + } else { + positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but actual calls were " + jasmine.pp(this.actual.argsForCall).replace(/^\[ | \]$/g, '') + } + return [positiveMessage, invertedMessage]; + }; + + return this.env.contains_(this.actual.argsForCall, expectedArgs); +}; + +/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ +jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; + +/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ +jasmine.Matchers.prototype.wasNotCalledWith = function() { + var expectedArgs = jasmine.util.argsToArray(arguments); + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", + "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" + ]; + }; + + return !this.env.contains_(this.actual.argsForCall, expectedArgs); +}; + +/** + * Matcher that checks that the expected item is an element in the actual Array. + * + * @param {Object} expected + */ +jasmine.Matchers.prototype.toContain = function(expected) { + return this.env.contains_(this.actual, expected); +}; + +/** + * Matcher that checks that the expected item is NOT an element in the actual Array. + * + * @param {Object} expected + * @deprecated as of 1.0. Use not.toContain() instead. + */ +jasmine.Matchers.prototype.toNotContain = function(expected) { + return !this.env.contains_(this.actual, expected); +}; + +jasmine.Matchers.prototype.toBeLessThan = function(expected) { + return this.actual < expected; +}; + +jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { + return this.actual > expected; +}; + +/** + * Matcher that checks that the expected item is equal to the actual item + * up to a given level of decimal precision (default 2). + * + * @param {Number} expected + * @param {Number} precision, as number of decimal places + */ +jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) { + if (!(precision === 0)) { + precision = precision || 2; + } + return Math.abs(expected - this.actual) < (Math.pow(10, -precision) / 2); +}; + +/** + * Matcher that checks that the expected exception was thrown by the actual. + * + * @param {String} [expected] + */ +jasmine.Matchers.prototype.toThrow = function(expected) { + var result = false; + var exception; + if (typeof this.actual != 'function') { + throw new Error('Actual is not a function'); + } + try { + this.actual(); + } catch (e) { + exception = e; + } + if (exception) { + result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); + } + + var not = this.isNot ? "not " : ""; + + this.message = function() { + if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { + return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' '); + } else { + return "Expected function to throw an exception."; + } + }; + + return result; +}; + +jasmine.Matchers.Any = function(expectedClass) { + this.expectedClass = expectedClass; +}; + +jasmine.Matchers.Any.prototype.jasmineMatches = function(other) { + if (this.expectedClass == String) { + return typeof other == 'string' || other instanceof String; + } + + if (this.expectedClass == Number) { + return typeof other == 'number' || other instanceof Number; + } + + if (this.expectedClass == Function) { + return typeof other == 'function' || other instanceof Function; + } + + if (this.expectedClass == Object) { + return typeof other == 'object'; + } + + return other instanceof this.expectedClass; +}; + +jasmine.Matchers.Any.prototype.jasmineToString = function() { + return ''; +}; + +jasmine.Matchers.ObjectContaining = function (sample) { + this.sample = sample; +}; + +jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) { + mismatchKeys = mismatchKeys || []; + mismatchValues = mismatchValues || []; + + var env = jasmine.getEnv(); + + var hasKey = function(obj, keyName) { + return obj != null && obj[keyName] !== jasmine.undefined; + }; + + for (var property in this.sample) { + if (!hasKey(other, property) && hasKey(this.sample, property)) { + mismatchKeys.push("expected has key '" + property + "', but missing from actual."); + } + else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) { + mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual."); + } + } + + return (mismatchKeys.length === 0 && mismatchValues.length === 0); +}; + +jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () { + return ""; +}; +// Mock setTimeout, clearTimeout +// Contributed by Pivotal Computer Systems, www.pivotalsf.com + +jasmine.FakeTimer = function() { + this.reset(); + + var self = this; + self.setTimeout = function(funcToCall, millis) { + self.timeoutsMade++; + self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); + return self.timeoutsMade; + }; + + self.setInterval = function(funcToCall, millis) { + self.timeoutsMade++; + self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); + return self.timeoutsMade; + }; + + self.clearTimeout = function(timeoutKey) { + self.scheduledFunctions[timeoutKey] = jasmine.undefined; + }; + + self.clearInterval = function(timeoutKey) { + self.scheduledFunctions[timeoutKey] = jasmine.undefined; + }; + +}; + +jasmine.FakeTimer.prototype.reset = function() { + this.timeoutsMade = 0; + this.scheduledFunctions = {}; + this.nowMillis = 0; +}; + +jasmine.FakeTimer.prototype.tick = function(millis) { + var oldMillis = this.nowMillis; + var newMillis = oldMillis + millis; + this.runFunctionsWithinRange(oldMillis, newMillis); + this.nowMillis = newMillis; +}; + +jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { + var scheduledFunc; + var funcsToRun = []; + for (var timeoutKey in this.scheduledFunctions) { + scheduledFunc = this.scheduledFunctions[timeoutKey]; + if (scheduledFunc != jasmine.undefined && + scheduledFunc.runAtMillis >= oldMillis && + scheduledFunc.runAtMillis <= nowMillis) { + funcsToRun.push(scheduledFunc); + this.scheduledFunctions[timeoutKey] = jasmine.undefined; + } + } + + if (funcsToRun.length > 0) { + funcsToRun.sort(function(a, b) { + return a.runAtMillis - b.runAtMillis; + }); + for (var i = 0; i < funcsToRun.length; ++i) { + try { + var funcToRun = funcsToRun[i]; + this.nowMillis = funcToRun.runAtMillis; + funcToRun.funcToCall(); + if (funcToRun.recurring) { + this.scheduleFunction(funcToRun.timeoutKey, + funcToRun.funcToCall, + funcToRun.millis, + true); + } + } catch(e) { + } + } + this.runFunctionsWithinRange(oldMillis, nowMillis); + } +}; + +jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { + this.scheduledFunctions[timeoutKey] = { + runAtMillis: this.nowMillis + millis, + funcToCall: funcToCall, + recurring: recurring, + timeoutKey: timeoutKey, + millis: millis + }; +}; + +/** + * @namespace + */ +jasmine.Clock = { + defaultFakeTimer: new jasmine.FakeTimer(), + + reset: function() { + jasmine.Clock.assertInstalled(); + jasmine.Clock.defaultFakeTimer.reset(); + }, + + tick: function(millis) { + jasmine.Clock.assertInstalled(); + jasmine.Clock.defaultFakeTimer.tick(millis); + }, + + runFunctionsWithinRange: function(oldMillis, nowMillis) { + jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); + }, + + scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { + jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); + }, + + useMock: function() { + if (!jasmine.Clock.isInstalled()) { + var spec = jasmine.getEnv().currentSpec; + spec.after(jasmine.Clock.uninstallMock); + + jasmine.Clock.installMock(); + } + }, + + installMock: function() { + jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; + }, + + uninstallMock: function() { + jasmine.Clock.assertInstalled(); + jasmine.Clock.installed = jasmine.Clock.real; + }, + + real: { + setTimeout: jasmine.getGlobal().setTimeout, + clearTimeout: jasmine.getGlobal().clearTimeout, + setInterval: jasmine.getGlobal().setInterval, + clearInterval: jasmine.getGlobal().clearInterval + }, + + assertInstalled: function() { + if (!jasmine.Clock.isInstalled()) { + throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); + } + }, + + isInstalled: function() { + return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; + }, + + installed: null +}; +jasmine.Clock.installed = jasmine.Clock.real; + +//else for IE support +jasmine.getGlobal().setTimeout = function(funcToCall, millis) { + if (jasmine.Clock.installed.setTimeout.apply) { + return jasmine.Clock.installed.setTimeout.apply(this, arguments); + } else { + return jasmine.Clock.installed.setTimeout(funcToCall, millis); + } +}; + +jasmine.getGlobal().setInterval = function(funcToCall, millis) { + if (jasmine.Clock.installed.setInterval.apply) { + return jasmine.Clock.installed.setInterval.apply(this, arguments); + } else { + return jasmine.Clock.installed.setInterval(funcToCall, millis); + } +}; + +jasmine.getGlobal().clearTimeout = function(timeoutKey) { + if (jasmine.Clock.installed.clearTimeout.apply) { + return jasmine.Clock.installed.clearTimeout.apply(this, arguments); + } else { + return jasmine.Clock.installed.clearTimeout(timeoutKey); + } +}; + +jasmine.getGlobal().clearInterval = function(timeoutKey) { + if (jasmine.Clock.installed.clearTimeout.apply) { + return jasmine.Clock.installed.clearInterval.apply(this, arguments); + } else { + return jasmine.Clock.installed.clearInterval(timeoutKey); + } +}; + +/** + * @constructor + */ +jasmine.MultiReporter = function() { + this.subReporters_ = []; +}; +jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); + +jasmine.MultiReporter.prototype.addReporter = function(reporter) { + this.subReporters_.push(reporter); +}; + +(function() { + var functionNames = [ + "reportRunnerStarting", + "reportRunnerResults", + "reportSuiteResults", + "reportSpecStarting", + "reportSpecResults", + "log" + ]; + for (var i = 0; i < functionNames.length; i++) { + var functionName = functionNames[i]; + jasmine.MultiReporter.prototype[functionName] = (function(functionName) { + return function() { + for (var j = 0; j < this.subReporters_.length; j++) { + var subReporter = this.subReporters_[j]; + if (subReporter[functionName]) { + subReporter[functionName].apply(subReporter, arguments); + } + } + }; + })(functionName); + } +})(); +/** + * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults + * + * @constructor + */ +jasmine.NestedResults = function() { + /** + * The total count of results + */ + this.totalCount = 0; + /** + * Number of passed results + */ + this.passedCount = 0; + /** + * Number of failed results + */ + this.failedCount = 0; + /** + * Was this suite/spec skipped? + */ + this.skipped = false; + /** + * @ignore + */ + this.items_ = []; +}; + +/** + * Roll up the result counts. + * + * @param result + */ +jasmine.NestedResults.prototype.rollupCounts = function(result) { + this.totalCount += result.totalCount; + this.passedCount += result.passedCount; + this.failedCount += result.failedCount; +}; + +/** + * Adds a log message. + * @param values Array of message parts which will be concatenated later. + */ +jasmine.NestedResults.prototype.log = function(values) { + this.items_.push(new jasmine.MessageResult(values)); +}; + +/** + * Getter for the results: message & results. + */ +jasmine.NestedResults.prototype.getItems = function() { + return this.items_; +}; + +/** + * Adds a result, tracking counts (total, passed, & failed) + * @param {jasmine.ExpectationResult|jasmine.NestedResults} result + */ +jasmine.NestedResults.prototype.addResult = function(result) { + if (result.type != 'log') { + if (result.items_) { + this.rollupCounts(result); + } else { + this.totalCount++; + if (result.passed()) { + this.passedCount++; + } else { + this.failedCount++; + } + } + } + this.items_.push(result); +}; + +/** + * @returns {Boolean} True if everything below passed + */ +jasmine.NestedResults.prototype.passed = function() { + return this.passedCount === this.totalCount; +}; +/** + * Base class for pretty printing for expectation results. + */ +jasmine.PrettyPrinter = function() { + this.ppNestLevel_ = 0; +}; + +/** + * Formats a value in a nice, human-readable string. + * + * @param value + */ +jasmine.PrettyPrinter.prototype.format = function(value) { + this.ppNestLevel_++; + try { + if (value === jasmine.undefined) { + this.emitScalar('undefined'); + } else if (value === null) { + this.emitScalar('null'); + } else if (value === jasmine.getGlobal()) { + this.emitScalar(''); + } else if (value.jasmineToString) { + this.emitScalar(value.jasmineToString()); + } else if (typeof value === 'string') { + this.emitString(value); + } else if (jasmine.isSpy(value)) { + this.emitScalar("spy on " + value.identity); + } else if (value instanceof RegExp) { + this.emitScalar(value.toString()); + } else if (typeof value === 'function') { + this.emitScalar('Function'); + } else if (typeof value.nodeType === 'number') { + this.emitScalar('HTMLNode'); + } else if (value instanceof Date) { + this.emitScalar('Date(' + value + ')'); + } else if (value.__Jasmine_been_here_before__) { + this.emitScalar(''); + } else if (jasmine.isArray_(value) || typeof value == 'object') { + value.__Jasmine_been_here_before__ = true; + if (jasmine.isArray_(value)) { + this.emitArray(value); + } else { + this.emitObject(value); + } + delete value.__Jasmine_been_here_before__; + } else { + this.emitScalar(value.toString()); + } + } finally { + this.ppNestLevel_--; + } +}; + +jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { + for (var property in obj) { + if (!obj.hasOwnProperty(property)) continue; + if (property == '__Jasmine_been_here_before__') continue; + fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && + obj.__lookupGetter__(property) !== null) : false); + } +}; + +jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; + +jasmine.StringPrettyPrinter = function() { + jasmine.PrettyPrinter.call(this); + + this.string = ''; +}; +jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); + +jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { + this.append(value); +}; + +jasmine.StringPrettyPrinter.prototype.emitString = function(value) { + this.append("'" + value + "'"); +}; + +jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { + if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) { + this.append("Array"); + return; + } + + this.append('[ '); + for (var i = 0; i < array.length; i++) { + if (i > 0) { + this.append(', '); + } + this.format(array[i]); + } + this.append(' ]'); +}; + +jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { + if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) { + this.append("Object"); + return; + } + + var self = this; + this.append('{ '); + var first = true; + + this.iterateObject(obj, function(property, isGetter) { + if (first) { + first = false; + } else { + self.append(', '); + } + + self.append(property); + self.append(' : '); + if (isGetter) { + self.append(''); + } else { + self.format(obj[property]); + } + }); + + this.append(' }'); +}; + +jasmine.StringPrettyPrinter.prototype.append = function(value) { + this.string += value; +}; +jasmine.Queue = function(env) { + this.env = env; + + // parallel to blocks. each true value in this array means the block will + // get executed even if we abort + this.ensured = []; + this.blocks = []; + this.running = false; + this.index = 0; + this.offset = 0; + this.abort = false; +}; + +jasmine.Queue.prototype.addBefore = function(block, ensure) { + if (ensure === jasmine.undefined) { + ensure = false; + } + + this.blocks.unshift(block); + this.ensured.unshift(ensure); +}; + +jasmine.Queue.prototype.add = function(block, ensure) { + if (ensure === jasmine.undefined) { + ensure = false; + } + + this.blocks.push(block); + this.ensured.push(ensure); +}; + +jasmine.Queue.prototype.insertNext = function(block, ensure) { + if (ensure === jasmine.undefined) { + ensure = false; + } + + this.ensured.splice((this.index + this.offset + 1), 0, ensure); + this.blocks.splice((this.index + this.offset + 1), 0, block); + this.offset++; +}; + +jasmine.Queue.prototype.start = function(onComplete) { + this.running = true; + this.onComplete = onComplete; + this.next_(); +}; + +jasmine.Queue.prototype.isRunning = function() { + return this.running; +}; + +jasmine.Queue.LOOP_DONT_RECURSE = true; + +jasmine.Queue.prototype.next_ = function() { + var self = this; + var goAgain = true; + + while (goAgain) { + goAgain = false; + + if (self.index < self.blocks.length && !(this.abort && !this.ensured[self.index])) { + var calledSynchronously = true; + var completedSynchronously = false; + + var onComplete = function () { + if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { + completedSynchronously = true; + return; + } + + if (self.blocks[self.index].abort) { + self.abort = true; + } + + self.offset = 0; + self.index++; + + var now = new Date().getTime(); + if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { + self.env.lastUpdate = now; + self.env.setTimeout(function() { + self.next_(); + }, 0); + } else { + if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { + goAgain = true; + } else { + self.next_(); + } + } + }; + self.blocks[self.index].execute(onComplete); + + calledSynchronously = false; + if (completedSynchronously) { + onComplete(); + } + + } else { + self.running = false; + if (self.onComplete) { + self.onComplete(); + } + } + } +}; + +jasmine.Queue.prototype.results = function() { + var results = new jasmine.NestedResults(); + for (var i = 0; i < this.blocks.length; i++) { + if (this.blocks[i].results) { + results.addResult(this.blocks[i].results()); + } + } + return results; +}; + + +/** + * Runner + * + * @constructor + * @param {jasmine.Env} env + */ +jasmine.Runner = function(env) { + var self = this; + self.env = env; + self.queue = new jasmine.Queue(env); + self.before_ = []; + self.after_ = []; + self.suites_ = []; +}; + +jasmine.Runner.prototype.execute = function() { + var self = this; + if (self.env.reporter.reportRunnerStarting) { + self.env.reporter.reportRunnerStarting(this); + } + self.queue.start(function () { + self.finishCallback(); + }); +}; + +jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { + beforeEachFunction.typeName = 'beforeEach'; + this.before_.splice(0,0,beforeEachFunction); +}; + +jasmine.Runner.prototype.afterEach = function(afterEachFunction) { + afterEachFunction.typeName = 'afterEach'; + this.after_.splice(0,0,afterEachFunction); +}; + + +jasmine.Runner.prototype.finishCallback = function() { + this.env.reporter.reportRunnerResults(this); +}; + +jasmine.Runner.prototype.addSuite = function(suite) { + this.suites_.push(suite); +}; + +jasmine.Runner.prototype.add = function(block) { + if (block instanceof jasmine.Suite) { + this.addSuite(block); + } + this.queue.add(block); +}; + +jasmine.Runner.prototype.specs = function () { + var suites = this.suites(); + var specs = []; + for (var i = 0; i < suites.length; i++) { + specs = specs.concat(suites[i].specs()); + } + return specs; +}; + +jasmine.Runner.prototype.suites = function() { + return this.suites_; +}; + +jasmine.Runner.prototype.topLevelSuites = function() { + var topLevelSuites = []; + for (var i = 0; i < this.suites_.length; i++) { + if (!this.suites_[i].parentSuite) { + topLevelSuites.push(this.suites_[i]); + } + } + return topLevelSuites; +}; + +jasmine.Runner.prototype.results = function() { + return this.queue.results(); +}; +/** + * Internal representation of a Jasmine specification, or test. + * + * @constructor + * @param {jasmine.Env} env + * @param {jasmine.Suite} suite + * @param {String} description + */ +jasmine.Spec = function(env, suite, description) { + if (!env) { + throw new Error('jasmine.Env() required'); + } + if (!suite) { + throw new Error('jasmine.Suite() required'); + } + var spec = this; + spec.id = env.nextSpecId ? env.nextSpecId() : null; + spec.env = env; + spec.suite = suite; + spec.description = description; + spec.queue = new jasmine.Queue(env); + + spec.afterCallbacks = []; + spec.spies_ = []; + + spec.results_ = new jasmine.NestedResults(); + spec.results_.description = description; + spec.matchersClass = null; +}; + +jasmine.Spec.prototype.getFullName = function() { + return this.suite.getFullName() + ' ' + this.description + '.'; +}; + + +jasmine.Spec.prototype.results = function() { + return this.results_; +}; + +/** + * All parameters are pretty-printed and concatenated together, then written to the spec's output. + * + * Be careful not to leave calls to jasmine.log in production code. + */ +jasmine.Spec.prototype.log = function() { + return this.results_.log(arguments); +}; + +jasmine.Spec.prototype.runs = function (func) { + var block = new jasmine.Block(this.env, func, this); + this.addToQueue(block); + return this; +}; + +jasmine.Spec.prototype.addToQueue = function (block) { + if (this.queue.isRunning()) { + this.queue.insertNext(block); + } else { + this.queue.add(block); + } +}; + +/** + * @param {jasmine.ExpectationResult} result + */ +jasmine.Spec.prototype.addMatcherResult = function(result) { + this.results_.addResult(result); +}; + +jasmine.Spec.prototype.expect = function(actual) { + var positive = new (this.getMatchersClass_())(this.env, actual, this); + positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); + return positive; +}; + +/** + * Waits a fixed time period before moving to the next block. + * + * @deprecated Use waitsFor() instead + * @param {Number} timeout milliseconds to wait + */ +jasmine.Spec.prototype.waits = function(timeout) { + var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); + this.addToQueue(waitsFunc); + return this; +}; + +/** + * Waits for the latchFunction to return true before proceeding to the next block. + * + * @param {Function} latchFunction + * @param {String} optional_timeoutMessage + * @param {Number} optional_timeout + */ +jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { + var latchFunction_ = null; + var optional_timeoutMessage_ = null; + var optional_timeout_ = null; + + for (var i = 0; i < arguments.length; i++) { + var arg = arguments[i]; + switch (typeof arg) { + case 'function': + latchFunction_ = arg; + break; + case 'string': + optional_timeoutMessage_ = arg; + break; + case 'number': + optional_timeout_ = arg; + break; + } + } + + var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); + this.addToQueue(waitsForFunc); + return this; +}; + +jasmine.Spec.prototype.fail = function (e) { + var expectationResult = new jasmine.ExpectationResult({ + passed: false, + message: e ? jasmine.util.formatException(e) : 'Exception', + trace: { stack: e.stack } + }); + this.results_.addResult(expectationResult); +}; + +jasmine.Spec.prototype.getMatchersClass_ = function() { + return this.matchersClass || this.env.matchersClass; +}; + +jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { + var parent = this.getMatchersClass_(); + var newMatchersClass = function() { + parent.apply(this, arguments); + }; + jasmine.util.inherit(newMatchersClass, parent); + jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); + this.matchersClass = newMatchersClass; +}; + +jasmine.Spec.prototype.finishCallback = function() { + this.env.reporter.reportSpecResults(this); +}; + +jasmine.Spec.prototype.finish = function(onComplete) { + this.removeAllSpies(); + this.finishCallback(); + if (onComplete) { + onComplete(); + } +}; + +jasmine.Spec.prototype.after = function(doAfter) { + if (this.queue.isRunning()) { + this.queue.add(new jasmine.Block(this.env, doAfter, this), true); + } else { + this.afterCallbacks.unshift(doAfter); + } +}; + +jasmine.Spec.prototype.execute = function(onComplete) { + var spec = this; + if (!spec.env.specFilter(spec)) { + spec.results_.skipped = true; + spec.finish(onComplete); + return; + } + + this.env.reporter.reportSpecStarting(this); + + spec.env.currentSpec = spec; + + spec.addBeforesAndAftersToQueue(); + + spec.queue.start(function () { + spec.finish(onComplete); + }); +}; + +jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { + var runner = this.env.currentRunner(); + var i; + + for (var suite = this.suite; suite; suite = suite.parentSuite) { + for (i = 0; i < suite.before_.length; i++) { + this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); + } + } + for (i = 0; i < runner.before_.length; i++) { + this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); + } + for (i = 0; i < this.afterCallbacks.length; i++) { + this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this), true); + } + for (suite = this.suite; suite; suite = suite.parentSuite) { + for (i = 0; i < suite.after_.length; i++) { + this.queue.add(new jasmine.Block(this.env, suite.after_[i], this), true); + } + } + for (i = 0; i < runner.after_.length; i++) { + this.queue.add(new jasmine.Block(this.env, runner.after_[i], this), true); + } +}; + +jasmine.Spec.prototype.explodes = function() { + throw 'explodes function should not have been called'; +}; + +jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { + if (obj == jasmine.undefined) { + throw "spyOn could not find an object to spy upon for " + methodName + "()"; + } + + if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { + throw methodName + '() method does not exist'; + } + + if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { + throw new Error(methodName + ' has already been spied upon'); + } + + var spyObj = jasmine.createSpy(methodName); + + this.spies_.push(spyObj); + spyObj.baseObj = obj; + spyObj.methodName = methodName; + spyObj.originalValue = obj[methodName]; + + obj[methodName] = spyObj; + + return spyObj; +}; + +jasmine.Spec.prototype.removeAllSpies = function() { + for (var i = 0; i < this.spies_.length; i++) { + var spy = this.spies_[i]; + spy.baseObj[spy.methodName] = spy.originalValue; + } + this.spies_ = []; +}; + +/** + * Internal representation of a Jasmine suite. + * + * @constructor + * @param {jasmine.Env} env + * @param {String} description + * @param {Function} specDefinitions + * @param {jasmine.Suite} parentSuite + */ +jasmine.Suite = function(env, description, specDefinitions, parentSuite) { + var self = this; + self.id = env.nextSuiteId ? env.nextSuiteId() : null; + self.description = description; + self.queue = new jasmine.Queue(env); + self.parentSuite = parentSuite; + self.env = env; + self.before_ = []; + self.after_ = []; + self.children_ = []; + self.suites_ = []; + self.specs_ = []; +}; + +jasmine.Suite.prototype.getFullName = function() { + var fullName = this.description; + for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { + fullName = parentSuite.description + ' ' + fullName; + } + return fullName; +}; + +jasmine.Suite.prototype.finish = function(onComplete) { + this.env.reporter.reportSuiteResults(this); + this.finished = true; + if (typeof(onComplete) == 'function') { + onComplete(); + } +}; + +jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { + beforeEachFunction.typeName = 'beforeEach'; + this.before_.unshift(beforeEachFunction); +}; + +jasmine.Suite.prototype.afterEach = function(afterEachFunction) { + afterEachFunction.typeName = 'afterEach'; + this.after_.unshift(afterEachFunction); +}; + +jasmine.Suite.prototype.results = function() { + return this.queue.results(); +}; + +jasmine.Suite.prototype.add = function(suiteOrSpec) { + this.children_.push(suiteOrSpec); + if (suiteOrSpec instanceof jasmine.Suite) { + this.suites_.push(suiteOrSpec); + this.env.currentRunner().addSuite(suiteOrSpec); + } else { + this.specs_.push(suiteOrSpec); + } + this.queue.add(suiteOrSpec); +}; + +jasmine.Suite.prototype.specs = function() { + return this.specs_; +}; + +jasmine.Suite.prototype.suites = function() { + return this.suites_; +}; + +jasmine.Suite.prototype.children = function() { + return this.children_; +}; + +jasmine.Suite.prototype.execute = function(onComplete) { + var self = this; + this.queue.start(function () { + self.finish(onComplete); + }); +}; +jasmine.WaitsBlock = function(env, timeout, spec) { + this.timeout = timeout; + jasmine.Block.call(this, env, null, spec); +}; + +jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); + +jasmine.WaitsBlock.prototype.execute = function (onComplete) { + if (jasmine.VERBOSE) { + this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); + } + this.env.setTimeout(function () { + onComplete(); + }, this.timeout); +}; +/** + * A block which waits for some condition to become true, with timeout. + * + * @constructor + * @extends jasmine.Block + * @param {jasmine.Env} env The Jasmine environment. + * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. + * @param {Function} latchFunction A function which returns true when the desired condition has been met. + * @param {String} message The message to display if the desired condition hasn't been met within the given time period. + * @param {jasmine.Spec} spec The Jasmine spec. + */ +jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { + this.timeout = timeout || env.defaultTimeoutInterval; + this.latchFunction = latchFunction; + this.message = message; + this.totalTimeSpentWaitingForLatch = 0; + jasmine.Block.call(this, env, null, spec); +}; +jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); + +jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; + +jasmine.WaitsForBlock.prototype.execute = function(onComplete) { + if (jasmine.VERBOSE) { + this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); + } + var latchFunctionResult; + try { + latchFunctionResult = this.latchFunction.apply(this.spec); + } catch (e) { + this.spec.fail(e); + onComplete(); + return; + } + + if (latchFunctionResult) { + onComplete(); + } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { + var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); + this.spec.fail({ + name: 'timeout', + message: message + }); + + this.abort = true; + onComplete(); + } else { + this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; + var self = this; + this.env.setTimeout(function() { + self.execute(onComplete); + }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); + } +}; + +jasmine.version_= { + "major": 1, + "minor": 3, + "build": 1, + "revision": 1354556913 +}; diff --git a/examples/vanillajs/bower_components/todomvc-common/base.css b/examples/vanillajs/bower_components/todomvc-common/base.css new file mode 100644 index 0000000000..285f531307 --- /dev/null +++ b/examples/vanillajs/bower_components/todomvc-common/base.css @@ -0,0 +1,554 @@ +html, +body { + margin: 0; + padding: 0; +} + +button { + margin: 0; + padding: 0; + border: 0; + background: none; + font-size: 100%; + vertical-align: baseline; + font-family: inherit; + color: inherit; + -webkit-appearance: none; + -ms-appearance: none; + -o-appearance: none; + appearance: none; +} + +body { + font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; + line-height: 1.4em; + background: #eaeaea url('bg.png'); + color: #4d4d4d; + width: 550px; + margin: 0 auto; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + -o-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +button, +input[type="checkbox"] { + outline: none; +} + +#todoapp { + background: #fff; + background: rgba(255, 255, 255, 0.9); + margin: 130px 0 40px 0; + border: 1px solid #ccc; + position: relative; + border-top-left-radius: 2px; + border-top-right-radius: 2px; + box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2), + 0 25px 50px 0 rgba(0, 0, 0, 0.15); +} + +#todoapp:before { + content: ''; + border-left: 1px solid #f5d6d6; + border-right: 1px solid #f5d6d6; + width: 2px; + position: absolute; + top: 0; + left: 40px; + height: 100%; +} + +#todoapp input::-webkit-input-placeholder { + font-style: italic; +} + +#todoapp input::-moz-placeholder { + font-style: italic; + color: #a9a9a9; +} + +#todoapp h1 { + position: absolute; + top: -120px; + width: 100%; + font-size: 70px; + font-weight: bold; + text-align: center; + color: #b3b3b3; + color: rgba(255, 255, 255, 0.3); + text-shadow: -1px -1px rgba(0, 0, 0, 0.2); + -webkit-text-rendering: optimizeLegibility; + -moz-text-rendering: optimizeLegibility; + -ms-text-rendering: optimizeLegibility; + -o-text-rendering: optimizeLegibility; + text-rendering: optimizeLegibility; +} + +#header { + padding-top: 15px; + border-radius: inherit; +} + +#header:before { + content: ''; + position: absolute; + top: 0; + right: 0; + left: 0; + height: 15px; + z-index: 2; + border-bottom: 1px solid #6c615c; + background: #8d7d77; + background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8))); + background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); + background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670'); + border-top-left-radius: 1px; + border-top-right-radius: 1px; +} + +#new-todo, +.edit { + position: relative; + margin: 0; + width: 100%; + font-size: 24px; + font-family: inherit; + line-height: 1.4em; + border: 0; + outline: none; + color: inherit; + padding: 6px; + border: 1px solid #999; + box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + -o-box-sizing: border-box; + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + -o-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +#new-todo { + padding: 16px 16px 16px 60px; + border: none; + background: rgba(0, 0, 0, 0.02); + z-index: 2; + box-shadow: none; +} + +#main { + position: relative; + z-index: 2; + border-top: 1px dotted #adadad; +} + +label[for='toggle-all'] { + display: none; +} + +#toggle-all { + position: absolute; + top: -42px; + left: -4px; + width: 40px; + text-align: center; + /* Mobile Safari */ + border: none; +} + +#toggle-all:before { + content: '»'; + font-size: 28px; + color: #d9d9d9; + padding: 0 25px 7px; +} + +#toggle-all:checked:before { + color: #737373; +} + +#todo-list { + margin: 0; + padding: 0; + list-style: none; +} + +#todo-list li { + position: relative; + font-size: 24px; + border-bottom: 1px dotted #ccc; +} + +#todo-list li:last-child { + border-bottom: none; +} + +#todo-list li.editing { + border-bottom: none; + padding: 0; +} + +#todo-list li.editing .edit { + display: block; + width: 506px; + padding: 13px 17px 12px 17px; + margin: 0 0 0 43px; +} + +#todo-list li.editing .view { + display: none; +} + +#todo-list li .toggle { + text-align: center; + width: 40px; + /* auto, since non-WebKit browsers doesn't support input styling */ + height: auto; + position: absolute; + top: 0; + bottom: 0; + margin: auto 0; + /* Mobile Safari */ + border: none; + -webkit-appearance: none; + -ms-appearance: none; + -o-appearance: none; + appearance: none; +} + +#todo-list li .toggle:after { + content: '✔'; + /* 40 + a couple of pixels visual adjustment */ + line-height: 43px; + font-size: 20px; + color: #d9d9d9; + text-shadow: 0 -1px 0 #bfbfbf; +} + +#todo-list li .toggle:checked:after { + color: #85ada7; + text-shadow: 0 1px 0 #669991; + bottom: 1px; + position: relative; +} + +#todo-list li label { + white-space: pre; + word-break: break-word; + padding: 15px 60px 15px 15px; + margin-left: 45px; + display: block; + line-height: 1.2; + -webkit-transition: color 0.4s; + transition: color 0.4s; +} + +#todo-list li.completed label { + color: #a9a9a9; + text-decoration: line-through; +} + +#todo-list li .destroy { + display: none; + position: absolute; + top: 0; + right: 10px; + bottom: 0; + width: 40px; + height: 40px; + margin: auto 0; + font-size: 22px; + color: #a88a8a; + -webkit-transition: all 0.2s; + transition: all 0.2s; +} + +#todo-list li .destroy:hover { + text-shadow: 0 0 1px #000, + 0 0 10px rgba(199, 107, 107, 0.8); + -webkit-transform: scale(1.3); + transform: scale(1.3); +} + +#todo-list li .destroy:after { + content: '✖'; +} + +#todo-list li:hover .destroy { + display: block; +} + +#todo-list li .edit { + display: none; +} + +#todo-list li.editing:last-child { + margin-bottom: -1px; +} + +#footer { + color: #777; + padding: 0 15px; + position: absolute; + right: 0; + bottom: -31px; + left: 0; + height: 20px; + z-index: 1; + text-align: center; +} + +#footer:before { + content: ''; + position: absolute; + right: 0; + bottom: 31px; + left: 0; + height: 50px; + z-index: -1; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), + 0 6px 0 -3px rgba(255, 255, 255, 0.8), + 0 7px 1px -3px rgba(0, 0, 0, 0.3), + 0 43px 0 -6px rgba(255, 255, 255, 0.8), + 0 44px 2px -6px rgba(0, 0, 0, 0.2); +} + +#todo-count { + float: left; + text-align: left; +} + +#filters { + margin: 0; + padding: 0; + list-style: none; + position: absolute; + right: 0; + left: 0; +} + +#filters li { + display: inline; +} + +#filters li a { + color: #83756f; + margin: 2px; + text-decoration: none; +} + +#filters li a.selected { + font-weight: bold; +} + +#clear-completed { + float: right; + position: relative; + line-height: 20px; + text-decoration: none; + background: rgba(0, 0, 0, 0.1); + font-size: 11px; + padding: 0 10px; + border-radius: 3px; + box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2); +} + +#clear-completed:hover { + background: rgba(0, 0, 0, 0.15); + box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3); +} + +#info { + margin: 65px auto 0; + color: #a6a6a6; + font-size: 12px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); + text-align: center; +} + +#info a { + color: inherit; +} + +/* + Hack to remove background from Mobile Safari. + Can't use it globally since it destroys checkboxes in Firefox and Opera +*/ + +@media screen and (-webkit-min-device-pixel-ratio:0) { + #toggle-all, + #todo-list li .toggle { + background: none; + } + + #todo-list li .toggle { + height: 40px; + } + + #toggle-all { + top: -56px; + left: -15px; + width: 65px; + height: 41px; + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + -webkit-appearance: none; + appearance: none; + } +} + +.hidden { + display: none; +} + +hr { + margin: 20px 0; + border: 0; + border-top: 1px dashed #C5C5C5; + border-bottom: 1px dashed #F7F7F7; +} + +.learn a { + font-weight: normal; + text-decoration: none; + color: #b83f45; +} + +.learn a:hover { + text-decoration: underline; + color: #787e7e; +} + +.learn h3, +.learn h4, +.learn h5 { + margin: 10px 0; + font-weight: 500; + line-height: 1.2; + color: #000; +} + +.learn h3 { + font-size: 24px; +} + +.learn h4 { + font-size: 18px; +} + +.learn h5 { + margin-bottom: 0; + font-size: 14px; +} + +.learn ul { + padding: 0; + margin: 0 0 30px 25px; +} + +.learn li { + line-height: 20px; +} + +.learn p { + font-size: 15px; + font-weight: 300; + line-height: 1.3; + margin-top: 0; + margin-bottom: 0; +} + +.quote { + border: none; + margin: 20px 0 60px 0; +} + +.quote p { + font-style: italic; +} + +.quote p:before { + content: '“'; + font-size: 50px; + opacity: .15; + position: absolute; + top: -20px; + left: 3px; +} + +.quote p:after { + content: '”'; + font-size: 50px; + opacity: .15; + position: absolute; + bottom: -42px; + right: 3px; +} + +.quote footer { + position: absolute; + bottom: -40px; + right: 0; +} + +.quote footer img { + border-radius: 3px; +} + +.quote footer a { + margin-left: 5px; + vertical-align: middle; +} + +.speech-bubble { + position: relative; + padding: 10px; + background: rgba(0, 0, 0, .04); + border-radius: 5px; +} + +.speech-bubble:after { + content: ''; + position: absolute; + top: 100%; + right: 30px; + border: 13px solid transparent; + border-top-color: rgba(0, 0, 0, .04); +} + +.learn-bar > .learn { + position: absolute; + width: 272px; + top: 8px; + left: -300px; + padding: 10px; + border-radius: 5px; + background-color: rgba(255, 255, 255, .6); + -webkit-transition-property: left; + transition-property: left; + -webkit-transition-duration: 500ms; + transition-duration: 500ms; +} + +@media (min-width: 899px) { + .learn-bar { + width: auto; + margin: 0 0 0 300px; + } + + .learn-bar > .learn { + left: 8px; + } + + .learn-bar #todoapp { + width: 550px; + margin: 130px auto 40px auto; + } +} diff --git a/examples/vanillajs/bower_components/todomvc-common/base.js b/examples/vanillajs/bower_components/todomvc-common/base.js new file mode 100644 index 0000000000..d3a15127d8 --- /dev/null +++ b/examples/vanillajs/bower_components/todomvc-common/base.js @@ -0,0 +1,217 @@ +(function () { + 'use strict'; + + // Underscore's Template Module + // Courtesy of underscorejs.org + var _ = (function (_) { + _.defaults = function (object) { + if (!object) { + return object; + } + for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { + var iterable = arguments[argsIndex]; + if (iterable) { + for (var key in iterable) { + if (object[key] == null) { + object[key] = iterable[key]; + } + } + } + } + return object; + } + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + _.template = function(text, data, settings) { + var render; + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = new RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset) + .replace(escaper, function(match) { return '\\' + escapes[match]; }); + + if (escape) { + source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; + } + if (interpolate) { + source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; + } + if (evaluate) { + source += "';\n" + evaluate + "\n__p+='"; + } + index = offset + match.length; + return match; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + "return __p;\n"; + + try { + render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + if (data) return render(data, _); + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled function source as a convenience for precompilation. + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + + return template; + }; + + return _; + })({}); + + if (location.hostname === 'todomvc.com') { + window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')); + } + + function redirect() { + if (location.hostname === 'tastejs.github.io') { + location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com'); + } + } + + function findRoot() { + var base = location.href.indexOf('examples/'); + return location.href.substr(0, base); + } + + function getFile(file, callback) { + if (!location.host) { + return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.'); + } + + var xhr = new XMLHttpRequest(); + + xhr.open('GET', findRoot() + file, true); + xhr.send(); + + xhr.onload = function () { + if (xhr.status === 200 && callback) { + callback(xhr.responseText); + } + }; + } + + function Learn(learnJSON, config) { + if (!(this instanceof Learn)) { + return new Learn(learnJSON, config); + } + + var template, framework; + + if (typeof learnJSON !== 'object') { + try { + learnJSON = JSON.parse(learnJSON); + } catch (e) { + return; + } + } + + if (config) { + template = config.template; + framework = config.framework; + } + + if (!template && learnJSON.templates) { + template = learnJSON.templates.todomvc; + } + + if (!framework && document.querySelector('[data-framework]')) { + framework = document.querySelector('[data-framework]').dataset.framework; + } + + this.template = template; + + if (learnJSON.backend) { + this.frameworkJSON = learnJSON.backend; + this.append({ + backend: true + }); + } else if (learnJSON[framework]) { + this.frameworkJSON = learnJSON[framework]; + this.append(); + } + } + + Learn.prototype.append = function (opts) { + var aside = document.createElement('aside'); + aside.innerHTML = _.template(this.template, this.frameworkJSON); + aside.className = 'learn'; + + if (opts && opts.backend) { + // Remove demo link + var sourceLinks = aside.querySelector('.source-links'); + var heading = sourceLinks.firstElementChild; + var sourceLink = sourceLinks.lastElementChild; + // Correct link path + var href = sourceLink.getAttribute('href'); + sourceLink.setAttribute('href', href.substr(href.lastIndexOf('http'))); + sourceLinks.innerHTML = heading.outerHTML + sourceLink.outerHTML; + } else { + // Localize demo links + var demoLinks = aside.querySelectorAll('.demo-link'); + Array.prototype.forEach.call(demoLinks, function (demoLink) { + if (demoLink.getAttribute('href').substr(0, 4) !== 'http') { + demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href')); + } + }); + } + + document.body.className = (document.body.className + ' learn-bar').trim(); + document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); + }; + + redirect(); + getFile('learn.json', Learn); +})(); diff --git a/examples/vanillajs/bower_components/todomvc-common/bg.png b/examples/vanillajs/bower_components/todomvc-common/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..b2a7600825ee11f21849ed475499c7d1ecbcc870 GIT binary patch literal 2126 zcmV-U2(kBxP)+9y`=HK7nt=~3t000O5Nklm=04hVa_lXl_bm=+M;=eI<%$rO2ta`suh*Sr#POw*?adq!O!CV z*0&b)rkCCEV)1nIuiLBntUH-MJI;&qYgz|d@Nhnn_abi2g`pu4NAMVid3hS%?quz~ zjlJf(x8cj{VS zUVEP1msg9`^OFUhSO5rtXHhRlyU2i>6$BT=glG`{JlctGHrwRIm)6Buu65jLQn^H8 zvl9lZqUBA+%qvM5XC+yY@mfA(YB~XP=e|6<{%$^eU8nrK?v2ccb@Jom5CG>rKAL7J zplHEIavVp|0p1&m!G`Ti?<7iZ;}@G7#Z)}Km%2!FHnQ0*&?PLR)G!HAaHgU#c|$dz zpj#0HmeadIe>`#)=Jjkb{B=FKnU3pc-&Y@j_j6(h4Y~+Uq!^J=LHunx1xi4QXK-Y{&MoT8ky6Vb9c|WhnOwF~c=3zOy8agktliS6# z`#8F`9H)D=bmk9B5MnW&_r#)f$c+;$LSr-@^An8dhc~Iquuv>jOK7pw7LJ;&X0i1C zGMsHdP1Os@ny$$j!>XAii%7bp5k>`pyNA!~epb)()p9zR4Yl6z=U}{CIdh1z(FpAo zQIW!;0zpCyC4*7YLkZCO@cul0R_&zghsA8Ek)z%jNpKa92{@NH+SstX%@}xB*Jk!l}PZ1cClIns~}5^!RncUk*rmA z%SIVgt58EQxLJ+OiFqKkBuwquyZLUp|gr zPUbUbFbBrPd1xO`^C*r-i3p9*F#(OBh!4Wy@aC&*!|O|GXcjDA&YhF{;cD7*o(GS^ z@pQSE7)x_81=Qyje6*LQ#TXu-7(o;S8u3tpDA0_ddj%hmCspeXdhYR}-i9A=C`EoChUYuH~^x!9+|&(Pgb*>Ck<=9j|)*@xyfT zpP#+Kt<}39b|3Wl4fxT(+G?aH>gG^d@MEaUOJRfy55TtFI7^Y)VuMU=7Pp0y55jS~ z+TJ)igMyrqkX;wU8j68iIDtqJYhS_D3_Oem-@g6me_RfiQ}uT_K-A-9qG%}gk3E9c z!8`KgsMNXm)beloPfMql*|&$M>1q`i!cY)RFYnjNYu*gSv@8XebV7%}xYL>6)GJPJ zJpA7K31lcJuIFKSw-{x4FX0K2Az)~Xg<`sOu$4|^-(^XJX4YzoiNRvL zyuY7~26w-P^Zw%6F}shBwV2$02c8RsSUy6dM92diCAxiX3IUqsq5ig6>U_!Vy*q5$ zjm}16tJr+QZ&T?HkkpeIwVX-r1EI=@%Zlt;6g*mj&E!A))%gXJ=x6u#l zcKEP>bx4rqV*k`BBrZ$Mqjt{TsHcxUH>;)iAK}B(Kila&VD%b?6m&^0WK4^&EZNAI z8AMYs_%$D%|M+@+cQqL-hi4yG>h*jwdii!W1{}p>ZhwFYt_4Su1kjY9<-5`!$VNOI1y(EDSH4WpCijE_>al!Nt=^eVER#uJ1=b z;BWF2IgyF5LexV+yec$Kin;Ai@myo;G>zJ&jrdW#ecXhIZph5OYqw_PEfcF56Z5Zu_ZZE#q3Mc+N&1O^7hoh9QR7`+L$cBP5pqG=fqA0uh zUBukaxTFmH)<|Xbvp2c=HSbNkpXw73a5lv7V$jb92#yZ141@$X?F}Jt8gIU7Wm6m9 z?e_;;zsnm6`LZeP>T=$)Kr>Z?kr*UmFqR7zx0C6^bmcsc@1AGtw_rNH>-Xm$d*|Q< zn&1Ln0u7=l&ILs>%CkJp`DiG9F18x4Ne+lg<#i?e7jL%x;4ZnRkN^Mx07*qoM6N<$ Ef(>0N!2kdN literal 0 HcmV?d00001 diff --git a/examples/vanillajs/index.html b/examples/vanillajs/index.html index 56e3b02205..61d9c98943 100644 --- a/examples/vanillajs/index.html +++ b/examples/vanillajs/index.html @@ -3,8 +3,7 @@ VanillaJS • TodoMVC - - +
    @@ -39,7 +38,7 @@

    todos

    Refactored by Christoph Burgmer

    Part of TodoMVC

    - + diff --git a/examples/vanillajs/node_modules/todomvc-app-css/index.css b/examples/vanillajs/node_modules/todomvc-app-css/index.css deleted file mode 100644 index 4308848800..0000000000 --- a/examples/vanillajs/node_modules/todomvc-app-css/index.css +++ /dev/null @@ -1,394 +0,0 @@ -html, -body { - margin: 0; - padding: 0; -} - -button { - margin: 0; - padding: 0; - border: 0; - background: none; - font-size: 100%; - vertical-align: baseline; - font-family: inherit; - font-weight: inherit; - color: inherit; - -webkit-appearance: none; - -ms-appearance: none; - appearance: none; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; -} - -body { - font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; - line-height: 1.4em; - background: #f5f5f5; - color: #4d4d4d; - min-width: 230px; - max-width: 550px; - margin: 0 auto; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; - font-weight: 300; -} - -button, -input[type="checkbox"] { - outline: none; -} - -.hidden { - display: none; -} - -#todoapp { - background: #fff; - margin: 130px 0 40px 0; - position: relative; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), - 0 25px 50px 0 rgba(0, 0, 0, 0.1); -} - -#todoapp input::-webkit-input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp input::-moz-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp input::input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -#todoapp h1 { - position: absolute; - top: -155px; - width: 100%; - font-size: 100px; - font-weight: 100; - text-align: center; - color: rgba(175, 47, 47, 0.15); - -webkit-text-rendering: optimizeLegibility; - -moz-text-rendering: optimizeLegibility; - -ms-text-rendering: optimizeLegibility; - text-rendering: optimizeLegibility; -} - -#new-todo, -.edit { - position: relative; - margin: 0; - width: 100%; - font-size: 24px; - font-family: inherit; - font-weight: inherit; - line-height: 1.4em; - border: 0; - outline: none; - color: inherit; - padding: 6px; - border: 1px solid #999; - box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); - -ms-box-sizing: border-box; - box-sizing: border-box; - -webkit-font-smoothing: antialiased; - -moz-font-smoothing: antialiased; - -ms-font-smoothing: antialiased; - font-smoothing: antialiased; -} - -#new-todo { - padding: 16px 16px 16px 60px; - border: none; - background: rgba(0, 0, 0, 0.003); - box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); -} - -#main { - position: relative; - z-index: 2; - border-top: 1px solid #e6e6e6; -} - -label[for='toggle-all'] { - display: none; -} - -#toggle-all { - position: absolute; - top: -55px; - left: -12px; - width: 60px; - height: 34px; - text-align: center; - border: none; /* Mobile Safari */ -} - -#toggle-all:before { - content: '❯'; - font-size: 22px; - color: #e6e6e6; - padding: 10px 27px 10px 27px; -} - -#toggle-all:checked:before { - color: #737373; -} - -#todo-list { - margin: 0; - padding: 0; - list-style: none; -} - -#todo-list li { - position: relative; - font-size: 24px; - border-bottom: 1px solid #ededed; -} - -#todo-list li:last-child { - border-bottom: none; -} - -#todo-list li.editing { - border-bottom: none; - padding: 0; -} - -#todo-list li.editing .edit { - display: block; - width: 506px; - padding: 13px 17px 12px 17px; - margin: 0 0 0 43px; -} - -#todo-list li.editing .view { - display: none; -} - -#todo-list li .toggle { - text-align: center; - width: 40px; - /* auto, since non-WebKit browsers doesn't support input styling */ - height: auto; - position: absolute; - top: 0; - bottom: 0; - margin: auto 0; - border: none; /* Mobile Safari */ - -webkit-appearance: none; - -ms-appearance: none; - appearance: none; -} - -#todo-list li .toggle:after { - content: url('data:image/svg+xml;utf8,'); -} - -#todo-list li .toggle:checked:after { - content: url('data:image/svg+xml;utf8,'); -} - -#todo-list li label { - white-space: pre; - word-break: break-word; - padding: 15px 60px 15px 15px; - margin-left: 45px; - display: block; - line-height: 1.2; - transition: color 0.4s; -} - -#todo-list li.completed label { - color: #d9d9d9; - text-decoration: line-through; -} - -#todo-list li .destroy { - display: none; - position: absolute; - top: 0; - right: 10px; - bottom: 0; - width: 40px; - height: 40px; - margin: auto 0; - font-size: 30px; - color: #cc9a9a; - margin-bottom: 11px; - transition: color 0.2s ease-out; -} - -#todo-list li .destroy:hover { - color: #af5b5e; -} - -#todo-list li .destroy:after { - content: '×'; -} - -#todo-list li:hover .destroy { - display: block; -} - -#todo-list li .edit { - display: none; -} - -#todo-list li.editing:last-child { - margin-bottom: -1px; -} - -#footer { - color: #777; - padding: 10px 15px; - height: 20px; - text-align: center; - border-top: 1px solid #e6e6e6; -} - -#footer:before { - content: ''; - position: absolute; - right: 0; - bottom: 0; - left: 0; - height: 50px; - overflow: hidden; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), - 0 8px 0 -3px #f6f6f6, - 0 9px 1px -3px rgba(0, 0, 0, 0.2), - 0 16px 0 -6px #f6f6f6, - 0 17px 2px -6px rgba(0, 0, 0, 0.2); -} - -#todo-count { - float: left; - text-align: left; -} - -#todo-count strong { - font-weight: 300; -} - -#filters { - margin: 0; - padding: 0; - list-style: none; - position: absolute; - right: 0; - left: 0; -} - -#filters li { - display: inline; -} - -#filters li a { - color: inherit; - margin: 3px; - padding: 3px 7px; - text-decoration: none; - border: 1px solid transparent; - border-radius: 3px; -} - -#filters li a.selected, -#filters li a:hover { - border-color: rgba(175, 47, 47, 0.1); -} - -#filters li a.selected { - border-color: rgba(175, 47, 47, 0.2); -} - -#clear-completed, -html #clear-completed:active { - float: right; - position: relative; - line-height: 20px; - text-decoration: none; - cursor: pointer; - visibility: hidden; - position: relative; -} - -#clear-completed::after { - visibility: visible; - content: 'Clear completed'; - position: absolute; - right: 0; - white-space: nowrap; -} - -#clear-completed:hover::after { - text-decoration: underline; -} - -#info { - margin: 65px auto 0; - color: #bfbfbf; - font-size: 10px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - text-align: center; -} - -#info p { - line-height: 1; -} - -#info a { - color: inherit; - text-decoration: none; - font-weight: 400; -} - -#info a:hover { - text-decoration: underline; -} - -/* - Hack to remove background from Mobile Safari. - Can't use it globally since it destroys checkboxes in Firefox -*/ -@media screen and (-webkit-min-device-pixel-ratio:0) { - #toggle-all, - #todo-list li .toggle { - background: none; - } - - #todo-list li .toggle { - height: 40px; - } - - #toggle-all { - -webkit-transform: rotate(90deg); - transform: rotate(90deg); - -webkit-appearance: none; - appearance: none; - } -} - -@media (max-width: 430px) { - #footer { - height: 50px; - } - - #filters { - bottom: 10px; - } -} diff --git a/examples/vanillajs/node_modules/todomvc-common/base.css b/examples/vanillajs/node_modules/todomvc-common/base.css deleted file mode 100644 index da65968a73..0000000000 --- a/examples/vanillajs/node_modules/todomvc-common/base.css +++ /dev/null @@ -1,141 +0,0 @@ -hr { - margin: 20px 0; - border: 0; - border-top: 1px dashed #c5c5c5; - border-bottom: 1px dashed #f7f7f7; -} - -.learn a { - font-weight: normal; - text-decoration: none; - color: #b83f45; -} - -.learn a:hover { - text-decoration: underline; - color: #787e7e; -} - -.learn h3, -.learn h4, -.learn h5 { - margin: 10px 0; - font-weight: 500; - line-height: 1.2; - color: #000; -} - -.learn h3 { - font-size: 24px; -} - -.learn h4 { - font-size: 18px; -} - -.learn h5 { - margin-bottom: 0; - font-size: 14px; -} - -.learn ul { - padding: 0; - margin: 0 0 30px 25px; -} - -.learn li { - line-height: 20px; -} - -.learn p { - font-size: 15px; - font-weight: 300; - line-height: 1.3; - margin-top: 0; - margin-bottom: 0; -} - -#issue-count { - display: none; -} - -.quote { - border: none; - margin: 20px 0 60px 0; -} - -.quote p { - font-style: italic; -} - -.quote p:before { - content: '“'; - font-size: 50px; - opacity: .15; - position: absolute; - top: -20px; - left: 3px; -} - -.quote p:after { - content: '”'; - font-size: 50px; - opacity: .15; - position: absolute; - bottom: -42px; - right: 3px; -} - -.quote footer { - position: absolute; - bottom: -40px; - right: 0; -} - -.quote footer img { - border-radius: 3px; -} - -.quote footer a { - margin-left: 5px; - vertical-align: middle; -} - -.speech-bubble { - position: relative; - padding: 10px; - background: rgba(0, 0, 0, .04); - border-radius: 5px; -} - -.speech-bubble:after { - content: ''; - position: absolute; - top: 100%; - right: 30px; - border: 13px solid transparent; - border-top-color: rgba(0, 0, 0, .04); -} - -.learn-bar > .learn { - position: absolute; - width: 272px; - top: 8px; - left: -300px; - padding: 10px; - border-radius: 5px; - background-color: rgba(255, 255, 255, .6); - transition-property: left; - transition-duration: 500ms; -} - -@media (min-width: 899px) { - .learn-bar { - width: auto; - padding-left: 300px; - } - - .learn-bar > .learn { - left: 8px; - } -} diff --git a/examples/vanillajs/node_modules/todomvc-common/base.js b/examples/vanillajs/node_modules/todomvc-common/base.js deleted file mode 100644 index 44fb50c613..0000000000 --- a/examples/vanillajs/node_modules/todomvc-common/base.js +++ /dev/null @@ -1,244 +0,0 @@ -/* global _ */ -(function () { - 'use strict'; - - /* jshint ignore:start */ - // Underscore's Template Module - // Courtesy of underscorejs.org - var _ = (function (_) { - _.defaults = function (object) { - if (!object) { - return object; - } - for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { - var iterable = arguments[argsIndex]; - if (iterable) { - for (var key in iterable) { - if (object[key] == null) { - object[key] = iterable[key]; - } - } - } - } - return object; - } - - // By default, Underscore uses ERB-style template delimiters, change the - // following template settings to use alternative delimiters. - _.templateSettings = { - evaluate : /<%([\s\S]+?)%>/g, - interpolate : /<%=([\s\S]+?)%>/g, - escape : /<%-([\s\S]+?)%>/g - }; - - // When customizing `templateSettings`, if you don't want to define an - // interpolation, evaluation or escaping regex, we need one that is - // guaranteed not to match. - var noMatch = /(.)^/; - - // Certain characters need to be escaped so that they can be put into a - // string literal. - var escapes = { - "'": "'", - '\\': '\\', - '\r': 'r', - '\n': 'n', - '\t': 't', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; - - var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; - - // JavaScript micro-templating, similar to John Resig's implementation. - // Underscore templating handles arbitrary delimiters, preserves whitespace, - // and correctly escapes quotes within interpolated code. - _.template = function(text, data, settings) { - var render; - settings = _.defaults({}, settings, _.templateSettings); - - // Combine delimiters into one regular expression via alternation. - var matcher = new RegExp([ - (settings.escape || noMatch).source, - (settings.interpolate || noMatch).source, - (settings.evaluate || noMatch).source - ].join('|') + '|$', 'g'); - - // Compile the template source, escaping string literals appropriately. - var index = 0; - var source = "__p+='"; - text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { - source += text.slice(index, offset) - .replace(escaper, function(match) { return '\\' + escapes[match]; }); - - if (escape) { - source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; - } - if (interpolate) { - source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; - } - if (evaluate) { - source += "';\n" + evaluate + "\n__p+='"; - } - index = offset + match.length; - return match; - }); - source += "';\n"; - - // If a variable is not specified, place data values in local scope. - if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; - - source = "var __t,__p='',__j=Array.prototype.join," + - "print=function(){__p+=__j.call(arguments,'');};\n" + - source + "return __p;\n"; - - try { - render = new Function(settings.variable || 'obj', '_', source); - } catch (e) { - e.source = source; - throw e; - } - - if (data) return render(data, _); - var template = function(data) { - return render.call(this, data, _); - }; - - // Provide the compiled function source as a convenience for precompilation. - template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; - - return template; - }; - - return _; - })({}); - - if (location.hostname === 'todomvc.com') { - window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')); - } - /* jshint ignore:end */ - - function redirect() { - if (location.hostname === 'tastejs.github.io') { - location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com'); - } - } - - function findRoot() { - var base = location.href.indexOf('examples/'); - return location.href.substr(0, base); - } - - function getFile(file, callback) { - if (!location.host) { - return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.'); - } - - var xhr = new XMLHttpRequest(); - - xhr.open('GET', findRoot() + file, true); - xhr.send(); - - xhr.onload = function () { - if (xhr.status === 200 && callback) { - callback(xhr.responseText); - } - }; - } - - function Learn(learnJSON, config) { - if (!(this instanceof Learn)) { - return new Learn(learnJSON, config); - } - - var template, framework; - - if (typeof learnJSON !== 'object') { - try { - learnJSON = JSON.parse(learnJSON); - } catch (e) { - return; - } - } - - if (config) { - template = config.template; - framework = config.framework; - } - - if (!template && learnJSON.templates) { - template = learnJSON.templates.todomvc; - } - - if (!framework && document.querySelector('[data-framework]')) { - framework = document.querySelector('[data-framework]').dataset.framework; - } - - this.template = template; - - if (learnJSON.backend) { - this.frameworkJSON = learnJSON.backend; - this.frameworkJSON.issueLabel = framework; - this.append({ - backend: true - }); - } else if (learnJSON[framework]) { - this.frameworkJSON = learnJSON[framework]; - this.frameworkJSON.issueLabel = framework; - this.append(); - } - - this.fetchIssueCount(); - } - - Learn.prototype.append = function (opts) { - var aside = document.createElement('aside'); - aside.innerHTML = _.template(this.template, this.frameworkJSON); - aside.className = 'learn'; - - if (opts && opts.backend) { - // Remove demo link - var sourceLinks = aside.querySelector('.source-links'); - var heading = sourceLinks.firstElementChild; - var sourceLink = sourceLinks.lastElementChild; - // Correct link path - var href = sourceLink.getAttribute('href'); - sourceLink.setAttribute('href', href.substr(href.lastIndexOf('http'))); - sourceLinks.innerHTML = heading.outerHTML + sourceLink.outerHTML; - } else { - // Localize demo links - var demoLinks = aside.querySelectorAll('.demo-link'); - Array.prototype.forEach.call(demoLinks, function (demoLink) { - if (demoLink.getAttribute('href').substr(0, 4) !== 'http') { - demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href')); - } - }); - } - - document.body.className = (document.body.className + ' learn-bar').trim(); - document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); - }; - - Learn.prototype.fetchIssueCount = function () { - var issueLink = document.getElementById('issue-count-link'); - if (issueLink) { - var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos'); - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.onload = function (e) { - var parsedResponse = JSON.parse(e.target.responseText); - if (parsedResponse instanceof Array) { - var count = parsedResponse.length - if (count !== 0) { - issueLink.innerHTML = 'This app has ' + count + ' open issues'; - document.getElementById('issue-count').style.display = 'inline'; - } - } - }; - xhr.send(); - } - }; - - redirect(); - getFile('learn.json', Learn); -})(); diff --git a/examples/vanillajs/package.json b/examples/vanillajs/package.json deleted file mode 100644 index bc43530481..0000000000 --- a/examples/vanillajs/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "private": true, - "dependencies": { - "todomvc-common": "^1.0.1", - "todomvc-app-css": "^1.0.1" - }, - "devDependencies": { - "jasmine-core": "^0.3.0" - } -} diff --git a/examples/vanillajs/test/SpecRunner.html b/examples/vanillajs/test/SpecRunner.html index 19d7016df3..c33217041d 100644 --- a/examples/vanillajs/test/SpecRunner.html +++ b/examples/vanillajs/test/SpecRunner.html @@ -3,12 +3,12 @@ Jasmine Spec Runner - - - + + + - + + - +