diff --git a/examples/emberjs/.gitignore b/examples/emberjs/.gitignore new file mode 100644 index 0000000000..ca7c0c5f39 --- /dev/null +++ b/examples/emberjs/.gitignore @@ -0,0 +1,22 @@ +node_modules/.bin +node_modules/jquery +!node_modules/jquery/dist/jquery.js + +node_modules/components-ember +!node_modules/components-ember/ember.js + +node_modules/ember-data +!node_modules/ember-data.js + +node_modules/ember-localstorage-adapter +!node_modules/ember-localstorage-adapter/localstorage_adapter.js + +node_modules/handlebars +!node_modules/handlebars/dist/handlebars.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 diff --git a/examples/emberjs/bower.json b/examples/emberjs/bower.json deleted file mode 100644 index 59a7974a87..0000000000 --- a/examples/emberjs/bower.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "todomvc-emberjs", - "version": "0.0.0", - "dependencies": { - "todomvc-common": "~0.3.0", - "jquery": "~2.1.0", - "handlebars": "~1.3.0", - "ember": "~1.6.0", - "ember-data": "1.0.0-beta.7", - "ember-localstorage-adapter": "latest" - } -} diff --git a/examples/emberjs/bower_components/todomvc-common/base.css b/examples/emberjs/bower_components/todomvc-common/base.css deleted file mode 100644 index 285f531307..0000000000 --- a/examples/emberjs/bower_components/todomvc-common/base.css +++ /dev/null @@ -1,554 +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; - 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/emberjs/bower_components/todomvc-common/bg.png b/examples/emberjs/bower_components/todomvc-common/bg.png deleted file mode 100644 index b2a7600825..0000000000 Binary files a/examples/emberjs/bower_components/todomvc-common/bg.png and /dev/null differ diff --git a/examples/emberjs/index.html b/examples/emberjs/index.html index cce67d1e08..9954f717cb 100644 --- a/examples/emberjs/index.html +++ b/examples/emberjs/index.html @@ -3,7 +3,8 @@ ember.js • TodoMVC - + + - - - - - - + + + + + + diff --git a/examples/emberjs/bower_components/ember/ember.js b/examples/emberjs/node_modules/components-ember/ember.js similarity index 61% rename from examples/emberjs/bower_components/ember/ember.js rename to examples/emberjs/node_modules/components-ember/ember.js index 647ee01560..627610b21d 100644 --- a/examples/emberjs/bower_components/ember/ember.js +++ b/examples/emberjs/node_modules/components-ember/ember.js @@ -5,20280 +5,26903 @@ * Portions Copyright 2008-2011 Apple Inc. All rights reserved. * @license Licensed under MIT license * See https://raw.github.com/emberjs/ember.js/master/LICENSE - * @version 1.6.1 + * @version 1.10.0-beta.3 */ - (function() { -var define, requireModule, require, requirejs, Ember; +var enifed, requireModule, eriuqer, requirejs, Ember; (function() { Ember = this.Ember = this.Ember || {}; - if (typeof Ember === 'undefined') { Ember = {} }; + if (typeof Ember === 'undefined') { Ember = {}; }; + function UNDEFINED() { } if (typeof Ember.__loader === 'undefined') { var registry = {}, seen = {}; - define = function(name, deps, callback) { + enifed = function(name, deps, callback) { registry[name] = { deps: deps, callback: callback }; }; - requirejs = require = requireModule = function(name) { - if (seen.hasOwnProperty(name)) { return seen[name]; } + requirejs = eriuqer = requireModule = function(name) { + var s = seen[name]; + + if (s !== undefined) { return seen[name]; } + if (s === UNDEFINED) { return undefined; } + seen[name] = {}; if (!registry[name]) { - throw new Error("Could not find module " + name); + throw new Error('Could not find module ' + name); } - var mod = registry[name], - deps = mod.deps, - callback = mod.callback, - reified = [], - exports; + var mod = registry[name]; + var deps = mod.deps; + var callback = mod.callback; + var reified = []; + var exports; + var length = deps.length; - for (var i=0, l=deps.length; i\s*\(([^\)]+)\)/gm, '{anonymous}($1)').split('\n'); - stack.shift(); + if (onError) { + try { + return method.apply(target, args); + } catch(error) { + onError(error); + } finally { + if (!didFinally) { + didFinally = true; + this.end(); + } + } } else { - // Firefox - stack = error.stack.replace(/(?:\n@:0)?\s+$/m, ''). - replace(/^\(/gm, '{anonymous}(').split('\n'); + try { + return method.apply(target, args); + } finally { + if (!didFinally) { + didFinally = true; + this.end(); + } + } } + }, - stackStr = "\n " + stack.slice(2).join("\n "); - message = message + stackStr; - } + join: function(target, method /*, args */) { + if (this.currentInstance) { + if (!method) { + method = target; + target = null; + } - Logger.warn("DEPRECATION: "+message); - }; + if (isString(method)) { + method = target[method]; + } + return method.apply(target, slice.call(arguments, 2)); + } else { + return this.run.apply(this, arguments); + } + }, + defer: function(queueName, target, method /* , args */) { + if (!method) { + method = target; + target = null; + } - /** - Alias an old, deprecated method with its new counterpart. + if (isString(method)) { + method = target[method]; + } - Display a deprecation warning with the provided message and a stack trace - (Chrome and Firefox only) when the assigned method is called. + var stack = this.DEBUG ? new Error() : undefined; + var length = arguments.length; + var args; - Ember build tools will not remove calls to `Ember.deprecateFunc()`, though - no warnings will be shown in production. + if (length > 3) { + args = new Array(length - 3); + for (var i = 3; i < length; i++) { + args[i-3] = arguments[i]; + } + } else { + args = undefined; + } - ```javascript - Ember.oldMethod = Ember.deprecateFunc('Please use the new, updated method', Ember.newMethod); - ``` + if (!this.currentInstance) { createAutorun(this); } + return this.currentInstance.schedule(queueName, target, method, args, false, stack); + }, - @method deprecateFunc - @param {String} message A description of the deprecation. - @param {Function} func The new function called to replace its deprecated counterpart. - @return {Function} a new function that wrapped the original function with a deprecation warning - */ - Ember.deprecateFunc = function(message, func) { - return function() { - Ember.deprecate(message); - return func.apply(this, arguments); - }; - }; + deferOnce: function(queueName, target, method /* , args */) { + if (!method) { + method = target; + target = null; + } + if (isString(method)) { + method = target[method]; + } - /** - Run a function meant for debugging. Ember build tools will remove any calls to - `Ember.runInDebug()` when doing a production build. + var stack = this.DEBUG ? new Error() : undefined; + var length = arguments.length; + var args; - ```javascript - Ember.runInDebug(function() { - Ember.Handlebars.EachView.reopen({ - didInsertElement: function() { - console.log('I\'m happy'); + if (length > 3) { + args = new Array(length - 3); + for (var i = 3; i < length; i++) { + args[i-3] = arguments[i]; } - }); - }); - ``` + } else { + args = undefined; + } - @method runInDebug - @param {Function} func The function to be executed. - @since 1.5.0 - */ - Ember.runInDebug = function(func) { - func() - }; + if (!this.currentInstance) { + createAutorun(this); + } + return this.currentInstance.schedule(queueName, target, method, args, true, stack); + }, - // Inform the developer about the Ember Inspector if not installed. - if (!Ember.testing) { - var isFirefox = typeof InstallTrigger !== 'undefined'; - var isChrome = !!window.chrome && !window.opera; + setTimeout: function() { + var l = arguments.length; + var args = new Array(l); - if (typeof window !== 'undefined' && (isFirefox || isChrome) && window.addEventListener) { - window.addEventListener("load", function() { - if (document.documentElement && document.documentElement.dataset && !document.documentElement.dataset.emberExtension) { - var downloadURL; + for (var x = 0; x < l; x++) { + args[x] = arguments[x]; + } - if(isChrome) { - downloadURL = 'https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi'; - } else if(isFirefox) { - downloadURL = 'https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/'; - } + var length = args.length, + method, wait, target, + methodOrTarget, methodOrWait, methodOrArgs; - Ember.debug('For more advanced debugging, install the Ember Inspector from ' + downloadURL); - } - }, false); - } - } - }); -})(); + if (length === 0) { + return; + } else if (length === 1) { + method = args.shift(); + wait = 0; + } else if (length === 2) { + methodOrTarget = args[0]; + methodOrWait = args[1]; -(function() { -define("ember-metal/array", - ["exports"], - function(__exports__) { - "use strict"; - /*jshint newcap:false*/ - /** - @module ember-metal - */ + if (isFunction(methodOrWait) || isFunction(methodOrTarget[methodOrWait])) { + target = args.shift(); + method = args.shift(); + wait = 0; + } else if (isCoercableNumber(methodOrWait)) { + method = args.shift(); + wait = args.shift(); + } else { + method = args.shift(); + wait = 0; + } + } else { + var last = args[args.length - 1]; - var ArrayPrototype = Array.prototype; + if (isCoercableNumber(last)) { + wait = args.pop(); + } else { + wait = 0; + } - // NOTE: There is a bug in jshint that doesn't recognize `Object()` without `new` - // as being ok unless both `newcap:false` and not `use strict`. - // https://github.com/jshint/jshint/issues/392 + methodOrTarget = args[0]; + methodOrArgs = args[1]; - // Testing this is not ideal, but we want to use native functions - // if available, but not to use versions created by libraries like Prototype - var isNativeFunc = function(func) { - // This should probably work in all browsers likely to have ES5 array methods - return func && Function.prototype.toString.call(func).indexOf('[native code]') > -1; - }; + if (isFunction(methodOrArgs) || (isString(methodOrArgs) && + methodOrTarget !== null && + methodOrArgs in methodOrTarget)) { + target = args.shift(); + method = args.shift(); + } else { + method = args.shift(); + } + } - // From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/map - var map = isNativeFunc(ArrayPrototype.map) ? ArrayPrototype.map : function(fun /*, thisp */) { - //"use strict"; + var executeAt = now() + parseInt(wait, 10); - if (this === void 0 || this === null) { - throw new TypeError(); - } + if (isString(method)) { + method = target[method]; + } - var t = Object(this); - var len = t.length >>> 0; - if (typeof fun !== "function") { - throw new TypeError(); - } + var onError = getOnError(this.options); - var res = new Array(len); - var thisp = arguments[1]; - for (var i = 0; i < len; i++) { - if (i in t) { - res[i] = fun.call(thisp, t[i], i, t); + function fn() { + if (onError) { + try { + method.apply(target, args); + } catch (e) { + onError(e); + } + } else { + method.apply(target, args); + } } - } - return res; - }; + // find position to insert + var i = searchTimer(executeAt, this._timers); - // From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/foreach - var forEach = isNativeFunc(ArrayPrototype.forEach) ? ArrayPrototype.forEach : function(fun /*, thisp */) { - //"use strict"; + this._timers.splice(i, 0, executeAt, fn); - if (this === void 0 || this === null) { - throw new TypeError(); - } + updateLaterTimer(this, executeAt, wait); - var t = Object(this); - var len = t.length >>> 0; - if (typeof fun !== "function") { - throw new TypeError(); - } + return fn; + }, - var thisp = arguments[1]; - for (var i = 0; i < len; i++) { - if (i in t) { - fun.call(thisp, t[i], i, t); + throttle: function(target, method /* , args, wait, [immediate] */) { + var backburner = this; + var args = arguments; + var immediate = pop.call(args); + var wait, throttler, index, timer; + + if (isNumber(immediate) || isString(immediate)) { + wait = immediate; + immediate = true; + } else { + wait = pop.call(args); } - } - }; - var indexOf = isNativeFunc(ArrayPrototype.indexOf) ? ArrayPrototype.indexOf : function (obj, fromIndex) { - if (fromIndex === null || fromIndex === undefined) { fromIndex = 0; } - else if (fromIndex < 0) { fromIndex = Math.max(0, this.length + fromIndex); } - for (var i = fromIndex, j = this.length; i < j; i++) { - if (this[i] === obj) { return i; } - } - return -1; - }; + wait = parseInt(wait, 10); - var filter = isNativeFunc(ArrayPrototype.filter) ? ArrayPrototype.filter : function (fn, context) { - var i, - value, - result = [], - length = this.length; + index = findThrottler(target, method, this._throttlers); + if (index > -1) { return this._throttlers[index]; } // throttled - for (i = 0; i < length; i++) { - if (this.hasOwnProperty(i)) { - value = this[i]; - if (fn.call(context, value, i, this)) { - result.push(value); + timer = global.setTimeout(function() { + if (!immediate) { + backburner.run.apply(backburner, args); + } + var index = findThrottler(target, method, backburner._throttlers); + if (index > -1) { + backburner._throttlers.splice(index, 1); } + }, wait); + + if (immediate) { + this.run.apply(this, args); } - } - return result; - }; + throttler = [target, method, timer]; - if (Ember.SHIM_ES5) { - if (!ArrayPrototype.map) { - ArrayPrototype.map = map; - } + this._throttlers.push(throttler); - if (!ArrayPrototype.forEach) { - ArrayPrototype.forEach = forEach; - } + return throttler; + }, - if (!ArrayPrototype.filter) { - ArrayPrototype.filter = filter; - } + debounce: function(target, method /* , args, wait, [immediate] */) { + var backburner = this; + var args = arguments; + var immediate = pop.call(args); + var wait, index, debouncee, timer; - if (!ArrayPrototype.indexOf) { - ArrayPrototype.indexOf = indexOf; - } - } + if (isNumber(immediate) || isString(immediate)) { + wait = immediate; + immediate = false; + } else { + wait = pop.call(args); + } - /** - Array polyfills to support ES5 features in older browsers. + wait = parseInt(wait, 10); + // Remove debouncee + index = findDebouncee(target, method, this._debouncees); - @namespace Ember - @property ArrayPolyfills - */ - __exports__.map = map; - __exports__.forEach = forEach; - __exports__.filter = filter; - __exports__.indexOf = indexOf; - }); -define("ember-metal/binding", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/map","ember-metal/observer","ember-metal/run_loop","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // Ember.Logger, Ember.LOG_BINDINGS, assert - var get = __dependency2__.get; - var set = __dependency3__.set; - var trySet = __dependency3__.trySet; - var guidFor = __dependency4__.guidFor; - var Map = __dependency5__.Map; - var addObserver = __dependency6__.addObserver; - var removeObserver = __dependency6__.removeObserver; - var _suspendObserver = __dependency6__._suspendObserver; - var run = __dependency7__["default"]; + if (index > -1) { + debouncee = this._debouncees[index]; + this._debouncees.splice(index, 1); + clearTimeout(debouncee[2]); + } - // ES6TODO: where is Ember.lookup defined? - /** - @module ember-metal - */ + timer = global.setTimeout(function() { + if (!immediate) { + backburner.run.apply(backburner, args); + } + var index = findDebouncee(target, method, backburner._debouncees); + if (index > -1) { + backburner._debouncees.splice(index, 1); + } + }, wait); - // .......................................................... - // CONSTANTS - // + if (immediate && index === -1) { + backburner.run.apply(backburner, args); + } - /** - Debug parameter you can turn on. This will log all bindings that fire to - the console. This should be disabled in production code. Note that you - can also enable this from the console or temporarily. + debouncee = [ + target, + method, + timer + ]; - @property LOG_BINDINGS - @for Ember - @type Boolean - @default false - */ - Ember.LOG_BINDINGS = false || !!Ember.ENV.LOG_BINDINGS; + backburner._debouncees.push(debouncee); - var IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/; + return debouncee; + }, - /** - Returns true if the provided path is global (e.g., `MyApp.fooController.bar`) - instead of local (`foo.bar.baz`). + cancelTimers: function() { + var clearItems = function(item) { + clearTimeout(item[2]); + }; - @method isGlobalPath - @for Ember - @private - @param {String} path - @return Boolean - */ - function isGlobalPath(path) { - return IS_GLOBAL.test(path); - }; + each(this._throttlers, clearItems); + this._throttlers = []; - function getWithGlobals(obj, path) { - return get(isGlobalPath(path) ? Ember.lookup : obj, path); - } + each(this._debouncees, clearItems); + this._debouncees = []; - // .......................................................... - // BINDING - // + if (this._laterTimer) { + clearTimeout(this._laterTimer); + this._laterTimer = null; + } + this._timers = []; - var Binding = function(toPath, fromPath) { - this._direction = 'fwd'; - this._from = fromPath; - this._to = toPath; - this._directionMap = Map.create(); - }; + if (this._autorun) { + clearTimeout(this._autorun); + this._autorun = null; + } + }, - /** - @class Binding - @namespace Ember - */ + hasTimers: function() { + return !!this._timers.length || !!this._debouncees.length || !!this._throttlers.length || this._autorun; + }, - Binding.prototype = { - /** - This copies the Binding so it can be connected to another object. + cancel: function(timer) { + var timerType = typeof timer; - @method copy - @return {Ember.Binding} `this` - */ - copy: function () { - var copy = new Binding(this._to, this._from); - if (this._oneWay) { copy._oneWay = true; } - return copy; + if (timer && timerType === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce + return timer.queue.cancel(timer); + } else if (timerType === 'function') { // we're cancelling a setTimeout + for (var i = 0, l = this._timers.length; i < l; i += 2) { + if (this._timers[i + 1] === timer) { + this._timers.splice(i, 2); // remove the two elements + if (i === 0) { + if (this._laterTimer) { // Active timer? Then clear timer and reset for future timer + clearTimeout(this._laterTimer); + this._laterTimer = null; + } + if (this._timers.length > 0) { // Update to next available timer when available + updateLaterTimer(this, this._timers[0], this._timers[0] - now()); + } + } + return true; + } + } + } else if (Object.prototype.toString.call(timer) === "[object Array]"){ // we're cancelling a throttle or debounce + return this._cancelItem(findThrottler, this._throttlers, timer) || + this._cancelItem(findDebouncee, this._debouncees, timer); + } else { + return; // timer was null or not a timer + } }, - // .......................................................... - // CONFIG - // + _cancelItem: function(findMethod, array, timer){ + var item, index; - /** - This will set `from` property path to the specified value. It will not - attempt to resolve this property path to an actual object until you - connect the binding. + if (timer.length < 3) { return false; } - The binding will search for the property path starting at the root object - you pass when you `connect()` the binding. It follows the same rules as - `get()` - see that method for more information. + index = findMethod(timer[0], timer[1], array); - @method from - @param {String} path the property path to connect to - @return {Ember.Binding} `this` - */ - from: function(path) { - this._from = path; - return this; - }, + if (index > -1) { - /** - This will set the `to` property path to the specified value. It will not - attempt to resolve this property path to an actual object until you - connect the binding. + item = array[index]; - The binding will search for the property path starting at the root object - you pass when you `connect()` the binding. It follows the same rules as - `get()` - see that method for more information. + if (item[2] === timer[2]) { + array.splice(index, 1); + clearTimeout(timer[2]); + return true; + } + } - @method to - @param {String|Tuple} path A property path or tuple - @return {Ember.Binding} `this` - */ - to: function(path) { - this._to = path; - return this; - }, + return false; + } + }; - /** - Configures the binding as one way. A one-way binding will relay changes - on the `from` side to the `to` side, but not the other way around. This - means that if you change the `to` side directly, the `from` side may have - a different value. + Backburner.prototype.schedule = Backburner.prototype.defer; + Backburner.prototype.scheduleOnce = Backburner.prototype.deferOnce; + Backburner.prototype.later = Backburner.prototype.setTimeout; - @method oneWay - @return {Ember.Binding} `this` - */ - oneWay: function() { - this._oneWay = true; - return this; - }, + if (needsIETryCatchFix) { + var originalRun = Backburner.prototype.run; + Backburner.prototype.run = wrapInTryCatch(originalRun); - /** - @method toString - @return {String} string representation of binding - */ - toString: function() { - var oneWay = this._oneWay ? '[oneWay]' : ''; - return "Ember.Binding<" + guidFor(this) + ">(" + this._from + " -> " + this._to + ")" + oneWay; - }, + var originalEnd = Backburner.prototype.end; + Backburner.prototype.end = wrapInTryCatch(originalEnd); + } - // .......................................................... - // CONNECT AND SYNC - // + function getOnError(options) { + return options.onError || (options.onErrorTarget && options.onErrorTarget[options.onErrorMethod]); + } - /** - Attempts to connect this binding instance so that it can receive and relay - changes. This method will raise an exception if you have not set the - from/to properties yet. + function createAutorun(backburner) { + backburner.begin(); + backburner._autorun = global.setTimeout(function() { + backburner._autorun = null; + backburner.end(); + }); + } - @method connect - @param {Object} obj The root object for this binding. - @return {Ember.Binding} `this` - */ - connect: function(obj) { - Ember.assert('Must pass a valid object to Ember.Binding.connect()', !!obj); + function updateLaterTimer(backburner, executeAt, wait) { + var n = now(); + if (!backburner._laterTimer || executeAt < backburner._laterTimerExpiresAt || backburner._laterTimerExpiresAt < n) { - var fromPath = this._from, toPath = this._to; - trySet(obj, toPath, getWithGlobals(obj, fromPath)); + if (backburner._laterTimer) { + // Clear when: + // - Already expired + // - New timer is earlier + clearTimeout(backburner._laterTimer); - // add an observer on the object to be notified when the binding should be updated - addObserver(obj, fromPath, this, this.fromDidChange); + if (backburner._laterTimerExpiresAt < n) { // If timer was never triggered + // Calculate the left-over wait-time + wait = Math.max(0, executeAt - n); + } + } - // if the binding is a two-way binding, also set up an observer on the target - if (!this._oneWay) { addObserver(obj, toPath, this, this.toDidChange); } + backburner._laterTimer = global.setTimeout(function() { + backburner._laterTimer = null; + backburner._laterTimerExpiresAt = null; + executeTimers(backburner); + }, wait); - this._readyToSync = true; + backburner._laterTimerExpiresAt = n + wait; + } + } - return this; - }, + function executeTimers(backburner) { + var n = now(); + var fns, i, l; - /** - Disconnects the binding instance. Changes will no longer be relayed. You - will not usually need to call this method. + backburner.run(function() { + i = searchTimer(n, backburner._timers); - @method disconnect - @param {Object} obj The root object you passed when connecting the binding. - @return {Ember.Binding} `this` - */ - disconnect: function(obj) { - Ember.assert('Must pass a valid object to Ember.Binding.disconnect()', !!obj); + fns = backburner._timers.splice(0, i); - var twoWay = !this._oneWay; + for (i = 1, l = fns.length; i < l; i += 2) { + backburner.schedule(backburner.options.defaultQueue, null, fns[i]); + } + }); - // remove an observer on the object so we're no longer notified of - // changes that should update bindings. - removeObserver(obj, this._from, this, this.fromDidChange); + if (backburner._timers.length) { + updateLaterTimer(backburner, backburner._timers[0], backburner._timers[0] - n); + } + } - // if the binding is two-way, remove the observer from the target as well - if (twoWay) { removeObserver(obj, this._to, this, this.toDidChange); } + function findDebouncee(target, method, debouncees) { + return findItem(target, method, debouncees); + } - this._readyToSync = false; // disable scheduled syncs... - return this; - }, + function findThrottler(target, method, throttlers) { + return findItem(target, method, throttlers); + } - // .......................................................... - // PRIVATE - // + function findItem(target, method, collection) { + var item; + var index = -1; - /* called when the from side changes */ - fromDidChange: function(target) { - this._scheduleSync(target, 'fwd'); - }, + for (var i = 0, l = collection.length; i < l; i++) { + item = collection[i]; + if (item[0] === target && item[1] === method) { + index = i; + break; + } + } - /* called when the to side changes */ - toDidChange: function(target) { - this._scheduleSync(target, 'back'); - }, + return index; + } - _scheduleSync: function(obj, dir) { - var directionMap = this._directionMap; - var existingDir = directionMap.get(obj); + __exports__["default"] = Backburner; + }); +enifed("backburner.umd", + ["./backburner"], + function(__dependency1__) { + "use strict"; + var Backburner = __dependency1__["default"]; - // if we haven't scheduled the binding yet, schedule it - if (!existingDir) { - run.schedule('sync', this, this._sync, obj); - directionMap.set(obj, dir); - } + /* global define:true module:true window: true */ + if (typeof enifed === 'function' && enifed.amd) { + enifed(function() { return Backburner; }); + } else if (typeof module !== 'undefined' && module.exports) { + module.exports = Backburner; + } else if (typeof this !== 'undefined') { + this['Backburner'] = Backburner; + } + }); +enifed("backburner/binary-search", + ["exports"], + function(__exports__) { + "use strict"; + __exports__["default"] = function binarySearch(time, timers) { + var start = 0; + var end = timers.length - 2; + var middle, l; - // If both a 'back' and 'fwd' sync have been scheduled on the same object, - // default to a 'fwd' sync so that it remains deterministic. - if (existingDir === 'back' && dir === 'fwd') { - directionMap.set(obj, 'fwd'); + while (start < end) { + // since timers is an array of pairs 'l' will always + // be an integer + l = (end - start) / 2; + + // compensate for the index in case even number + // of pairs inside timers + middle = start + l - (l % 2); + + if (time >= timers[middle]) { + start = middle + 2; + } else { + end = middle; } - }, + } - _sync: function(obj) { - var log = Ember.LOG_BINDINGS; + return (time >= timers[start]) ? start + 2 : start; + } + }); +enifed("backburner/deferred-action-queues", + ["./utils","./queue","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var each = __dependency1__.each; + var Queue = __dependency2__["default"]; - // don't synchronize destroyed objects or disconnected bindings - if (obj.isDestroyed || !this._readyToSync) { return; } + function DeferredActionQueues(queueNames, options) { + var queues = this.queues = Object.create(null); + this.queueNames = queueNames = queueNames || []; - // get the direction of the binding for the object we are - // synchronizing from - var directionMap = this._directionMap; - var direction = directionMap.get(obj); + this.options = options; + + each(queueNames, function(queueName) { + queues[queueName] = new Queue(queueName, options[queueName], options); + }); + } - var fromPath = this._from, toPath = this._to; + function noSuchQueue(name) { + throw new Error("You attempted to schedule an action in a queue (" + name + ") that doesn't exist"); + } - directionMap.remove(obj); + DeferredActionQueues.prototype = { + schedule: function(name, target, method, args, onceFlag, stack) { + var queues = this.queues; + var queue = queues[name]; - // if we're synchronizing from the remote object... - if (direction === 'fwd') { - var fromValue = getWithGlobals(obj, this._from); - if (log) { - Ember.Logger.log(' ', this.toString(), '->', fromValue, obj); - } - if (this._oneWay) { - trySet(obj, toPath, fromValue); + if (!queue) { + noSuchQueue(name); + } + + if (onceFlag) { + return queue.pushUnique(target, method, args, stack); + } else { + return queue.push(target, method, args, stack); + } + }, + + flush: function() { + var queues = this.queues; + var queueNames = this.queueNames; + var queueName, queue, queueItems, priorQueueNameIndex; + var queueNameIndex = 0; + var numberOfQueues = queueNames.length; + var options = this.options; + + while (queueNameIndex < numberOfQueues) { + queueName = queueNames[queueNameIndex]; + queue = queues[queueName]; + + var numberOfQueueItems = queue._queue.length; + + if (numberOfQueueItems === 0) { + queueNameIndex++; } else { - _suspendObserver(obj, toPath, this, this.toDidChange, function () { - trySet(obj, toPath, fromValue); - }); + queue.flush(false /* async */); + queueNameIndex = 0; } - // if we're synchronizing *to* the remote object - } else if (direction === 'back') { - var toValue = get(obj, this._to); - if (log) { - Ember.Logger.log(' ', this.toString(), '<-', toValue, obj); - } - _suspendObserver(obj, fromPath, this, this.fromDidChange, function () { - trySet(isGlobalPath(fromPath) ? Ember.lookup : obj, fromPath, toValue); - }); } } - }; - function mixinProperties(to, from) { - for (var key in from) { - if (from.hasOwnProperty(key)) { - to[key] = from[key]; - } - } - } + __exports__["default"] = DeferredActionQueues; + }); +enifed("backburner/platform", + ["exports"], + function(__exports__) { + "use strict"; + // In IE 6-8, try/finally doesn't work without a catch. + // Unfortunately, this is impossible to test for since wrapping it in a parent try/catch doesn't trigger the bug. + // This tests for another broken try/catch behavior that only exhibits in the same versions of IE. + var needsIETryCatchFix = (function(e,x){ + try{ x(); } + catch(e) { } // jshint ignore:line + return !!e; + })(); + __exports__.needsIETryCatchFix = needsIETryCatchFix; + }); +enifed("backburner/queue", + ["./utils","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var isString = __dependency1__.isString; - mixinProperties(Binding, { + function Queue(name, options, globalOptions) { + this.name = name; + this.globalOptions = globalOptions || {}; + this.options = options; + this._queue = []; + this.targetQueues = Object.create(null); + this._queueBeingFlushed = undefined; + } - /* - See `Ember.Binding.from`. + Queue.prototype = { + push: function(target, method, args, stack) { + var queue = this._queue; + queue.push(target, method, args, stack); - @method from - @static - */ - from: function() { - var C = this, binding = new C(); - return binding.from.apply(binding, arguments); + return { + queue: this, + target: target, + method: method + }; }, - /* - See `Ember.Binding.to`. + pushUniqueWithoutGuid: function(target, method, args, stack) { + var queue = this._queue; - @method to - @static - */ - to: function() { - var C = this, binding = new C(); - return binding.to.apply(binding, arguments); - }, + for (var i = 0, l = queue.length; i < l; i += 4) { + var currentTarget = queue[i]; + var currentMethod = queue[i+1]; - /** - Creates a new Binding instance and makes it apply in a single direction. - A one-way binding will relay changes on the `from` side object (supplied - as the `from` argument) the `to` side, but not the other way around. - This means that if you change the "to" side directly, the "from" side may have - a different value. + if (currentTarget === target && currentMethod === method) { + queue[i+2] = args; // replace args + queue[i+3] = stack; // replace stack + return; + } + } - See `Binding.oneWay`. + queue.push(target, method, args, stack); + }, - @method oneWay - @param {String} from from path. - @param {Boolean} [flag] (Optional) passing nothing here will make the - binding `oneWay`. You can instead pass `false` to disable `oneWay`, making the - binding two way again. - @return {Ember.Binding} `this` - */ - oneWay: function(from, flag) { - var C = this, binding = new C(null, from); - return binding.oneWay(flag); - } + targetQueue: function(targetQueue, target, method, args, stack) { + var queue = this._queue; - }); + for (var i = 0, l = targetQueue.length; i < l; i += 4) { + var currentMethod = targetQueue[i]; + var currentIndex = targetQueue[i + 1]; - /** - An `Ember.Binding` connects the properties of two objects so that whenever - the value of one property changes, the other property will be changed also. + if (currentMethod === method) { + queue[currentIndex + 2] = args; // replace args + queue[currentIndex + 3] = stack; // replace stack + return; + } + } - ## Automatic Creation of Bindings with `/^*Binding/`-named Properties + targetQueue.push( + method, + queue.push(target, method, args, stack) - 4 + ); + }, - You do not usually create Binding objects directly but instead describe - bindings in your class or object definition using automatic binding - detection. + pushUniqueWithGuid: function(guid, target, method, args, stack) { + var hasLocalQueue = this.targetQueues[guid]; - Properties ending in a `Binding` suffix will be converted to `Ember.Binding` - instances. The value of this property should be a string representing a path - to another object or a custom binding instanced created using Binding helpers - (see "One Way Bindings"): + if (hasLocalQueue) { + this.targetQueue(hasLocalQueue, target, method, args, stack); + } else { + this.targetQueues[guid] = [ + method, + this._queue.push(target, method, args, stack) - 4 + ]; + } - ``` - valueBinding: "MyApp.someController.title" - ``` + return { + queue: this, + target: target, + method: method + }; + }, - This will create a binding from `MyApp.someController.title` to the `value` - property of your object instance automatically. Now the two values will be - kept in sync. + pushUnique: function(target, method, args, stack) { + var queue = this._queue, currentTarget, currentMethod, i, l; + var KEY = this.globalOptions.GUID_KEY; - ## One Way Bindings + if (target && KEY) { + var guid = target[KEY]; + if (guid) { + return this.pushUniqueWithGuid(guid, target, method, args, stack); + } + } - One especially useful binding customization you can use is the `oneWay()` - helper. This helper tells Ember that you are only interested in - receiving changes on the object you are binding from. For example, if you - are binding to a preference and you want to be notified if the preference - has changed, but your object will not be changing the preference itself, you - could do: + this.pushUniqueWithoutGuid(target, method, args, stack); - ``` - bigTitlesBinding: Ember.Binding.oneWay("MyApp.preferencesController.bigTitles") - ``` + return { + queue: this, + target: target, + method: method + }; + }, - This way if the value of `MyApp.preferencesController.bigTitles` changes the - `bigTitles` property of your object will change also. However, if you - change the value of your `bigTitles` property, it will not update the - `preferencesController`. + invoke: function(target, method, args, _, _errorRecordedForStack) { + if (args && args.length > 0) { + method.apply(target, args); + } else { + method.call(target); + } + }, - One way bindings are almost twice as fast to setup and twice as fast to - execute because the binding only has to worry about changes to one side. + invokeWithOnError: function(target, method, args, onError, errorRecordedForStack) { + try { + if (args && args.length > 0) { + method.apply(target, args); + } else { + method.call(target); + } + } catch(error) { + onError(error, errorRecordedForStack); + } + }, - You should consider using one way bindings anytime you have an object that - may be created frequently and you do not intend to change a property; only - to monitor it for changes (such as in the example above). + flush: function(sync) { + var queue = this._queue; + var length = queue.length; - ## Adding Bindings Manually + if (length === 0) { + return; + } - All of the examples above show you how to configure a custom binding, but the - result of these customizations will be a binding template, not a fully active - Binding instance. The binding will actually become active only when you - instantiate the object the binding belongs to. It is useful however, to - understand what actually happens when the binding is activated. + var globalOptions = this.globalOptions; + var options = this.options; + var before = options && options.before; + var after = options && options.after; + var onError = globalOptions.onError || (globalOptions.onErrorTarget && + globalOptions.onErrorTarget[globalOptions.onErrorMethod]); + var target, method, args, errorRecordedForStack; + var invoke = onError ? this.invokeWithOnError : this.invoke; - For a binding to function it must have at least a `from` property and a `to` - property. The `from` property path points to the object/key that you want to - bind from while the `to` path points to the object/key you want to bind to. + this.targetQueues = Object.create(null); + var queueItems = this._queueBeingFlushed = this._queue.slice(); + this._queue = []; - When you define a custom binding, you are usually describing the property - you want to bind from (such as `MyApp.someController.value` in the examples - above). When your object is created, it will automatically assign the value - you want to bind `to` based on the name of your binding key. In the - examples above, during init, Ember objects will effectively call - something like this on your binding: + if (before) { + before(); + } - ```javascript - binding = Ember.Binding.from(this.valueBinding).to("value"); - ``` + for (var i = 0; i < length; i += 4) { + target = queueItems[i]; + method = queueItems[i+1]; + args = queueItems[i+2]; + errorRecordedForStack = queueItems[i+3]; // Debugging assistance - This creates a new binding instance based on the template you provide, and - sets the to path to the `value` property of the new object. Now that the - binding is fully configured with a `from` and a `to`, it simply needs to be - connected to become active. This is done through the `connect()` method: + if (isString(method)) { + method = target[method]; + } - ```javascript - binding.connect(this); - ``` + // method could have been nullified / canceled during flush + if (method) { + // + // ** Attention intrepid developer ** + // + // To find out the stack of this task when it was scheduled onto + // the run loop, add the following to your app.js: + // + // Ember.run.backburner.DEBUG = true; // NOTE: This slows your app, don't leave it on in production. + // + // Once that is in place, when you are at a breakpoint and navigate + // here in the stack explorer, you can look at `errorRecordedForStack.stack`, + // which will be the captured stack when this job was scheduled. + // + invoke(target, method, args, onError, errorRecordedForStack); + } + } - Note that when you connect a binding you pass the object you want it to be - connected to. This object will be used as the root for both the from and - to side of the binding when inspecting relative paths. This allows the - binding to be automatically inherited by subclassed objects as well. + if (after) { + after(); + } - Now that the binding is connected, it will observe both the from and to side - and relay changes. + this._queueBeingFlushed = undefined; - If you ever needed to do so (you almost never will, but it is useful to - understand this anyway), you could manually create an active binding by - using the `Ember.bind()` helper method. (This is the same method used by - to setup your bindings on objects): + if (sync !== false && + this._queue.length > 0) { + // check if new items have been added + this.flush(true); + } + }, - ```javascript - Ember.bind(MyApp.anotherObject, "value", "MyApp.someController.value"); - ``` + cancel: function(actionToCancel) { + var queue = this._queue, currentTarget, currentMethod, i, l; + var target = actionToCancel.target; + var method = actionToCancel.method; + var GUID_KEY = this.globalOptions.GUID_KEY; - Both of these code fragments have the same effect as doing the most friendly - form of binding creation like so: + if (GUID_KEY && this.targetQueues && target) { + var targetQueue = this.targetQueues[target[GUID_KEY]]; - ```javascript - MyApp.anotherObject = Ember.Object.create({ - valueBinding: "MyApp.someController.value", + if (targetQueue) { + for (i = 0, l = targetQueue.length; i < l; i++) { + if (targetQueue[i] === method) { + targetQueue.splice(i, 1); + } + } + } + } - // OTHER CODE FOR THIS OBJECT... - }); - ``` + for (i = 0, l = queue.length; i < l; i += 4) { + currentTarget = queue[i]; + currentMethod = queue[i+1]; - Ember's built in binding creation method makes it easy to automatically - create bindings for you. You should always use the highest-level APIs - available, even if you understand how it works underneath. + if (currentTarget === target && + currentMethod === method) { + queue.splice(i, 4); + return true; + } + } - @class Binding - @namespace Ember - @since Ember 0.9 - */ - // Ember.Binding = Binding; ES6TODO: where to put this? + // if not found in current queue + // could be in the queue that is being flushed + queue = this._queueBeingFlushed; + if (!queue) { + return; + } - /** - Global helper method to create a new binding. Just pass the root object - along with a `to` and `from` path to create and connect the binding. + for (i = 0, l = queue.length; i < l; i += 4) { + currentTarget = queue[i]; + currentMethod = queue[i+1]; - @method bind - @for Ember - @param {Object} obj The root object of the transform. - @param {String} to The path to the 'to' side of the binding. - Must be relative to obj. - @param {String} from The path to the 'from' side of the binding. - Must be relative to obj or a global path. - @return {Ember.Binding} binding instance - */ - function bind(obj, to, from) { - return new Binding(to, from).connect(obj); - }; - - /** - @method oneWay - @for Ember - @param {Object} obj The root object of the transform. - @param {String} to The path to the 'to' side of the binding. - Must be relative to obj. - @param {String} from The path to the 'from' side of the binding. - Must be relative to obj or a global path. - @return {Ember.Binding} binding instance - */ - function oneWay(obj, to, from) { - return new Binding(to, from).oneWay().connect(obj); + if (currentTarget === target && + currentMethod === method) { + // don't mess with array during flush + // just nullify the method + queue[i+1] = null; + return true; + } + } + } }; - __exports__.Binding = Binding; - __exports__.bind = bind; - __exports__.oneWay = oneWay; - __exports__.isGlobalPath = isGlobalPath; + __exports__["default"] = Queue; }); -define("ember-metal/chains", - ["ember-metal/core","ember-metal/property_get","ember-metal/utils","ember-metal/array","ember-metal/watch_key","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { +enifed("backburner/utils", + ["exports"], + function(__exports__) { "use strict"; - var Ember = __dependency1__["default"]; - // warn, assert, etc; - var get = __dependency2__.get; - var normalizeTuple = __dependency2__.normalizeTuple; - var meta = __dependency3__.meta; - var META_KEY = __dependency3__.META_KEY; - var forEach = __dependency4__.forEach; - var watchKey = __dependency5__.watchKey; - var unwatchKey = __dependency5__.unwatchKey; + var NUMBER = /\d+/; - var metaFor = meta, - warn = Ember.warn, - FIRST_KEY = /^([^\.]+)/; + function each(collection, callback) { + for (var i = 0; i < collection.length; i++) { + callback(collection[i]); + } + } - function firstKey(path) { - return path.match(FIRST_KEY)[0]; + __exports__.each = each;// Date.now is not available in browsers < IE9 + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now#Compatibility + var now = Date.now || function() { return new Date().getTime(); }; + __exports__.now = now; + function isString(suspect) { + return typeof suspect === 'string'; } - var pendingQueue = []; + __exports__.isString = isString;function isFunction(suspect) { + return typeof suspect === 'function'; + } - // attempts to add the pendingQueue chains again. If some of them end up - // back in the queue and reschedule is true, schedules a timeout to try - // again. - function flushPendingChains() { - if (pendingQueue.length === 0) { return; } // nothing to do + __exports__.isFunction = isFunction;function isNumber(suspect) { + return typeof suspect === 'number'; + } - var queue = pendingQueue; - pendingQueue = []; + __exports__.isNumber = isNumber;function isCoercableNumber(number) { + return isNumber(number) || NUMBER.test(number); + } - forEach.call(queue, function(q) { q[0].add(q[1]); }); + __exports__.isCoercableNumber = isCoercableNumber;function wrapInTryCatch(func) { + return function () { + try { + return func.apply(this, arguments); + } catch (e) { + throw e; + } + }; + } - warn('Watching an undefined global, Ember expects watched globals to be setup by the time the run loop is flushed, check for typos', pendingQueue.length === 0); - }; + __exports__.wrapInTryCatch = wrapInTryCatch; + }); +enifed("calculateVersion", + [], + function() { + "use strict"; + 'use strict'; + var fs = eriuqer('fs'); + var path = eriuqer('path'); - function addChainWatcher(obj, keyName, node) { - if (!obj || ('object' !== typeof obj)) { return; } // nothing to do + module.exports = function () { + var packageVersion = eriuqer('../package.json').version; + var output = [packageVersion]; + var gitPath = path.join(__dirname,'..','.git'); + var headFilePath = path.join(gitPath, 'HEAD'); - var m = metaFor(obj), nodes = m.chainWatchers; + if (packageVersion.indexOf('+') > -1) { + try { + if (fs.existsSync(headFilePath)) { + var headFile = fs.readFileSync(headFilePath, {encoding: 'utf8'}); + var branchName = headFile.split('/').slice(-1)[0].trim(); + var refPath = headFile.split(' ')[1]; + var branchSHA; + + if (refPath) { + var branchPath = path.join(gitPath, refPath.trim()); + branchSHA = fs.readFileSync(branchPath); + } else { + branchSHA = branchName; + } - if (!m.hasOwnProperty('chainWatchers')) { - nodes = m.chainWatchers = {}; + output.push(branchSHA.slice(0,10)); + } + } catch (err) { + console.error(err.stack); + } + return output.join('.'); + } else { + return packageVersion; } + }; + }); +enifed("container", + ["container/container","exports"], + function(__dependency1__, __exports__) { + "use strict"; + /* + Public api for the container is still in flux. + The public api, specified on the application namespace should be considered the stable api. + // @module container + @private + */ - if (!nodes[keyName]) { nodes[keyName] = []; } - nodes[keyName].push(node); - watchKey(obj, keyName, m); - } + /* + Flag to enable/disable model factory injections (disabled by default) + If model factory injections are enabled, models should not be + accessed globally (only through `container.lookupFactory('model:modelName'))`); + */ + Ember.MODEL_FACTORY_INJECTIONS = false; - function removeChainWatcher(obj, keyName, node) { - if (!obj || 'object' !== typeof obj) { return; } // nothing to do + if (Ember.ENV && typeof Ember.ENV.MODEL_FACTORY_INJECTIONS !== 'undefined') { + Ember.MODEL_FACTORY_INJECTIONS = !!Ember.ENV.MODEL_FACTORY_INJECTIONS; + } - var m = obj[META_KEY]; - if (m && !m.hasOwnProperty('chainWatchers')) { return; } // nothing to do - var nodes = m && m.chainWatchers; + var Container = __dependency1__["default"]; - if (nodes && nodes[keyName]) { - nodes = nodes[keyName]; - for (var i = 0, l = nodes.length; i < l; i++) { - if (nodes[i] === node) { nodes.splice(i, 1); } - } - } - unwatchKey(obj, keyName, m); - }; + __exports__["default"] = Container; + }); +enifed("container/container", + ["ember-metal/core","ember-metal/keys","ember-metal/dictionary","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // Ember.assert + var emberKeys = __dependency2__["default"]; + var dictionary = __dependency3__["default"]; - // A ChainNode watches a single key on an object. If you provide a starting - // value for the key then the node won't actually watch it. For a root node - // pass null for parent and key and object for value. - function ChainNode(parent, key, value) { - this._parent = parent; - this._key = key; + // A lightweight container that helps to assemble and decouple components. + // Public api for the container is still in flux. + // The public api, specified on the application namespace should be considered the stable api. + function Container(parent) { + this.parent = parent; + this.children = []; - // _watching is true when calling get(this._parent, this._key) will - // return the value of this node. - // - // It is false for the root of a chain (because we have no parent) - // and for global paths (because the parent node is the object with - // the observer on it) - this._watching = value===undefined; + this.resolver = parent && parent.resolver || function() {}; - this._value = value; - this._paths = {}; - if (this._watching) { - this._object = parent.value(); - if (this._object) { addChainWatcher(this._object, this._key, this); } - } + this.registry = dictionary(parent ? parent.registry : null); + this.cache = dictionary(parent ? parent.cache : null); + this.factoryCache = dictionary(parent ? parent.factoryCache : null); + this.resolveCache = dictionary(parent ? parent.resolveCache : null); + this.typeInjections = dictionary(parent ? parent.typeInjections : null); + this.injections = dictionary(null); + this.normalizeCache = dictionary(null); + + this.validationCache = dictionary(parent ? parent.validationCache : null); + - // Special-case: the EachProxy relies on immediate evaluation to - // establish its observers. - // - // TODO: Replace this with an efficient callback that the EachProxy - // can implement. - if (this._parent && this._parent._key === '@each') { - this.value(); - } - }; + this.factoryTypeInjections = dictionary(parent ? parent.factoryTypeInjections : null); + this.factoryInjections = dictionary(null); - var ChainNodePrototype = ChainNode.prototype; + this._options = dictionary(parent ? parent._options : null); + this._typeOptions = dictionary(parent ? parent._typeOptions : null); + } - function lazyGet(obj, key) { - if (!obj) return undefined; + Container.prototype = { - var meta = obj[META_KEY]; - // check if object meant only to be a prototype - if (meta && meta.proto === obj) return undefined; + /** + @property parent + @type Container + @default null + */ + parent: null, - if (key === "@each") return get(obj, key); + /** + @property children + @type Array + @default [] + */ + children: null, - // if a CP only return cached value - var desc = meta && meta.descs[key]; - if (desc && desc._cacheable) { - if (key in meta.cache) { - return meta.cache[key]; - } else { - return undefined; - } - } + /** + @property resolver + @type function + */ + resolver: null, - return get(obj, key); - } + /** + @property registry + @type InheritingDict + */ + registry: null, - ChainNodePrototype.value = function() { - if (this._value === undefined && this._watching) { - var obj = this._parent.value(); - this._value = lazyGet(obj, this._key); - } - return this._value; - }; + /** + @property cache + @type InheritingDict + */ + cache: null, - ChainNodePrototype.destroy = function() { - if (this._watching) { - var obj = this._object; - if (obj) { removeChainWatcher(obj, this._key, this); } - this._watching = false; // so future calls do nothing - } - }; + /** + @property typeInjections + @type InheritingDict + */ + typeInjections: null, - // copies a top level object only - ChainNodePrototype.copy = function(obj) { - var ret = new ChainNode(null, null, obj), - paths = this._paths, path; - for (path in paths) { - if (paths[path] <= 0) { continue; } // this check will also catch non-number vals. - ret.add(path); - } - return ret; - }; + /** + @property injections + @type Object + @default {} + */ + injections: null, - // called on the root node of a chain to setup watchers on the specified - // path. - ChainNodePrototype.add = function(path) { - var obj, tuple, key, src, paths; + /** + @private - paths = this._paths; - paths[path] = (paths[path] || 0) + 1; + @property _options + @type InheritingDict + @default null + */ + _options: null, - obj = this.value(); - tuple = normalizeTuple(obj, path); + /** + @private - // the path was a local path - if (tuple[0] && tuple[0] === obj) { - path = tuple[1]; - key = firstKey(path); - path = path.slice(key.length+1); + @property _typeOptions + @type InheritingDict + */ + _typeOptions: null, - // global path, but object does not exist yet. - // put into a queue and try to connect later. - } else if (!tuple[0]) { - pendingQueue.push([this, path]); - tuple.length = 0; - return; + /** + Returns a new child of the current container. These children are configured + to correctly inherit from the current container. - // global path, and object already exists - } else { - src = tuple[0]; - key = path.slice(0, 0-(tuple[1].length+1)); - path = tuple[1]; - } + @method child + @return {Container} + */ + child: function() { + var container = new Container(this); + this.children.push(container); + return container; + }, - tuple.length = 0; - this.chain(key, path, src); - }; + /** + Registers a factory for later injection. - // called on the root node of a chain to teardown watcher on the specified - // path - ChainNodePrototype.remove = function(path) { - var obj, tuple, key, src, paths; + Example: - paths = this._paths; - if (paths[path] > 0) { paths[path]--; } + ```javascript + var container = new Container(); - obj = this.value(); - tuple = normalizeTuple(obj, path); - if (tuple[0] === obj) { - path = tuple[1]; - key = firstKey(path); - path = path.slice(key.length+1); - } else { - src = tuple[0]; - key = path.slice(0, 0-(tuple[1].length+1)); - path = tuple[1]; - } + container.register('model:user', Person, {singleton: false }); + container.register('fruit:favorite', Orange); + container.register('communication:main', Email, {singleton: false}); + ``` - tuple.length = 0; - this.unchain(key, path); - }; + @method register + @param {String} fullName + @param {Function} factory + @param {Object} options + */ + register: function(fullName, factory, options) { + Ember.assert('fullName must be a proper full name', validateFullName(fullName)); - ChainNodePrototype.count = 0; + if (factory === undefined) { + throw new TypeError('Attempting to register an unknown factory: `' + fullName + '`'); + } - ChainNodePrototype.chain = function(key, path, src) { - var chains = this._chains, node; - if (!chains) { chains = this._chains = {}; } + var normalizedName = this.normalize(fullName); - node = chains[key]; - if (!node) { node = chains[key] = new ChainNode(this, key, src); } - node.count++; // count chains... + if (normalizedName in this.cache) { + throw new Error('Cannot re-register: `' + fullName +'`, as it has already been looked up.'); + } - // chain rest of path if there is one - if (path && path.length>0) { - key = firstKey(path); - path = path.slice(key.length+1); - node.chain(key, path); // NOTE: no src means it will observe changes... - } - }; + this.registry[normalizedName] = factory; + this._options[normalizedName] = (options || {}); + }, - ChainNodePrototype.unchain = function(key, path) { - var chains = this._chains, node = chains[key]; + /** + Unregister a fullName - // unchain rest of path first... - if (path && path.length>1) { - key = firstKey(path); - path = path.slice(key.length+1); - node.unchain(key, path); - } + ```javascript + var container = new Container(); + container.register('model:user', User); - // delete node if needed. - node.count--; - if (node.count<=0) { - delete chains[node._key]; - node.destroy(); - } + container.lookup('model:user') instanceof User //=> true - }; + container.unregister('model:user') + container.lookup('model:user') === undefined //=> true + ``` - ChainNodePrototype.willChange = function(events) { - var chains = this._chains; - if (chains) { - for(var key in chains) { - if (!chains.hasOwnProperty(key)) { continue; } - chains[key].willChange(events); - } - } + @method unregister + @param {String} fullName + */ + unregister: function(fullName) { + Ember.assert('fullName must be a proper full name', validateFullName(fullName)); - if (this._parent) { this._parent.chainWillChange(this, this._key, 1, events); } - }; + var normalizedName = this.normalize(fullName); - ChainNodePrototype.chainWillChange = function(chain, path, depth, events) { - if (this._key) { path = this._key + '.' + path; } + delete this.registry[normalizedName]; + delete this.cache[normalizedName]; + delete this.factoryCache[normalizedName]; + delete this.resolveCache[normalizedName]; + delete this._options[normalizedName]; + + delete this.validationCache[normalizedName]; + + }, - if (this._parent) { - this._parent.chainWillChange(this, path, depth+1, events); - } else { - if (depth > 1) { - events.push(this.value(), path); - } - path = 'this.' + path; - if (this._paths[path] > 0) { - events.push(this.value(), path); - } - } - }; + /** + Given a fullName return the corresponding factory. - ChainNodePrototype.chainDidChange = function(chain, path, depth, events) { - if (this._key) { path = this._key + '.' + path; } - if (this._parent) { - this._parent.chainDidChange(this, path, depth+1, events); - } else { - if (depth > 1) { - events.push(this.value(), path); - } - path = 'this.' + path; - if (this._paths[path] > 0) { - events.push(this.value(), path); - } - } - }; + By default `resolve` will retrieve the factory from + its container's registry. - ChainNodePrototype.didChange = function(events) { - // invalidate my own value first. - if (this._watching) { - var obj = this._parent.value(); - if (obj !== this._object) { - removeChainWatcher(this._object, this._key, this); - this._object = obj; - addChainWatcher(obj, this._key, this); - } - this._value = undefined; + ```javascript + var container = new Container(); + container.register('api:twitter', Twitter); - // Special-case: the EachProxy relies on immediate evaluation to - // establish its observers. - if (this._parent && this._parent._key === '@each') - this.value(); - } + container.resolve('api:twitter') // => Twitter + ``` - // then notify chains... - var chains = this._chains; - if (chains) { - for(var key in chains) { - if (!chains.hasOwnProperty(key)) { continue; } - chains[key].didChange(events); - } - } + Optionally the container can be provided with a custom resolver. + If provided, `resolve` will first provide the custom resolver + the opportunity to resolve the fullName, otherwise it will fallback + to the registry. - // if no events are passed in then we only care about the above wiring update - if (events === null) { return; } + ```javascript + var container = new Container(); + container.resolver = function(fullName) { + // lookup via the module system of choice + }; - // and finally tell parent about my path changing... - if (this._parent) { this._parent.chainDidChange(this, this._key, 1, events); } - }; + // the twitter factory is added to the module system + container.resolve('api:twitter') // => Twitter + ``` - function finishChains(obj) { - // We only create meta if we really have to - var m = obj[META_KEY], chains = m && m.chains; - if (chains) { - if (chains.value() !== obj) { - metaFor(obj).chains = chains = chains.copy(obj); - } else { - chains.didChange(null); - } - } - }; + @method resolve + @param {String} fullName + @return {Function} fullName's factory + */ + resolve: function(fullName) { + Ember.assert('fullName must be a proper full name', validateFullName(fullName)); + return resolve(this, this.normalize(fullName)); + }, - __exports__.flushPendingChains = flushPendingChains; - __exports__.removeChainWatcher = removeChainWatcher; - __exports__.ChainNode = ChainNode; - __exports__.finishChains = finishChains; - }); -define("ember-metal/computed", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/enumerable_utils","ember-metal/platform","ember-metal/watching","ember-metal/expand_properties","ember-metal/error","ember-metal/properties","ember-metal/property_events","ember-metal/is_empty","ember-metal/is_none","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - var get = __dependency2__.get; - var set = __dependency3__.set; - var meta = __dependency4__.meta; - var META_KEY = __dependency4__.META_KEY; - var guidFor = __dependency4__.guidFor; - var typeOf = __dependency4__.typeOf; - var inspect = __dependency4__.inspect; - var EnumerableUtils = __dependency5__["default"]; - var create = __dependency6__.create; - var watch = __dependency7__.watch; - var unwatch = __dependency7__.unwatch; - var expandProperties = __dependency8__["default"]; - var EmberError = __dependency9__["default"]; - var Descriptor = __dependency10__.Descriptor; - var defineProperty = __dependency10__.defineProperty; - var propertyWillChange = __dependency11__.propertyWillChange; - var propertyDidChange = __dependency11__.propertyDidChange; - var isEmpty = __dependency12__["default"]; - var isNone = __dependency13__.isNone; + /** + A hook that can be used to describe how the resolver will + attempt to find the factory. - /** - @module ember-metal - */ + For example, the default Ember `.describe` returns the full + class name (including namespace) where Ember's resolver expects + to find the `fullName`. - Ember.warn("The CP_DEFAULT_CACHEABLE flag has been removed and computed properties are always cached by default. Use `volatile` if you don't want caching.", Ember.ENV.CP_DEFAULT_CACHEABLE !== false); + @method describe + @param {String} fullName + @return {string} described fullName + */ + describe: function(fullName) { + return fullName; + }, + /** + A hook to enable custom fullName normalization behaviour - var metaFor = meta, - a_slice = [].slice, - o_create = create; + @method normalizeFullName + @param {String} fullName + @return {string} normalized fullName + */ + normalizeFullName: function(fullName) { + return fullName; + }, - function UNDEFINED() { } + /** + normalize a fullName based on the applications conventions - var lengthPattern = /\.(length|\[\])$/; + @method normalize + @param {String} fullName + @return {string} normalized fullName + */ + normalize: function(fullName) { + return this.normalizeCache[fullName] || ( + this.normalizeCache[fullName] = this.normalizeFullName(fullName) + ); + }, - // .......................................................... - // DEPENDENT KEYS - // + /** + @method makeToString - // data structure: - // meta.deps = { - // 'depKey': { - // 'keyName': count, - // } - // } + @param {any} factory + @param {string} fullName + @return {function} toString function + */ + makeToString: function(factory, fullName) { + return factory.toString(); + }, - /* - This function returns a map of unique dependencies for a - given object and key. - */ - function keysForDep(depsMeta, depKey) { - var keys = depsMeta[depKey]; - if (!keys) { - // if there are no dependencies yet for a the given key - // create a new empty list of dependencies for the key - keys = depsMeta[depKey] = {}; - } else if (!depsMeta.hasOwnProperty(depKey)) { - // otherwise if the dependency list is inherited from - // a superclass, clone the hash - keys = depsMeta[depKey] = o_create(keys); - } - return keys; - } + /** + Given a fullName return a corresponding instance. - function metaForDeps(meta) { - return keysForDep(meta, 'deps'); - } + The default behaviour is for lookup to return a singleton instance. + The singleton is scoped to the container, allowing multiple containers + to all have their own locally scoped singletons. - function addDependentKeys(desc, obj, keyName, meta) { - // the descriptor has a list of dependent keys, so - // add all of its dependent keys. - var depKeys = desc._dependentKeys, depsMeta, idx, len, depKey, keys; - if (!depKeys) return; + ```javascript + var container = new Container(); + container.register('api:twitter', Twitter); - depsMeta = metaForDeps(meta); + var twitter = container.lookup('api:twitter'); - for(idx = 0, len = depKeys.length; idx < len; idx++) { - depKey = depKeys[idx]; - // Lookup keys meta for depKey - keys = keysForDep(depsMeta, depKey); - // Increment the number of times depKey depends on keyName. - keys[keyName] = (keys[keyName] || 0) + 1; - // Watch the depKey - watch(obj, depKey, meta); - } - } + twitter instanceof Twitter; // => true - function removeDependentKeys(desc, obj, keyName, meta) { - // the descriptor has a list of dependent keys, so - // add all of its dependent keys. - var depKeys = desc._dependentKeys, depsMeta, idx, len, depKey, keys; - if (!depKeys) return; + // by default the container will return singletons + var twitter2 = container.lookup('api:twitter'); + twitter2 instanceof Twitter; // => true - depsMeta = metaForDeps(meta); + twitter === twitter2; //=> true + ``` - for(idx = 0, len = depKeys.length; idx < len; idx++) { - depKey = depKeys[idx]; - // Lookup keys meta for depKey - keys = keysForDep(depsMeta, depKey); - // Increment the number of times depKey depends on keyName. - keys[keyName] = (keys[keyName] || 0) - 1; - // Watch the depKey - unwatch(obj, depKey, meta); - } - } + If singletons are not wanted an optional flag can be provided at lookup. - // .......................................................... - // COMPUTED PROPERTY - // + ```javascript + var container = new Container(); + container.register('api:twitter', Twitter); - /** - A computed property transforms an objects function into a property. + var twitter = container.lookup('api:twitter', { singleton: false }); + var twitter2 = container.lookup('api:twitter', { singleton: false }); - By default the function backing the computed property will only be called - once and the result will be cached. You can specify various properties - that your computed property is dependent on. This will force the cached - result to be recomputed if the dependencies are modified. + twitter === twitter2; //=> false + ``` - In the following example we declare a computed property (by calling - `.property()` on the fullName function) and setup the properties - dependencies (depending on firstName and lastName). The fullName function - will be called once (regardless of how many times it is accessed) as long - as it's dependencies have not been changed. Once firstName or lastName are updated - any future calls (or anything bound) to fullName will incorporate the new - values. + @method lookup + @param {String} fullName + @param {Object} options + @return {any} + */ + lookup: function(fullName, options) { + Ember.assert('fullName must be a proper full name', validateFullName(fullName)); + return lookup(this, this.normalize(fullName), options); + }, - ```javascript - var Person = Ember.Object.extend({ - // these will be supplied by `create` - firstName: null, - lastName: null, + /** + Given a fullName return the corresponding factory. - fullName: function() { - var firstName = this.get('firstName'); - var lastName = this.get('lastName'); + @method lookupFactory + @param {String} fullName + @return {any} + */ + lookupFactory: function(fullName) { + Ember.assert('fullName must be a proper full name', validateFullName(fullName)); + return factoryFor(this, this.normalize(fullName)); + }, - return firstName + ' ' + lastName; - }.property('firstName', 'lastName') - }); + /** + Given a fullName check if the container is aware of its factory + or singleton instance. - var tom = Person.create({ - firstName: 'Tom', - lastName: 'Dale' - }); + @method has + @param {String} fullName + @return {Boolean} + */ + has: function(fullName) { + Ember.assert('fullName must be a proper full name', validateFullName(fullName)); + return has(this, this.normalize(fullName)); + }, - tom.get('fullName') // 'Tom Dale' - ``` + /** + Allow registering options for all factories of a type. - You can also define what Ember should do when setting a computed property. - If you try to set a computed property, it will be invoked with the key and - value you want to set it to. You can also accept the previous value as the - third parameter. + ```javascript + var container = new Container(); - ```javascript - var Person = Ember.Object.extend({ - // these will be supplied by `create` - firstName: null, - lastName: null, + // if all of type `connection` must not be singletons + container.optionsForType('connection', { singleton: false }); - fullName: function(key, value, oldValue) { - // getter - if (arguments.length === 1) { - var firstName = this.get('firstName'); - var lastName = this.get('lastName'); + container.register('connection:twitter', TwitterConnection); + container.register('connection:facebook', FacebookConnection); - return firstName + ' ' + lastName; + var twitter = container.lookup('connection:twitter'); + var twitter2 = container.lookup('connection:twitter'); - // setter - } else { - var name = value.split(' '); + twitter === twitter2; // => false - this.set('firstName', name[0]); - this.set('lastName', name[1]); + var facebook = container.lookup('connection:facebook'); + var facebook2 = container.lookup('connection:facebook'); - return value; - } - }.property('firstName', 'lastName') - }); + facebook === facebook2; // => false + ``` - var person = Person.create(); + @method optionsForType + @param {String} type + @param {Object} options + */ + optionsForType: function(type, options) { + if (this.parent) { illegalChildOperation('optionsForType'); } - person.set('fullName', 'Peter Wagenet'); - person.get('firstName'); // 'Peter' - person.get('lastName'); // 'Wagenet' - ``` + this._typeOptions[type] = options; + }, - @class ComputedProperty - @namespace Ember - @extends Ember.Descriptor - @constructor - */ - function ComputedProperty(func, opts) { - func.__ember_arity__ = func.length; - this.func = func; + /** + @method options + @param {String} fullName + @param {Object} options + */ + options: function(fullName, options) { + options = options || {}; + var normalizedName = this.normalize(fullName); + this._options[normalizedName] = options; + }, - this._cacheable = (opts && opts.cacheable !== undefined) ? opts.cacheable : true; - this._dependentKeys = opts && opts.dependentKeys; - this._readOnly = opts && (opts.readOnly !== undefined || !!opts.readOnly) || false; - } + /** + Used only via `injection`. - ComputedProperty.prototype = new Descriptor(); + Provides a specialized form of injection, specifically enabling + all objects of one type to be injected with a reference to another + object. - var ComputedPropertyPrototype = ComputedProperty.prototype; - ComputedPropertyPrototype._dependentKeys = undefined; - ComputedPropertyPrototype._suspended = undefined; - ComputedPropertyPrototype._meta = undefined; + For example, provided each object of type `controller` needed a `router`. + one would do the following: - /** - Properties are cacheable by default. Computed property will automatically - cache the return value of your function until one of the dependent keys changes. + ```javascript + var container = new Container(); - Call `volatile()` to set it into non-cached mode. When in this mode - the computed property will not automatically cache the return value. + container.register('router:main', Router); + container.register('controller:user', UserController); + container.register('controller:post', PostController); - However, if a property is properly observable, there is no reason to disable - caching. + container.typeInjection('controller', 'router', 'router:main'); - @method cacheable - @param {Boolean} aFlag optional set to `false` to disable caching - @return {Ember.ComputedProperty} this - @chainable - */ - ComputedPropertyPrototype.cacheable = function(aFlag) { - this._cacheable = aFlag !== false; - return this; - }; + var user = container.lookup('controller:user'); + var post = container.lookup('controller:post'); - /** - Call on a computed property to set it into non-cached mode. When in this - mode the computed property will not automatically cache the return value. + user.router instanceof Router; //=> true + post.router instanceof Router; //=> true - ```javascript - var outsideService = Ember.Object.extend({ - value: function() { - return OutsideService.getValue(); - }.property().volatile() - }).create(); - ``` + // both controllers share the same router + user.router === post.router; //=> true + ``` - @method volatile - @return {Ember.ComputedProperty} this - @chainable - */ - ComputedPropertyPrototype.volatile = function() { - return this.cacheable(false); - }; + @private + @method typeInjection + @param {String} type + @param {String} property + @param {String} fullName + */ + typeInjection: function(type, property, fullName) { + Ember.assert('fullName must be a proper full name', validateFullName(fullName)); - /** - Call on a computed property to set it into read-only mode. When in this - mode the computed property will throw an error when set. + if (this.parent) { illegalChildOperation('typeInjection'); } - ```javascript - var Person = Ember.Object.extend({ - guid: function() { - return 'guid-guid-guid'; - }.property().readOnly() - }); + var fullNameType = fullName.split(':')[0]; + if (fullNameType === type) { + throw new Error('Cannot inject a `' + fullName + + '` on other ' + type + + '(s). Register the `' + fullName + + '` as a different type and perform the typeInjection.'); + } - var person = Person.create(); + addTypeInjection(this.typeInjections, type, property, fullName); + }, - person.set('guid', 'new-guid'); // will throw an exception - ``` + /** + Defines injection rules. - @method readOnly - @return {Ember.ComputedProperty} this - @chainable - */ - ComputedPropertyPrototype.readOnly = function(readOnly) { - this._readOnly = readOnly === undefined || !!readOnly; - return this; - }; + These rules are used to inject dependencies onto objects when they + are instantiated. - /** - Sets the dependent keys on this computed property. Pass any number of - arguments containing key paths that this computed property depends on. + Two forms of injections are possible: - ```javascript - var President = Ember.Object.extend({ - fullName: computed(function() { - return this.get('firstName') + ' ' + this.get('lastName'); + * Injecting one fullName on another fullName + * Injecting one fullName on a type - // Tell Ember that this computed property depends on firstName - // and lastName - }).property('firstName', 'lastName') - }); + Example: - var president = President.create({ - firstName: 'Barack', - lastName: 'Obama', - }); + ```javascript + var container = new Container(); - president.get('fullName'); // 'Barack Obama' - ``` + container.register('source:main', Source); + container.register('model:user', User); + container.register('model:post', Post); - @method property - @param {String} path* zero or more property paths - @return {Ember.ComputedProperty} this - @chainable - */ - ComputedPropertyPrototype.property = function() { - var args; + // injecting one fullName on another fullName + // eg. each user model gets a post model + container.injection('model:user', 'post', 'model:post'); - var addArg = function (property) { - args.push(property); - }; + // injecting one fullName on another type + container.injection('model', 'source', 'source:main'); - args = []; - for (var i = 0, l = arguments.length; i < l; i++) { - expandProperties(arguments[i], addArg); - } + var user = container.lookup('model:user'); + var post = container.lookup('model:post'); - this._dependentKeys = args; - return this; - }; + user.source instanceof Source; //=> true + post.source instanceof Source; //=> true - /** - 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. + user.post instanceof Post; //=> true - You can pass a hash of these values to a computed property like this: + // and both models share the same source + user.source === post.source; //=> true + ``` - ``` - person: function() { - var personId = this.get('personId'); - return App.Person.create({ id: personId }); - }.property().meta({ type: App.Person }) - ``` + @method injection + @param {String} factoryName + @param {String} property + @param {String} injectionName + */ + injection: function(fullName, property, injectionName) { + if (this.parent) { illegalChildOperation('injection'); } - The hash that you pass to the `meta()` function will be saved on the - computed property descriptor under the `_meta` key. Ember runtime - exposes a public API for retrieving these values from classes, - via the `metaForProperty()` function. + validateFullName(injectionName); + var normalizedInjectionName = this.normalize(injectionName); - @method meta - @param {Hash} meta - @chainable - */ + if (fullName.indexOf(':') === -1) { + return this.typeInjection(fullName, property, normalizedInjectionName); + } - ComputedPropertyPrototype.meta = function(meta) { - if (arguments.length === 0) { - return this._meta || {}; - } else { - this._meta = meta; - return this; - } - }; + Ember.assert('fullName must be a proper full name', validateFullName(fullName)); + var normalizedName = this.normalize(fullName); - /* impl descriptor API */ - ComputedPropertyPrototype.didChange = function(obj, keyName) { - // _suspended is set via a CP.set to ensure we don't clear - // the cached value set by the setter - if (this._cacheable && this._suspended !== obj) { - var meta = metaFor(obj); - if (meta.cache[keyName] !== undefined) { - meta.cache[keyName] = undefined; - removeDependentKeys(this, obj, keyName, meta); + if (this.cache[normalizedName]) { + throw new Error("Attempted to register an injection for a type that has already been looked up. ('" + + normalizedName + "', '" + + property + "', '" + + injectionName + "')"); } - } - }; - function finishChains(chainNodes) - { - for (var i=0, l=chainNodes.length; i true + ``` - chainNodes = meta.chainWatchers && meta.chainWatchers[keyName]; - if (chainNodes) { finishChains(chainNodes); } - addDependentKeys(this, obj, keyName, meta); - } else { - ret = this.func.call(obj, keyName); - } - return ret; - }; + @private + @method factoryTypeInjection + @param {String} type + @param {String} property + @param {String} fullName + */ + factoryTypeInjection: function(type, property, fullName) { + if (this.parent) { illegalChildOperation('factoryTypeInjection'); } - /** - Set the value of a computed property. If the function that backs your - computed property does not accept arguments then the default action for - setting would be to define the property on the current object, and set - the value of the property to the value being set. + addTypeInjection(this.factoryTypeInjections, type, property, this.normalize(fullName)); + }, - Generally speaking if you intend for your computed property to be set - your backing function should accept either two or three arguments. + /** + Defines factory injection rules. - @method set - @param {String} keyName The key being accessed. - @param {Object} newValue The new value being assigned. - @param {String} oldValue The old value being replaced. - @return {Object} The return value of the function backing the CP. - */ - ComputedPropertyPrototype.set = function(obj, keyName, value) { - var cacheable = this._cacheable, - func = this.func, - meta = metaFor(obj, cacheable), - oldSuspended = this._suspended, - hadCachedValue = false, - cache = meta.cache, - funcArgLength, cachedValue, ret; + Similar to regular injection rules, but are run against factories, via + `Container#lookupFactory`. - if (this._readOnly) { - throw new EmberError('Cannot set read-only property "' + keyName + '" on object: ' + inspect(obj)); - } + These rules are used to inject objects onto factories when they + are looked up. - this._suspended = obj; + Two forms of injections are possible: - try { + * Injecting one fullName on another fullName + * Injecting one fullName on a type - if (cacheable && cache[keyName] !== undefined) { - cachedValue = cache[keyName]; - hadCachedValue = true; - } - - // Check if the CP has been wrapped. If it has, use the - // length from the wrapped function. - - funcArgLength = func.wrappedFunction ? func.wrappedFunction.__ember_arity__ : func.__ember_arity__; - - // For backwards-compatibility with computed properties - // that check for arguments.length === 2 to determine if - // they are being get or set, only pass the old cached - // value if the computed property opts into a third - // argument. - if (funcArgLength === 3) { - ret = func.call(obj, keyName, value, cachedValue); - } else if (funcArgLength === 2) { - ret = func.call(obj, keyName, value); - } else { - defineProperty(obj, keyName, null, cachedValue); - set(obj, keyName, value); - return; - } + Example: - if (hadCachedValue && cachedValue === ret) { return; } + ```javascript + var container = new Container(); - var watched = meta.watching[keyName]; - if (watched) { propertyWillChange(obj, keyName); } + container.register('store:main', Store); + container.register('store:secondary', OtherStore); + container.register('model:user', User); + container.register('model:post', Post); - if (hadCachedValue) { - cache[keyName] = undefined; - } + // injecting one fullName on another type + container.factoryInjection('model', 'store', 'store:main'); - if (cacheable) { - if (!hadCachedValue) { - addDependentKeys(this, obj, keyName, meta); - } - if (ret === undefined) { - cache[keyName] = UNDEFINED; - } else { - cache[keyName] = ret; - } - } + // injecting one fullName on another fullName + container.factoryInjection('model:post', 'secondaryStore', 'store:secondary'); - if (watched) { propertyDidChange(obj, keyName); } - } finally { - this._suspended = oldSuspended; - } - return ret; - }; + var UserFactory = container.lookupFactory('model:user'); + var PostFactory = container.lookupFactory('model:post'); + var store = container.lookup('store:main'); - /* called before property is overridden */ - ComputedPropertyPrototype.teardown = function(obj, keyName) { - var meta = metaFor(obj); + UserFactory.store instanceof Store; //=> true + UserFactory.secondaryStore instanceof OtherStore; //=> false - if (keyName in meta.cache) { - removeDependentKeys(this, obj, keyName, meta); - } + PostFactory.store instanceof Store; //=> true + PostFactory.secondaryStore instanceof OtherStore; //=> true - if (this._cacheable) { delete meta.cache[keyName]; } + // and both models share the same source instance + UserFactory.store === PostFactory.store; //=> true + ``` - return null; // no value to restore - }; + @method factoryInjection + @param {String} factoryName + @param {String} property + @param {String} injectionName + */ + factoryInjection: function(fullName, property, injectionName) { + if (this.parent) { illegalChildOperation('injection'); } + var normalizedName = this.normalize(fullName); + var normalizedInjectionName = this.normalize(injectionName); - /** - This helper returns a new property descriptor that wraps the passed - computed property function. You can use this helper to define properties - with mixins or via `Ember.defineProperty()`. + validateFullName(injectionName); - The function you pass will be used to both get and set property values. - The function should accept two parameters, key and value. If value is not - undefined you should set the value first. In either case return the - current value of the property. + if (fullName.indexOf(':') === -1) { + return this.factoryTypeInjection(normalizedName, property, normalizedInjectionName); + } - @method computed - @for Ember - @param {Function} func The computed property function. - @return {Ember.ComputedProperty} property descriptor instance - */ - function computed(func) { - var args; + Ember.assert('fullName must be a proper full name', validateFullName(fullName)); - if (arguments.length > 1) { - args = a_slice.call(arguments, 0, -1); - func = a_slice.call(arguments, -1)[0]; - } + if (this.factoryCache[normalizedName]) { + throw new Error('Attempted to register a factoryInjection for a type that has already ' + + 'been looked up. (\'' + normalizedName + '\', \'' + property + '\', \'' + injectionName + '\')'); + } - if (typeof func !== "function") { - throw new EmberError("Computed Property declared without a property function"); - } + addInjection(initRules(this.factoryInjections, normalizedName), property, normalizedInjectionName); + }, - var cp = new ComputedProperty(func); + /** + A depth first traversal, destroying the container, its descendant containers and all + their managed objects. - if (args) { - cp.property.apply(cp, args); - } + @method destroy + */ + destroy: function() { + for (var i = 0, length = this.children.length; i < length; i++) { + this.children[i].destroy(); + } - return cp; - }; + this.children = []; - /** - Returns the cached value for a property, if one exists. - This can be useful for peeking at the value of a computed - property that is generated lazily, without accidentally causing - it to be created. + eachDestroyable(this, function(item) { + item.destroy(); + }); - @method cacheFor - @for Ember - @param {Object} obj the object whose property you want to check - @param {String} key the name of the property whose cached value you want - to return - @return {Object} the cached value - */ - function cacheFor(obj, key) { - var meta = obj[META_KEY], - cache = meta && meta.cache, - ret = cache && cache[key]; + this.parent = undefined; + this.isDestroyed = true; + }, - if (ret === UNDEFINED) { return undefined; } - return ret; - }; + /** + @method reset + */ + reset: function() { + for (var i = 0, length = this.children.length; i < length; i++) { + resetCache(this.children[i]); + } - cacheFor.set = function(cache, key, value) { - if (value === undefined) { - cache[key] = UNDEFINED; - } else { - cache[key] = value; + resetCache(this); } }; - cacheFor.get = function(cache, key) { - var ret = cache[key]; - if (ret === UNDEFINED) { return undefined; } - return ret; - }; + function resolve(container, normalizedName) { + var cached = container.resolveCache[normalizedName]; + if (cached) { return cached; } - cacheFor.remove = function(cache, key) { - cache[key] = undefined; - }; + var resolved = container.resolver(normalizedName) || container.registry[normalizedName]; + container.resolveCache[normalizedName] = resolved; - function getProperties(self, propertyNames) { - var ret = {}; - for(var i = 0; i < propertyNames.length; i++) { - ret[propertyNames[i]] = get(self, propertyNames[i]); - } - return ret; + return resolved; } - function registerComputed(name, macro) { - computed[name] = function(dependentKey) { - var args = a_slice.call(arguments); - return computed(dependentKey, function() { - return macro.apply(this, args); - }); - }; - }; + function has(container, fullName){ + if (container.cache[fullName]) { + return true; + } - function registerComputedWithProperties(name, macro) { - computed[name] = function() { - var properties = a_slice.call(arguments); + return container.resolve(fullName) !== undefined; + } - var computedFunc = computed(function() { - return macro.apply(this, [getProperties(this, properties)]); - }); + function lookup(container, fullName, options) { + options = options || {}; - return computedFunc.property.apply(computedFunc, properties); - }; - }; + if (container.cache[fullName] && options.singleton !== false) { + return container.cache[fullName]; + } - /** - A computed property that returns true if the value of the dependent - property is null, an empty string, empty array, or empty function. + var value = instantiate(container, fullName); - Example + if (value === undefined) { return; } - ```javascript - var ToDoList = Ember.Object.extend({ - done: Ember.computed.empty('todos') - }); + if (isSingleton(container, fullName) && options.singleton !== false) { + container.cache[fullName] = value; + } - var todoList = ToDoList.create({ - todos: ['Unit Test', 'Documentation', 'Release'] - }); + return value; + } - todoList.get('done'); // false - todoList.get('todos').clear(); - todoList.get('done'); // true - ``` + function illegalChildOperation(operation) { + throw new Error(operation + ' is not currently supported on child containers'); + } - @since 1.6.0 - @method computed.empty - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which negate - the original value for property - */ - computed.empty = function (dependentKey) { - return computed(dependentKey + '.length', function () { - return isEmpty(get(this, dependentKey)); - }); - }; + function isSingleton(container, fullName) { + var singleton = option(container, fullName, 'singleton'); - /** - A computed property that returns true if the value of the dependent - property is NOT null, an empty string, empty array, or empty function. + return singleton !== false; + } - Note: When using `computed.notEmpty` to watch an array make sure to - use the `array.[]` syntax so the computed can subscribe to transitions - from empty to non-empty states. + function buildInjections(container, injections) { + var hash = {}; - Example + if (!injections) { return hash; } - ```javascript - var Hamster = Ember.Object.extend({ - hasStuff: Ember.computed.notEmpty('backpack.[]') - }); + validateInjections(container, injections); - var hamster = Hamster.create({ backpack: ['Food', 'Sleeping Bag', 'Tent'] }); + var injection; - hamster.get('hasStuff'); // true - hamster.get('backpack').clear(); // [] - hamster.get('hasStuff'); // false - ``` + for (var i = 0, length = injections.length; i < length; i++) { + injection = injections[i]; + hash[injection.property] = lookup(container, injection.fullName); + } - @method computed.notEmpty - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which returns true if - original value for property is not empty. - */ - registerComputed('notEmpty', function(dependentKey) { - return !isEmpty(get(this, dependentKey)); - }); + return hash; + } - /** - A computed property that returns true if the value of the dependent - property is null or undefined. This avoids errors from JSLint complaining - about use of ==, which can be technically confusing. + function validateInjections(container, injections) { + if (!injections) { return; } - Example + var fullName; - ```javascript - var Hamster = Ember.Object.extend({ - isHungry: Ember.computed.none('food') - }); + for (var i = 0, length = injections.length; i < length; i++) { + fullName = injections[i].fullName; - var hamster = Hamster.create(); + if (!container.has(fullName)) { + throw new Error('Attempting to inject an unknown injection: `' + fullName + '`'); + } + } + } - hamster.get('isHungry'); // true - hamster.set('food', 'Banana'); - hamster.get('isHungry'); // false - hamster.set('food', null); - hamster.get('isHungry'); // true - ``` + function option(container, fullName, optionName) { + var options = container._options[fullName]; - @method computed.none - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which - returns true if original value for property is null or undefined. - */ - registerComputed('none', function(dependentKey) { - return isNone(get(this, dependentKey)); - }); + if (options && options[optionName] !== undefined) { + return options[optionName]; + } - /** - A computed property that returns the inverse boolean value - of the original value for the dependent property. + var type = fullName.split(':')[0]; + options = container._typeOptions[type]; - Example + if (options) { + return options[optionName]; + } + } - ```javascript - var User = Ember.Object.extend({ - isAnonymous: Ember.computed.not('loggedIn') - }); + function factoryFor(container, fullName) { + var cache = container.factoryCache; + if (cache[fullName]) { + return cache[fullName]; + } + var factory = container.resolve(fullName); + if (factory === undefined) { return; } - var user = User.create({loggedIn: false}); + var type = fullName.split(':')[0]; + if (!factory || typeof factory.extend !== 'function' || (!Ember.MODEL_FACTORY_INJECTIONS && type === 'model')) { + if (factory && typeof factory._onLookup === 'function') { + factory._onLookup(fullName); + } - user.get('isAnonymous'); // true - user.set('loggedIn', true); - user.get('isAnonymous'); // false - ``` + // TODO: think about a 'safe' merge style extension + // for now just fallback to create time injection + cache[fullName] = factory; + return factory; + } else { + var injections = injectionsFor(container, fullName); + var factoryInjections = factoryInjectionsFor(container, fullName); - @method computed.not - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which returns - inverse of the original value for property - */ - registerComputed('not', function(dependentKey) { - return !get(this, dependentKey); - }); + factoryInjections._toString = container.makeToString(factory, fullName); - /** - A computed property that converts the provided dependent property - into a boolean value. + var injectedFactory = factory.extend(injections); + injectedFactory.reopenClass(factoryInjections); - ```javascript - var Hamster = Ember.Object.extend({ - hasBananas: Ember.computed.bool('numBananas') - }); + if (factory && typeof factory._onLookup === 'function') { + factory._onLookup(fullName); + } - var hamster = Hamster.create(); + cache[fullName] = injectedFactory; - hamster.get('hasBananas'); // false - hamster.set('numBananas', 0); - hamster.get('hasBananas'); // false - hamster.set('numBananas', 1); - hamster.get('hasBananas'); // true - hamster.set('numBananas', null); - hamster.get('hasBananas'); // false - ``` + return injectedFactory; + } + } - @method computed.bool - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which converts - to boolean the original value for property - */ - registerComputed('bool', function(dependentKey) { - return !!get(this, dependentKey); - }); + function injectionsFor(container, fullName) { + var splitName = fullName.split(':'); + var type = splitName[0]; + var injections = []; - /** - A computed property which matches the original value for the - dependent property against a given RegExp, returning `true` - if they values matches the RegExp and `false` if it does not. + injections = injections.concat(container.typeInjections[type] || []); + injections = injections.concat(container.injections[fullName] || []); - Example + injections = buildInjections(container, injections); + injections._debugContainerKey = fullName; + injections.container = container; - ```javascript - var User = Ember.Object.extend({ - hasValidEmail: Ember.computed.match('email', /^.+@.+\..+$/) - }); + return injections; + } - var user = User.create({loggedIn: false}); + function factoryInjectionsFor(container, fullName) { + var splitName = fullName.split(':'); + var type = splitName[0]; + var factoryInjections = []; - user.get('hasValidEmail'); // false - user.set('email', ''); - user.get('hasValidEmail'); // false - user.set('email', 'ember_hamster@example.com'); - user.get('hasValidEmail'); // true - ``` + factoryInjections = factoryInjections.concat(container.factoryTypeInjections[type] || []); + factoryInjections = factoryInjections.concat(container.factoryInjections[fullName] || []); - @method computed.match - @for Ember - @param {String} dependentKey - @param {RegExp} regexp - @return {Ember.ComputedProperty} computed property which match - the original value for property against a given RegExp - */ - registerComputed('match', function(dependentKey, regexp) { - var value = get(this, dependentKey); - return typeof value === 'string' ? regexp.test(value) : false; - }); + factoryInjections = buildInjections(container, factoryInjections); + factoryInjections._debugContainerKey = fullName; - /** - A computed property that returns true if the provided dependent property - is equal to the given value. + return factoryInjections; + } - Example + function normalizeInjectionsHash(hash) { + var injections = []; - ```javascript - var Hamster = Ember.Object.extend({ - napTime: Ember.computed.equal('state', 'sleepy') - }); + for (var key in hash) { + if (hash.hasOwnProperty(key)) { + Ember.assert("Expected a proper full name, given '" + hash[key] + "'", validateFullName(hash[key])); - var hamster = Hamster.create(); + addInjection(injections, key, hash[key]); + } + } - hamster.get('napTime'); // false - hamster.set('state', 'sleepy'); - hamster.get('napTime'); // true - hamster.set('state', 'hungry'); - hamster.get('napTime'); // false - ``` + return injections; + } - @method computed.equal - @for Ember - @param {String} dependentKey - @param {String|Number|Object} value - @return {Ember.ComputedProperty} computed property which returns true if - the original value for property is equal to the given value. - */ - registerComputed('equal', function(dependentKey, value) { - return get(this, dependentKey) === value; - }); + function instantiate(container, fullName) { + var factory = factoryFor(container, fullName); + var lazyInjections, validationCache; - /** - A computed property that returns true if the provied dependent property - is greater than the provided value. + if (option(container, fullName, 'instantiate') === false) { + return factory; + } - Example + if (factory) { + if (typeof factory.create !== 'function') { + throw new Error('Failed to create an instance of \'' + fullName + '\'. ' + + 'Most likely an improperly defined class or an invalid module export.'); + } - ```javascript - var Hamster = Ember.Object.extend({ - hasTooManyBananas: Ember.computed.gt('numBananas', 10) - }); + + validationCache = container.validationCache; - var hamster = Hamster.create(); + // Ensure that all lazy injections are valid at instantiation time + if (!validationCache[fullName] && typeof factory._lazyInjections === 'function') { + lazyInjections = factory._lazyInjections(); - hamster.get('hasTooManyBananas'); // false - hamster.set('numBananas', 3); - hamster.get('hasTooManyBananas'); // false - hamster.set('numBananas', 11); - hamster.get('hasTooManyBananas'); // true - ``` + validateInjections(container, normalizeInjectionsHash(lazyInjections)); + } - @method computed.gt - @for Ember - @param {String} dependentKey - @param {Number} value - @return {Ember.ComputedProperty} computed property which returns true if - the original value for property is greater then given value. - */ - registerComputed('gt', function(dependentKey, value) { - return get(this, dependentKey) > value; - }); + validationCache[fullName] = true; + - /** - A computed property that returns true if the provided dependent property - is greater than or equal to the provided value. + if (typeof factory.extend === 'function') { + // assume the factory was extendable and is already injected + return factory.create(); + } else { + // assume the factory was extendable + // to create time injections + // TODO: support new'ing for instantiation and merge injections for pure JS Functions + return factory.create(injectionsFor(container, fullName)); + } + } + } - Example + function eachDestroyable(container, callback) { + var cache = container.cache; + var keys = emberKeys(cache); + var key, value; - ```javascript - var Hamster = Ember.Object.extend({ - hasTooManyBananas: Ember.computed.gte('numBananas', 10) - }); + for (var i = 0, l = keys.length; i < l; i++) { + key = keys[i]; + value = cache[key]; - var hamster = Hamster.create(); + if (option(container, key, 'instantiate') !== false) { + callback(value); + } + } + } - hamster.get('hasTooManyBananas'); // false - hamster.set('numBananas', 3); - hamster.get('hasTooManyBananas'); // false - hamster.set('numBananas', 10); - hamster.get('hasTooManyBananas'); // true - ``` + function resetCache(container) { + eachDestroyable(container, function(value) { + value.destroy(); + }); - @method computed.gte - @for Ember - @param {String} dependentKey - @param {Number} value - @return {Ember.ComputedProperty} computed property which returns true if - the original value for property is greater or equal then given value. - */ - registerComputed('gte', function(dependentKey, value) { - return get(this, dependentKey) >= value; - }); + container.cache.dict = dictionary(null); + } - /** - A computed property that returns true if the provided dependent property - is less than the provided value. + function addTypeInjection(rules, type, property, fullName) { + var injections = rules[type]; - Example + if (!injections) { + injections = []; + rules[type] = injections; + } - ```javascript - var Hamster = Ember.Object.extend({ - needsMoreBananas: Ember.computed.lt('numBananas', 3) + injections.push({ + property: property, + fullName: fullName }); + } - var hamster = Hamster.create(); + var VALID_FULL_NAME_REGEXP = /^[^:]+.+:[^:]+$/; + function validateFullName(fullName) { + if (!VALID_FULL_NAME_REGEXP.test(fullName)) { + throw new TypeError('Invalid Fullname, expected: `type:name` got: ' + fullName); + } + return true; + } - hamster.get('needsMoreBananas'); // true - hamster.set('numBananas', 3); - hamster.get('needsMoreBananas'); // false - hamster.set('numBananas', 2); - hamster.get('needsMoreBananas'); // true - ``` + function initRules(rules, factoryName) { + return rules[factoryName] || (rules[factoryName] = []); + } - @method computed.lt - @for Ember - @param {String} dependentKey - @param {Number} value - @return {Ember.ComputedProperty} computed property which returns true if - the original value for property is less then given value. - */ - registerComputed('lt', function(dependentKey, value) { - return get(this, dependentKey) < value; - }); - - /** - A computed property that returns true if the provided dependent property - is less than or equal to the provided value. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - needsMoreBananas: Ember.computed.lte('numBananas', 3) + function addInjection(injections, property, injectionName) { + injections.push({ + property: property, + fullName: injectionName }); + } - var hamster = Hamster.create(); + __exports__["default"] = Container; + }); +enifed("dag-map", + ["exports"], + function(__exports__) { + "use strict"; + function visit(vertex, fn, visited, path) { + var name = vertex.name; + var vertices = vertex.incoming; + var names = vertex.incomingNames; + var len = names.length; + var i; - hamster.get('needsMoreBananas'); // true - hamster.set('numBananas', 5); - hamster.get('needsMoreBananas'); // false - hamster.set('numBananas', 3); - hamster.get('needsMoreBananas'); // true - ``` + if (!visited) { + visited = {}; + } + if (!path) { + path = []; + } + if (visited.hasOwnProperty(name)) { + return; + } + path.push(name); + visited[name] = true; + for (i = 0; i < len; i++) { + visit(vertices[names[i]], fn, visited, path); + } + fn(vertex, path); + path.pop(); + } - @method computed.lte - @for Ember - @param {String} dependentKey - @param {Number} value - @return {Ember.ComputedProperty} computed property which returns true if - the original value for property is less or equal then given value. - */ - registerComputed('lte', function(dependentKey, value) { - return get(this, dependentKey) <= value; - }); /** - A computed property that performs a logical `and` on the - original values for the provided dependent properties. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - readyForCamp: Ember.computed.and('hasTent', 'hasBackpack') - }); + * DAG stands for Directed acyclic graph. + * + * It is used to build a graph of dependencies checking that there isn't circular + * dependencies. p.e Registering initializers with a certain precedence order. + * + * @class DAG + * @constructor + */ + function DAG() { + this.names = []; + this.vertices = Object.create(null); + } - var hamster = Hamster.create(); + /** + * DAG Vertex + * + * @class Vertex + * @constructor + */ - hamster.get('readyForCamp'); // false - hamster.set('hasTent', true); - hamster.get('readyForCamp'); // false - hamster.set('hasBackpack', true); - hamster.get('readyForCamp'); // true - ``` + function Vertex(name) { + this.name = name; + this.incoming = {}; + this.incomingNames = []; + this.hasOutgoing = false; + this.value = null; + } - @method computed.and - @for Ember - @param {String} dependentKey* - @return {Ember.ComputedProperty} computed property which performs - a logical `and` on the values of all the original values for properties. - */ - registerComputedWithProperties('and', function(properties) { - for (var key in properties) { - if (properties.hasOwnProperty(key) && !properties[key]) { - return false; - } + /** + * Adds a vertex entry to the graph unless it is already added. + * + * @private + * @method add + * @param {String} name The name of the vertex to add + */ + DAG.prototype.add = function(name) { + if (!name) { + throw new Error("Can't add Vertex without name"); } - return true; - }); + if (this.vertices[name] !== undefined) { + return this.vertices[name]; + } + var vertex = new Vertex(name); + this.vertices[name] = vertex; + this.names.push(name); + return vertex; + }; /** - A computed property which performs a logical `or` on the - original values for the provided dependent properties. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - readyForRain: Ember.computed.or('hasJacket', 'hasUmbrella') - }); - - var hamster = Hamster.create(); - - hamster.get('readyForRain'); // false - hamster.set('hasJacket', true); - hamster.get('readyForRain'); // true - ``` + * Adds a vertex to the graph and sets its value. + * + * @private + * @method map + * @param {String} name The name of the vertex. + * @param value The value to put in the vertex. + */ + DAG.prototype.map = function(name, value) { + this.add(name).value = value; + }; - @method computed.or - @for Ember - @param {String} dependentKey* - @return {Ember.ComputedProperty} computed property which performs - a logical `or` on the values of all the original values for properties. - */ - registerComputedWithProperties('or', function(properties) { - for (var key in properties) { - if (properties.hasOwnProperty(key) && properties[key]) { - return true; + /** + * Connects the vertices with the given names, adding them to the graph if + * necessary, only if this does not produce is any circular dependency. + * + * @private + * @method addEdge + * @param {String} fromName The name the vertex where the edge starts. + * @param {String} toName The name the vertex where the edge ends. + */ + DAG.prototype.addEdge = function(fromName, toName) { + if (!fromName || !toName || fromName === toName) { + return; + } + var from = this.add(fromName); + var to = this.add(toName); + if (to.incoming.hasOwnProperty(fromName)) { + return; + } + function checkCycle(vertex, path) { + if (vertex.name === toName) { + throw new Error("cycle detected: " + toName + " <- " + path.join(" <- ")); } } - return false; - }); + visit(from, checkCycle); + from.hasOutgoing = true; + to.incoming[fromName] = from; + to.incomingNames.push(fromName); + }; /** - A computed property that returns the first truthy value - from a list of dependent properties. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - hasClothes: Ember.computed.any('hat', 'shirt') - }); - - var hamster = Hamster.create(); - - hamster.get('hasClothes'); // null - hamster.set('shirt', 'Hawaiian Shirt'); - hamster.get('hasClothes'); // 'Hawaiian Shirt' - ``` + * Visits all the vertex of the graph calling the given function with each one, + * ensuring that the vertices are visited respecting their precedence. + * + * @method topsort + * @param {Function} fn The function to be invoked on each vertex. + */ + DAG.prototype.topsort = function(fn) { + var visited = {}; + var vertices = this.vertices; + var names = this.names; + var len = names.length; + var i, vertex; - @method computed.any - @for Ember - @param {String} dependentKey* - @return {Ember.ComputedProperty} computed property which returns - the first truthy value of given list of properties. - */ - registerComputedWithProperties('any', function(properties) { - for (var key in properties) { - if (properties.hasOwnProperty(key) && properties[key]) { - return properties[key]; + for (i = 0; i < len; i++) { + vertex = vertices[names[i]]; + if (!vertex.hasOutgoing) { + visit(vertex, fn, visited); } } - return null; - }); + }; /** - A computed property that returns the array of values - for the provided dependent properties. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - clothes: Ember.computed.collect('hat', 'shirt') - }); - - var hamster = Hamster.create(); - - hamster.get('clothes'); // [null, null] - hamster.set('hat', 'Camp Hat'); - hamster.set('shirt', 'Camp Shirt'); - hamster.get('clothes'); // ['Camp Hat', 'Camp Shirt'] - ``` - - @method computed.collect - @for Ember - @param {String} dependentKey* - @return {Ember.ComputedProperty} computed property which maps - values of all passed properties in to an array. - */ - registerComputedWithProperties('collect', function(properties) { - var res = []; - for (var key in properties) { - if (properties.hasOwnProperty(key)) { - if (isNone(properties[key])) { - res.push(null); - } else { - res.push(properties[key]); + * Adds a vertex with the given name and value to the graph and joins it with the + * vertices referenced in _before_ and _after_. If there isn't vertices with those + * names, they are added too. + * + * If either _before_ or _after_ are falsy/empty, the added vertex will not have + * an incoming/outgoing edge. + * + * @method addEdges + * @param {String} name The name of the vertex to be added. + * @param value The value of that vertex. + * @param before An string or array of strings with the names of the vertices before + * which this vertex must be visited. + * @param after An string or array of strings with the names of the vertex after + * which this vertex must be visited. + * + */ + DAG.prototype.addEdges = function(name, value, before, after) { + var i; + this.map(name, value); + if (before) { + if (typeof before === 'string') { + this.addEdge(name, before); + } else { + for (i = 0; i < before.length; i++) { + this.addEdge(name, before[i]); } } } - return res; - }); - - /** - Creates a new property that is an alias for another property - on an object. Calls to `get` or `set` this property behave as - though they were called on the original property. - - ```javascript - var Person = Ember.Object.extend({ - name: 'Alex Matchneer', - nomen: Ember.computed.alias('name') - }); - - var alex = Person.create(); - - alex.get('nomen'); // 'Alex Matchneer' - alex.get('name'); // 'Alex Matchneer' - - alex.set('nomen', '@machty'); - alex.get('name'); // '@machty' - ``` - - @method computed.alias - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which creates an - alias to the original value for property. - */ - computed.alias = function(dependentKey) { - return computed(dependentKey, function(key, value) { - if (arguments.length > 1) { - set(this, dependentKey, value); - return get(this, dependentKey); + if (after) { + if (typeof after === 'string') { + this.addEdge(after, name); } else { - return get(this, dependentKey); + for (i = 0; i < after.length; i++) { + this.addEdge(after[i], name); + } } - }); + } }; - /** - Where `computed.alias` aliases `get` and `set`, and allows for bidirectional - data flow, `computed.oneWay` only provides an aliased `get`. The `set` will - not mutate the upstream property, rather causes the current property to - become the value set. This causes the downstream property to permanently - diverge from the upstream property. + __exports__["default"] = DAG; + }); +enifed("dag-map.umd", + ["./dag-map"], + function(__dependency1__) { + "use strict"; + var DAG = __dependency1__["default"]; - Example + /* global define:true module:true window: true */ + if (typeof enifed === 'function' && enifed.amd) { + enifed(function() { return DAG; }); + } else if (typeof module !== 'undefined' && module.exports) { + module.exports = DAG; + } else if (typeof this !== 'undefined') { + this['DAG'] = DAG; + } + }); +enifed("ember-application", + ["ember-metal/core","ember-runtime/system/lazy_load","ember-application/system/resolver","ember-application/system/application","ember-application/ext/controller"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { + "use strict"; + var Ember = __dependency1__["default"]; + var runLoadHooks = __dependency2__.runLoadHooks; - ```javascript - var User = Ember.Object.extend({ - firstName: null, - lastName: null, - nickName: Ember.computed.oneWay('firstName') - }); + /** + Ember Application - var teddy = User.create({ - firstName: 'Teddy', - lastName: 'Zeenny' - }); + @module ember + @submodule ember-application + @requires ember-views, ember-routing + */ - teddy.get('nickName'); // 'Teddy' - teddy.set('nickName', 'TeddyBear'); // 'TeddyBear' - teddy.get('firstName'); // 'Teddy' - ``` + var Resolver = __dependency3__.Resolver; + var DefaultResolver = __dependency3__["default"]; + var Application = __dependency4__["default"]; + // side effect of extending ControllerMixin - @method computed.oneWay - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which creates a - one way computed property to the original value for property. - */ - computed.oneWay = function(dependentKey) { - return computed(dependentKey, function() { - return get(this, dependentKey); - }); - }; + Ember.Application = Application; + Ember.Resolver = Resolver; + Ember.DefaultResolver = DefaultResolver; - + runLoadHooks('Ember.Application', Application); + }); +enifed("ember-application/ext/controller", + ["ember-metal/core","ember-metal/property_get","ember-metal/error","ember-metal/utils","ember-metal/computed","ember-runtime/mixins/controller","ember-routing/system/controller_for","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; /** - Where `computed.oneWay` provides oneWay bindings, `computed.readOnly` provides - a readOnly one way binding. Very often when using `computed.oneWay` one does - not also want changes to propogate back up, as they will replace the value. - - This prevents the reverse flow, and also throws an exception when it occurs. + @module ember + @submodule ember-application + */ - Example + var Ember = __dependency1__["default"]; + // Ember.assert + var get = __dependency2__.get; + var EmberError = __dependency3__["default"]; + var inspect = __dependency4__.inspect; + var computed = __dependency5__.computed; + var ControllerMixin = __dependency6__["default"]; + var meta = __dependency4__.meta; + var controllerFor = __dependency7__["default"]; - ```javascript - var User = Ember.Object.extend({ - firstName: null, - lastName: null, - nickName: Ember.computed.readOnly('firstName') - }); + function verifyNeedsDependencies(controller, container, needs) { + var dependency, i, l; + var missing = []; - var teddy = User.create({ - firstName: 'Teddy', - lastName: 'Zeenny' - }); + for (i=0, l=needs.length; i' );` - teddy.get('firstName'); // 'Teddy' - ``` + Ember.assert(inspect(controller) + "#needs must not specify dependencies with periods in their names (" + + dependency + ")", dependency.indexOf('.') === -1); - @method computed.readOnly - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which creates a - one way computed property to the original value for property. - @since 1.5.0 - */ - computed.readOnly = function(dependentKey) { - return computed(dependentKey, function() { - return get(this, dependentKey); - }).readOnly(); - }; - /** - A computed property that acts like a standard getter and setter, - but returns the value at the provided `defaultPath` if the - property itself has not been set to a value + if (dependency.indexOf(':') === -1) { + dependency = "controller:" + dependency; + } - Example + // Structure assert to still do verification but not string concat in production + if (!container.has(dependency)) { + missing.push(dependency); + } + } + if (missing.length) { + throw new EmberError(inspect(controller) + " needs [ " + missing.join(', ') + + " ] but " + (missing.length > 1 ? 'they' : 'it') + " could not be found"); + } + } - ```javascript - var Hamster = Ember.Object.extend({ - wishList: Ember.computed.defaultTo('favoriteFood') - }); + var defaultControllersComputedProperty = computed(function() { + var controller = this; - var hamster = Hamster.create({ favoriteFood: 'Banana' }); + return { + needs: get(controller, 'needs'), + container: get(controller, 'container'), + unknownProperty: function(controllerName) { + var needs = this.needs; + var dependency, i, l; - hamster.get('wishList'); // 'Banana' - hamster.set('wishList', 'More Unit Tests'); - hamster.get('wishList'); // 'More Unit Tests' - hamster.get('favoriteFood'); // 'Banana' - ``` + for (i=0, l=needs.length; i 0) { + Ember.assert(' `' + inspect(this) + ' specifies `needs`, but does ' + + "not have a container. Please ensure this controller was " + + "instantiated with a container.", + this.container || meta(this, false).descs.controllers !== defaultControllersComputedProperty); - if (Ember.ENV) { - // do nothing if Ember.ENV is already setup - } else if ('undefined' !== typeof EmberENV) { - Ember.ENV = EmberENV; - } else if('undefined' !== typeof ENV) { - Ember.ENV = ENV; - } else { - Ember.ENV = {}; - } + if (this.container) { + verifyNeedsDependencies(this, this.container, needs); + } - Ember.config = Ember.config || {}; + // if needs then initialize controllers proxy + get(this, 'controllers'); + } - // We disable the RANGE API by default for performance reasons - if ('undefined' === typeof Ember.ENV.DISABLE_RANGE_API) { - Ember.ENV.DISABLE_RANGE_API = true; - } + this._super.apply(this, arguments); + }, - if ("undefined" === typeof MetamorphENV) { - exports.MetamorphENV = {}; - } + /** + @method controllerFor + @see {Ember.Route#controllerFor} + @deprecated Use `needs` instead + */ + controllerFor: function(controllerName) { + Ember.deprecate("Controller#controllerFor is deprecated, please use Controller#needs instead"); + return controllerFor(get(this, 'container'), controllerName); + }, - MetamorphENV.DISABLE_RANGE_API = Ember.ENV.DISABLE_RANGE_API; + /** + Stores the instances of other controllers available from within + this controller. Any controller listed by name in the `needs` + property will be accessible by name through this property. - /** - Hash of enabled Canary features. Add to before creating your application. + ```javascript + App.CommentsController = Ember.ArrayController.extend({ + needs: ['post'], + postTitle: function(){ + var currentPost = this.get('controllers.post'); // instance of App.PostController + return currentPost.get('title'); + }.property('controllers.post.title') + }); + ``` - You can also define `ENV.FEATURES` if you need to enable features flagged at runtime. + @see {Ember.ControllerMixin#needs} + @property {Object} controllers + @default null + */ + controllers: defaultControllersComputedProperty + }); - @class FEATURES - @namespace Ember - @static - @since 1.1.0 + __exports__["default"] = ControllerMixin; + }); +enifed("ember-application/system/application", + ["dag-map","container/container","ember-metal","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/lazy_load","ember-runtime/system/namespace","ember-runtime/mixins/deferred","ember-application/system/resolver","ember-metal/platform","ember-metal/run_loop","ember-metal/utils","ember-runtime/controllers/controller","ember-metal/enumerable_utils","ember-runtime/controllers/object_controller","ember-runtime/controllers/array_controller","ember-views/views/select","ember-views/system/event_dispatcher","ember-views/system/jquery","ember-routing/system/route","ember-routing/system/router","ember-routing/location/hash_location","ember-routing/location/history_location","ember-routing/location/auto_location","ember-routing/location/none_location","ember-routing/system/cache","ember-extension-support/container_debug_adapter","ember-metal/core","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__, __dependency22__, __dependency23__, __dependency24__, __dependency25__, __dependency26__, __dependency27__, __dependency28__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-application */ + var DAG = __dependency1__["default"]; + var Container = __dependency2__["default"]; - Ember.FEATURES = Ember.ENV.FEATURES || {}; - /** - Test that a feature is enabled. Parsed by Ember's build tools to leave - experimental features out of beta/stable builds. + var Ember = __dependency3__["default"]; + // Ember.FEATURES, Ember.deprecate, Ember.assert, Ember.libraries, LOG_VERSION, Namespace, BOOTED + var get = __dependency4__.get; + var set = __dependency5__.set; + var runLoadHooks = __dependency6__.runLoadHooks; + var Namespace = __dependency7__["default"]; + var DeferredMixin = __dependency8__["default"]; + var DefaultResolver = __dependency9__["default"]; + var create = __dependency10__.create; + var run = __dependency11__["default"]; + var canInvoke = __dependency12__.canInvoke; + var Controller = __dependency13__["default"]; + var EnumerableUtils = __dependency14__["default"]; + var ObjectController = __dependency15__["default"]; + var ArrayController = __dependency16__["default"]; + var SelectView = __dependency17__["default"]; + var EventDispatcher = __dependency18__["default"]; + var jQuery = __dependency19__["default"]; + var Route = __dependency20__["default"]; + var Router = __dependency21__["default"]; + var HashLocation = __dependency22__["default"]; + var HistoryLocation = __dependency23__["default"]; + var AutoLocation = __dependency24__["default"]; + var NoneLocation = __dependency25__["default"]; + var BucketCache = __dependency26__["default"]; - You can define the following configuration options: + // this is technically incorrect (per @wycats) + // it should work properly with: + // `import ContainerDebugAdapter from 'ember-extension-support/container_debug_adapter';` but + // es6-module-transpiler 0.4.0 eagerly grabs the module (which is undefined) - * `ENV.ENABLE_ALL_FEATURES` - force all features to be enabled. - * `ENV.ENABLE_OPTIONAL_FEATURES` - enable any features that have not been explicitly - enabled/disabled. + var ContainerDebugAdapter = __dependency27__["default"]; - @method isEnabled - @param {String} feature - @return {Boolean} - @for Ember.FEATURES - @since 1.1.0 - */ + var K = __dependency28__.K; - Ember.FEATURES.isEnabled = function(feature) { - var featureValue = Ember.FEATURES[feature]; + function props(obj) { + var properties = []; - if (Ember.ENV.ENABLE_ALL_FEATURES) { - return true; - } else if (featureValue === true || featureValue === false || featureValue === undefined) { - return featureValue; - } else if (Ember.ENV.ENABLE_OPTIONAL_FEATURES) { - return true; - } else { - return false; + for (var key in obj) { + properties.push(key); } - }; - - // .......................................................... - // BOOTSTRAP - // - - /** - Determines whether Ember should enhance some built-in object prototypes to - provide a more friendly API. If enabled, a few methods will be added to - `Function`, `String`, and `Array`. `Object.prototype` will not be enhanced, - which is the one that causes most trouble for people. - - In general we recommend leaving this option set to true since it rarely - conflicts with other code. If you need to turn it off however, you can - define an `ENV.EXTEND_PROTOTYPES` config to disable it. - - @property EXTEND_PROTOTYPES - @type Boolean - @default true - @for Ember - */ - Ember.EXTEND_PROTOTYPES = Ember.ENV.EXTEND_PROTOTYPES; - if (typeof Ember.EXTEND_PROTOTYPES === 'undefined') { - Ember.EXTEND_PROTOTYPES = true; + return properties; } - /** - Determines whether Ember logs a full stack trace during deprecation warnings - - @property LOG_STACKTRACE_ON_DEPRECATION - @type Boolean - @default true - */ - Ember.LOG_STACKTRACE_ON_DEPRECATION = (Ember.ENV.LOG_STACKTRACE_ON_DEPRECATION !== false); - - /** - Determines whether Ember should add ECMAScript 5 shims to older browsers. - - @property SHIM_ES5 - @type Boolean - @default Ember.EXTEND_PROTOTYPES - */ - Ember.SHIM_ES5 = (Ember.ENV.SHIM_ES5 === false) ? false : Ember.EXTEND_PROTOTYPES; + var librariesRegistered = false; /** - Determines whether Ember logs info about version of used libraries + An instance of `Ember.Application` is the starting point for every Ember + application. It helps to instantiate, initialize and coordinate the many + objects that make up your app. - @property LOG_VERSION - @type Boolean - @default true - */ - Ember.LOG_VERSION = (Ember.ENV.LOG_VERSION === false) ? false : true; + Each Ember app has one and only one `Ember.Application` object. In fact, the + very first thing you should do in your application is create the instance: - /** - Empty function. Useful for some operations. Always returns `this`. + ```javascript + window.App = Ember.Application.create(); + ``` - @method K - @private - @return {Object} - */ - Ember.K = function() { return this; }; + Typically, the application object is the only global variable. All other + classes in your app should be properties on the `Ember.Application` instance, + which highlights its first role: a global namespace. + For example, if you define a view class, it might look like this: - // Stub out the methods defined by the ember-debug package in case it's not loaded + ```javascript + App.MyView = Ember.View.extend(); + ``` - if ('undefined' === typeof Ember.assert) { Ember.assert = Ember.K; } - if ('undefined' === typeof Ember.warn) { Ember.warn = Ember.K; } - if ('undefined' === typeof Ember.debug) { Ember.debug = Ember.K; } - if ('undefined' === typeof Ember.runInDebug) { Ember.runInDebug = Ember.K; } - if ('undefined' === typeof Ember.deprecate) { Ember.deprecate = Ember.K; } - if ('undefined' === typeof Ember.deprecateFunc) { - Ember.deprecateFunc = function(_, func) { return func; }; - } + By default, calling `Ember.Application.create()` will automatically initialize + your application by calling the `Ember.Application.initialize()` method. If + you need to delay initialization, you can call your app's `deferReadiness()` + method. When you are ready for your app to be initialized, call its + `advanceReadiness()` method. - /** - Previously we used `Ember.$.uuid`, however `$.uuid` has been removed from - jQuery master. We'll just bootstrap our own uuid now. + You can define a `ready` method on the `Ember.Application` instance, which + will be run by Ember when the application is initialized. - @property uuid - @type Number - @private - */ - Ember.uuid = 0; + Because `Ember.Application` inherits from `Ember.Namespace`, any classes + you create will have useful string representations when calling `toString()`. + See the `Ember.Namespace` documentation for more information. - __exports__["default"] = Ember; - }); -define("ember-metal/enumerable_utils", - ["ember-metal/array","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var map, forEach, indexOf, splice, filter; + While you can think of your `Ember.Application` as a container that holds the + other classes in your application, there are several other responsibilities + going on under-the-hood that you may want to understand. - var map = __dependency1__.map; - var forEach = __dependency1__.forEach; - var indexOf = __dependency1__.indexOf; - var filter = __dependency1__.filter; + ### Event Delegation - // ES6TODO: doesn't array polyfills already do this? - map = Array.prototype.map || map; - forEach = Array.prototype.forEach || forEach; - indexOf = Array.prototype.indexOf || indexOf; - filter = Array.prototype.filter || filter; - splice = Array.prototype.splice; + Ember uses a technique called _event delegation_. This allows the framework + to set up a global, shared event listener instead of requiring each view to + do it manually. For example, instead of each view registering its own + `mousedown` listener on its associated element, Ember sets up a `mousedown` + listener on the `body`. - /** - * Defines some convenience methods for working with Enumerables. - * `Ember.EnumerableUtils` uses `Ember.ArrayPolyfills` when necessary. - * - * @class EnumerableUtils - * @namespace Ember - * @static - * */ - var utils = { - /** - * Calls the map function on the passed object with a specified callback. This - * uses `Ember.ArrayPolyfill`'s-map method when necessary. - * - * @method map - * @param {Object} obj The object that should be mapped - * @param {Function} callback The callback to execute - * @param {Object} thisArg Value to use as this when executing *callback* - * - * @return {Array} An array of mapped values. - */ - map: function(obj, callback, thisArg) { - return obj.map ? obj.map.call(obj, callback, thisArg) : map.call(obj, callback, thisArg); - }, + If a `mousedown` event occurs, Ember will look at the target of the event and + start walking up the DOM node tree, finding corresponding views and invoking + their `mouseDown` method as it goes. - /** - * Calls the forEach function on the passed object with a specified callback. This - * uses `Ember.ArrayPolyfill`'s-forEach method when necessary. - * - * @method forEach - * @param {Object} obj The object to call forEach on - * @param {Function} callback The callback to execute - * @param {Object} thisArg Value to use as this when executing *callback* - * - */ - forEach: function(obj, callback, thisArg) { - return obj.forEach ? obj.forEach.call(obj, callback, thisArg) : forEach.call(obj, callback, thisArg); - }, + `Ember.Application` has a number of default events that it listens for, as + well as a mapping from lowercase events to camel-cased view method names. For + example, the `keypress` event causes the `keyPress` method on the view to be + called, the `dblclick` event causes `doubleClick` to be called, and so on. - /** - * Calls the filter function on the passed object with a specified callback. This - * uses `Ember.ArrayPolyfill`'s-filter method when necessary. - * - * @method filter - * @param {Object} obj The object to call filter on - * @param {Function} callback The callback to execute - * @param {Object} thisArg Value to use as this when executing *callback* - * - * @return {Array} An array containing the filtered values - * @since 1.4.0 - */ - filter: function(obj, callback, thisArg) { - return obj.filter ? obj.filter.call(obj, callback, thisArg) : filter.call(obj, callback, thisArg); - }, + If there is a bubbling browser event that Ember does not listen for by + default, you can specify custom events and their corresponding view method + names by setting the application's `customEvents` property: - /** - * Calls the indexOf function on the passed object with a specified callback. This - * uses `Ember.ArrayPolyfill`'s-indexOf method when necessary. - * - * @method indexOf - * @param {Object} obj The object to call indexOn on - * @param {Function} callback The callback to execute - * @param {Object} index The index to start searching from - * - */ - indexOf: function(obj, element, index) { - return obj.indexOf ? obj.indexOf.call(obj, element, index) : indexOf.call(obj, element, index); - }, + ```javascript + var App = Ember.Application.create({ + customEvents: { + // add support for the paste event + paste: 'paste' + } + }); + ``` - /** - * Returns an array of indexes of the first occurrences of the passed elements - * on the passed object. - * - * ```javascript - * var array = [1, 2, 3, 4, 5]; - * Ember.EnumerableUtils.indexesOf(array, [2, 5]); // [1, 4] - * - * var fubar = "Fubarr"; - * Ember.EnumerableUtils.indexesOf(fubar, ['b', 'r']); // [2, 4] - * ``` - * - * @method indexesOf - * @param {Object} obj The object to check for element indexes - * @param {Array} elements The elements to search for on *obj* - * - * @return {Array} An array of indexes. - * - */ - indexesOf: function(obj, elements) { - return elements === undefined ? [] : utils.map(elements, function(item) { - return utils.indexOf(obj, item); - }); - }, + By default, the application sets up these event listeners on the document + body. However, in cases where you are embedding an Ember application inside + an existing page, you may want it to set up the listeners on an element + inside the body. - /** - * Adds an object to an array. If the array already includes the object this - * method has no effect. - * - * @method addObject - * @param {Array} array The array the passed item should be added to - * @param {Object} item The item to add to the passed array - * - * @return 'undefined' - */ - addObject: function(array, item) { - var index = utils.indexOf(array, item); - if (index === -1) { array.push(item); } - }, + For example, if only events inside a DOM element with the ID of `ember-app` + should be delegated, set your application's `rootElement` property: - /** - * Removes an object from an array. If the array does not contain the passed - * object this method has no effect. - * - * @method removeObject - * @param {Array} array The array to remove the item from. - * @param {Object} item The item to remove from the passed array. - * - * @return 'undefined' - */ - removeObject: function(array, item) { - var index = utils.indexOf(array, item); - if (index !== -1) { array.splice(index, 1); } - }, + ```javascript + var App = Ember.Application.create({ + rootElement: '#ember-app' + }); + ``` - _replace: function(array, idx, amt, objects) { - var args = [].concat(objects), chunk, ret = [], - // https://code.google.com/p/chromium/issues/detail?id=56588 - size = 60000, start = idx, ends = amt, count; + The `rootElement` can be either a DOM element or a jQuery-compatible selector + string. Note that *views appended to the DOM outside the root element will + not receive events.* If you specify a custom root element, make sure you only + append views inside it! - while (args.length) { - count = ends > size ? size : ends; - if (count <= 0) { count = 0; } + To learn more about the advantages of event delegation and the Ember view + layer, and a list of the event listeners that are setup by default, visit the + [Ember View Layer guide](http://emberjs.com/guides/understanding-ember/the-view-layer/#toc_event-delegation). - chunk = args.splice(0, size); - chunk = [start, count].concat(chunk); + ### Initializers - start += size; - ends -= count; + Libraries on top of Ember can add initializers, like so: - ret = ret.concat(splice.apply(array, chunk)); - } - return ret; - }, + ```javascript + Ember.Application.initializer({ + name: 'api-adapter', - /** - * Replaces objects in an array with the passed objects. - * - * ```javascript - * var array = [1,2,3]; - * Ember.EnumerableUtils.replace(array, 1, 2, [4, 5]); // [1, 4, 5] - * - * var array = [1,2,3]; - * Ember.EnumerableUtils.replace(array, 1, 1, [4, 5]); // [1, 4, 5, 3] - * - * var array = [1,2,3]; - * Ember.EnumerableUtils.replace(array, 10, 1, [4, 5]); // [1, 2, 3, 4, 5] - * ``` - * - * @method replace - * @param {Array} array The array the objects should be inserted into. - * @param {Number} idx Starting index in the array to replace. If *idx* >= - * length, then append to the end of the array. - * @param {Number} amt Number of elements that should be removed from the array, - * starting at *idx* - * @param {Array} objects An array of zero or more objects that should be - * inserted into the array at *idx* - * - * @return {Array} The modified array. - */ - replace: function(array, idx, amt, objects) { - if (array.replace) { - return array.replace(idx, amt, objects); - } else { - return utils._replace(array, idx, amt, objects); + initialize: function(container, application) { + application.register('api-adapter:main', ApiAdapter); } - }, + }); + ``` - /** - * Calculates the intersection of two arrays. This method returns a new array - * filled with the records that the two passed arrays share with each other. - * If there is no intersection, an empty array will be returned. - * - * ```javascript - * var array1 = [1, 2, 3, 4, 5]; - * var array2 = [1, 3, 5, 6, 7]; - * - * Ember.EnumerableUtils.intersection(array1, array2); // [1, 3, 5] - * - * var array1 = [1, 2, 3]; - * var array2 = [4, 5, 6]; - * - * Ember.EnumerableUtils.intersection(array1, array2); // [] - * ``` - * - * @method intersection - * @param {Array} array1 The first array - * @param {Array} array2 The second array - * - * @return {Array} The intersection of the two passed arrays. - */ - intersection: function(array1, array2) { - var intersection = []; + Initializers provide an opportunity to access the container, which + organizes the different components of an Ember application. Additionally + they provide a chance to access the instantiated application. Beyond + being used for libraries, initializers are also a great way to organize + dependency injection or setup in your own application. - utils.forEach(array1, function(element) { - if (utils.indexOf(array2, element) >= 0) { - intersection.push(element); - } - }); + ### Routing - return intersection; - } - }; + In addition to creating your application's router, `Ember.Application` is + also responsible for telling the router when to start routing. Transitions + between routes can be logged with the `LOG_TRANSITIONS` flag, and more + detailed intra-transition logging can be logged with + the `LOG_TRANSITIONS_INTERNAL` flag: - __exports__["default"] = utils; - }); -define("ember-metal/error", - ["ember-metal/platform","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var create = __dependency1__.create; + ```javascript + var App = Ember.Application.create({ + LOG_TRANSITIONS: true, // basic logging of successful transitions + LOG_TRANSITIONS_INTERNAL: true // detailed logging of all routing steps + }); + ``` - var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; + By default, the router will begin trying to translate the current URL into + application state once the browser emits the `DOMContentReady` event. If you + need to defer routing, you can call the application's `deferReadiness()` + method. Once routing can begin, call the `advanceReadiness()` method. - /** - A subclass of the JavaScript Error object for use in Ember. + If there is any setup required before routing begins, you can implement a + `ready()` method on your app that will be invoked immediately before routing + begins. + ``` - @class Error + @class Application @namespace Ember - @extends Error - @constructor + @extends Ember.Namespace */ - var EmberError = function() { - var tmp = Error.apply(this, arguments); - // Adds a `stack` property to the given error object that will yield the - // stack trace at the time captureStackTrace was called. - // When collecting the stack trace all frames above the topmost call - // to this function, including that call, will be left out of the - // stack trace. - // This is useful because we can hide Ember implementation details - // that are not very helpful for the user. - if (Error.captureStackTrace) { - Error.captureStackTrace(this, Ember.Error); - } - // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. - for (var idx = 0; idx < errorProps.length; idx++) { - this[errorProps[idx]] = tmp[errorProps[idx]]; - } - }; + var Application = Namespace.extend(DeferredMixin, { + _suppressDeferredDeprecation: true, - EmberError.prototype = create(Error.prototype); + /** + The root DOM element of the Application. This can be specified as an + element or a + [jQuery-compatible selector string](http://api.jquery.com/category/selectors/). - __exports__["default"] = EmberError; - }); -define("ember-metal/events", - ["ember-metal/core","ember-metal/utils","ember-metal/platform","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - /** - @module ember-metal - */ - var Ember = __dependency1__["default"]; - var meta = __dependency2__.meta; - var META_KEY = __dependency2__.META_KEY; - var tryFinally = __dependency2__.tryFinally; - var apply = __dependency2__.apply; - var applyStr = __dependency2__.applyStr; - var create = __dependency3__.create; + This is the element that will be passed to the Application's, + `eventDispatcher`, which sets up the listeners for event delegation. Every + view in your application should be a child of the element you specify here. - var a_slice = [].slice, - metaFor = meta, - /* listener flags */ - ONCE = 1, SUSPENDED = 2; + @property rootElement + @type DOMElement + @default 'body' + */ + rootElement: 'body', + /** + The `Ember.EventDispatcher` responsible for delegating events to this + application's views. - /* - The event system uses a series of nested hashes to store listeners on an - object. When a listener is registered, or when an event arrives, these - hashes are consulted to determine which target and action pair to invoke. + The event dispatcher is created by the application at initialization time + and sets up event listeners on the DOM element described by the + application's `rootElement` property. - The hashes are stored in the object's meta hash, and look like this: + See the documentation for `Ember.EventDispatcher` for more information. - // Object's meta hash - { - listeners: { // variable name: `listenerSet` - "foo:changed": [ // variable name: `actions` - target, method, flags - ] - } - } + @property eventDispatcher + @type Ember.EventDispatcher + @default null + */ + eventDispatcher: null, - */ + /** + The DOM events for which the event dispatcher should listen. - function indexOf(array, target, method) { - var index = -1; - // hashes are added to the end of the event array - // so it makes sense to start searching at the end - // of the array and search in reverse - for (var i = array.length - 3 ; i >=0; i -= 3) { - if (target === array[i] && method === array[i + 1]) { - index = i; break; - } - } - return index; - } + By default, the application's `Ember.EventDispatcher` listens + for a set of standard DOM events, such as `mousedown` and + `keyup`, and delegates them to your application's `Ember.View` + instances. - function actionsFor(obj, eventName) { - var meta = metaFor(obj, true), - actions; + If you would like additional bubbling events to be delegated to your + views, set your `Ember.Application`'s `customEvents` property + to a hash containing the DOM event name as the key and the + corresponding view method name as the value. For example: - if (!meta.listeners) { meta.listeners = {}; } + ```javascript + var App = Ember.Application.create({ + customEvents: { + // add support for the paste event + paste: 'paste' + } + }); + ``` - if (!meta.hasOwnProperty('listeners')) { - // setup inherited copy of the listeners object - meta.listeners = create(meta.listeners); - } + @property customEvents + @type Object + @default null + */ + customEvents: null, - actions = meta.listeners[eventName]; + init: function() { + // Start off the number of deferrals at 1. This will be + // decremented by the Application's own `initialize` method. + this._readinessDeferrals = 1; - // if there are actions, but the eventName doesn't exist in our listeners, then copy them from the prototype - if (actions && !meta.listeners.hasOwnProperty(eventName)) { - actions = meta.listeners[eventName] = meta.listeners[eventName].slice(); - } else if (!actions) { - actions = meta.listeners[eventName] = []; - } + if (!this.$) { + this.$ = jQuery; + } + this.__container__ = this.buildContainer(); - return actions; - } + this.Router = this.defaultRouter(); - function listenersUnion(obj, eventName, otherActions) { - var meta = obj[META_KEY], - actions = meta && meta.listeners && meta.listeners[eventName]; + this._super(); - if (!actions) { return; } - for (var i = actions.length - 3; i >= 0; i -= 3) { - var target = actions[i], - method = actions[i+1], - flags = actions[i+2], - actionIndex = indexOf(otherActions, target, method); + this.scheduleInitialize(); - if (actionIndex === -1) { - otherActions.push(target, method, flags); + if (!librariesRegistered) { + librariesRegistered = true; + Ember.libraries.registerCoreLibrary('jQuery', jQuery().jquery); } - } - } - - function listenersDiff(obj, eventName, otherActions) { - var meta = obj[META_KEY], - actions = meta && meta.listeners && meta.listeners[eventName], - diffActions = []; - if (!actions) { return; } - for (var i = actions.length - 3; i >= 0; i -= 3) { - var target = actions[i], - method = actions[i+1], - flags = actions[i+2], - actionIndex = indexOf(otherActions, target, method); + if (Ember.LOG_VERSION) { + // we only need to see this once per Application#init + Ember.LOG_VERSION = false; + var libs = Ember.libraries._registry; - if (actionIndex !== -1) { continue; } - - otherActions.push(target, method, flags); - diffActions.push(target, method, flags); - } + var nameLengths = EnumerableUtils.map(libs, function(item) { + return get(item, 'name.length'); + }); - return diffActions; - } + var maxNameLength = Math.max.apply(this, nameLengths); - /** - Add an event listener + Ember.debug('-------------------------------'); + for (var i = 0, l = libs.length; i < l; i++) { + var lib = libs[i]; + var spaces = new Array(maxNameLength - lib.name.length + 1).join(' '); + Ember.debug([lib.name, spaces, ' : ', lib.version].join('')); + } + Ember.debug('-------------------------------'); + } + }, - @method addListener - @for Ember - @param obj - @param {String} eventName - @param {Object|Function} targetOrMethod A target object or a function - @param {Function|String} method A function or the name of a function to be called on `target` - @param {Boolean} once A flag whether a function should only be called once - */ - function addListener(obj, eventName, target, method, once) { - Ember.assert("You must pass at least an object and event name to Ember.addListener", !!obj && !!eventName); + /** + Build the container for the current application. - if (!method && 'function' === typeof target) { - method = target; - target = null; - } + Also register a default application view in case the application + itself does not. - var actions = actionsFor(obj, eventName), - actionIndex = indexOf(actions, target, method), - flags = 0; + @private + @method buildContainer + @return {Ember.Container} the configured container + */ + buildContainer: function() { + var container = this.__container__ = Application.buildContainer(this); - if (once) flags |= ONCE; + return container; + }, - if (actionIndex !== -1) { return; } + /** + If the application has not opted out of routing and has not explicitly + defined a router, supply a default router for the application author + to configure. - actions.push(target, method, flags); + This allows application developers to do: - if ('function' === typeof obj.didAddListener) { - obj.didAddListener(eventName, target, method); - } - } + ```javascript + var App = Ember.Application.create(); - /** - Remove an event listener + App.Router.map(function() { + this.resource('posts'); + }); + ``` - Arguments should match those passed to `Ember.addListener`. + @private + @method defaultRouter + @return {Ember.Router} the default router + */ - @method removeListener - @for Ember - @param obj - @param {String} eventName - @param {Object|Function} targetOrMethod A target object or a function - @param {Function|String} method A function or the name of a function to be called on `target` - */ - function removeListener(obj, eventName, target, method) { - Ember.assert("You must pass at least an object and event name to Ember.removeListener", !!obj && !!eventName); + defaultRouter: function() { + if (this.Router === false) { return; } + var container = this.__container__; - if (!method && 'function' === typeof target) { - method = target; - target = null; - } + if (this.Router) { + container.unregister('router:main'); + container.register('router:main', this.Router); + } - function _removeListener(target, method) { - var actions = actionsFor(obj, eventName), - actionIndex = indexOf(actions, target, method); + return container.lookupFactory('router:main'); + }, - // action doesn't exist, give up silently - if (actionIndex === -1) { return; } + /** + Automatically initialize the application once the DOM has + become ready. - actions.splice(actionIndex, 3); + The initialization itself is scheduled on the actions queue + which ensures that application loading finishes before + booting. - if ('function' === typeof obj.didRemoveListener) { - obj.didRemoveListener(eventName, target, method); + If you are asynchronously loading code, you should call + `deferReadiness()` to defer booting, and then call + `advanceReadiness()` once all of your code has finished + loading. + + @private + @method scheduleInitialize + */ + scheduleInitialize: function() { + if (!this.$ || this.$.isReady) { + run.schedule('actions', this, '_initialize'); + } else { + this.$().ready(Ember.run.bind(this, '_initialize')); } - } + }, - if (method) { - _removeListener(target, method); - } else { - var meta = obj[META_KEY], - actions = meta && meta.listeners && meta.listeners[eventName]; + /** + Use this to defer readiness until some condition is true. - if (!actions) { return; } - for (var i = actions.length - 3; i >= 0; i -= 3) { - _removeListener(actions[i], actions[i+1]); - } - } - } + Example: - /** - Suspend listener during callback. + ```javascript + var App = Ember.Application.create(); + + App.deferReadiness(); + // Ember.$ is a reference to the jQuery object/function + Ember.$.getJSON('/auth-token', function(token) { + App.token = token; + App.advanceReadiness(); + }); + ``` - This should only be used by the target of the event listener - when it is taking an action that would cause the event, e.g. - an object might suspend its property change listener while it is - setting that property. + This allows you to perform asynchronous setup logic and defer + booting your application until the setup has finished. - @method suspendListener - @for Ember + However, if the setup requires a loading UI, it might be better + to use the router for this purpose. - @private - @param obj - @param {String} eventName - @param {Object|Function} targetOrMethod A target object or a function - @param {Function|String} method A function or the name of a function to be called on `target` - @param {Function} callback - */ - function suspendListener(obj, eventName, target, method, callback) { - if (!method && 'function' === typeof target) { - method = target; - target = null; - } + @method deferReadiness + */ + deferReadiness: function() { + Ember.assert("You must call deferReadiness on an instance of Ember.Application", this instanceof Application); + Ember.assert("You cannot defer readiness since the `ready()` hook has already been called.", this._readinessDeferrals > 0); + this._readinessDeferrals++; + }, - var actions = actionsFor(obj, eventName), - actionIndex = indexOf(actions, target, method); + /** + Call `advanceReadiness` after any asynchronous setup logic has completed. + Each call to `deferReadiness` must be matched by a call to `advanceReadiness` + or the application will never become ready and routing will not begin. - if (actionIndex !== -1) { - actions[actionIndex+2] |= SUSPENDED; // mark the action as suspended - } + @method advanceReadiness + @see {Ember.Application#deferReadiness} + */ + advanceReadiness: function() { + Ember.assert("You must call advanceReadiness on an instance of Ember.Application", this instanceof Application); + this._readinessDeferrals--; - function tryable() { return callback.call(target); } - function finalizer() { if (actionIndex !== -1) { actions[actionIndex+2] &= ~SUSPENDED; } } + if (this._readinessDeferrals === 0) { + run.once(this, this.didBecomeReady); + } + }, - return tryFinally(tryable, finalizer); - } + /** + Registers a factory that can be used for dependency injection (with + `App.inject`) or for service lookup. Each factory is registered with + a full name including two parts: `type:name`. - /** - Suspends multiple listeners during a callback. + A simple example: - @method suspendListeners - @for Ember + ```javascript + var App = Ember.Application.create(); + + App.Orange = Ember.Object.extend(); + App.register('fruit:favorite', App.Orange); + ``` - @private - @param obj - @param {Array} eventName Array of event names - @param {Object|Function} targetOrMethod A target object or a function - @param {Function|String} method A function or the name of a function to be called on `target` - @param {Function} callback - */ - function suspendListeners(obj, eventNames, target, method, callback) { - if (!method && 'function' === typeof target) { - method = target; - target = null; - } + Ember will resolve factories from the `App` namespace automatically. + For example `App.CarsController` will be discovered and returned if + an application requests `controller:cars`. - var suspendedActions = [], - actionsList = [], - eventName, actions, i, l; + An example of registering a controller with a non-standard name: - for (i=0, l=eventNames.length; i= 0; i -= 3) { // looping in reverse for once listeners - var target = actions[i], method = actions[i+1], flags = actions[i+2]; - if (!method) { continue; } - if (flags & SUSPENDED) { continue; } - if (flags & ONCE) { removeListener(obj, eventName, target, method); } - if (!target) { target = obj; } - if ('string' === typeof method) { - if (params) { - applyStr(target, method, params); - } else { - target[method](); - } - } else { - if (params) { - apply(target, method, params); - } else { - method.call(target); - } - } - } - return true; - } + ```javascript + var App = Ember.Application.create(); + var Session = Ember.Object.extend({ isAuthenticated: false }); - /** - @private - @method hasListeners - @for Ember - @param obj - @param {String} eventName - */ - function hasListeners(obj, eventName) { - var meta = obj[META_KEY], - actions = meta && meta.listeners && meta.listeners[eventName]; + // A factory must be registered before it can be injected + App.register('session:main', Session); - return !!(actions && actions.length); - } + // Inject 'session:main' onto all factories of the type 'controller' + // with the name 'session' + App.inject('controller', 'session', 'session:main'); - /** - @private - @method listenersFor - @for Ember - @param obj - @param {String} eventName - */ - function listenersFor(obj, eventName) { - var ret = []; - var meta = obj[META_KEY], - actions = meta && meta.listeners && meta.listeners[eventName]; + App.IndexController = Ember.Controller.extend({ + isLoggedIn: Ember.computed.alias('session.isAuthenticated') + }); + ``` - if (!actions) { return ret; } + Injections can also be performed on specific factories. - for (var i = 0, l = actions.length; i < l; i += 3) { - var target = actions[i], - method = actions[i+1]; - ret.push([target, method]); - } + ```javascript + App.inject(, , ) + App.inject('route', 'source', 'source:main') + App.inject('route:application', 'email', 'model:email') + ``` - return ret; - } + It is important to note that injections can only be performed on + classes that are instantiated by Ember itself. Instantiating a class + directly (via `create` or `new`) bypasses the dependency injection + system. - /** - Define a property as a function that should be executed when - a specified event or events are triggered. + **Note:** Ember-Data instantiates its models in a unique manner, and consequently + injections onto models (or all models) will not work as expected. Injections + on models can be enabled by setting `Ember.MODEL_FACTORY_INJECTIONS` + to `true`. + @method inject + @param factoryNameOrType {String} + @param property {String} + @param injectionName {String} + **/ + inject: function() { + var container = this.__container__; + container.injection.apply(container, arguments); + }, - ``` javascript - var Job = Ember.Object.extend({ - logCompleted: Ember.on('completed', function() { - console.log('Job completed!'); - }) - }); + /** + Calling initialize manually is not supported. - var job = Job.create(); + Please see Ember.Application#advanceReadiness and + Ember.Application#deferReadiness. - Ember.sendEvent(job, 'completed'); // Logs 'Job completed!' - ``` + @private + @deprecated + @method initialize + **/ + initialize: function() { + Ember.deprecate('Calling initialize manually is not supported. Please see Ember.Application#advanceReadiness and Ember.Application#deferReadiness'); + }, - @method on - @for Ember - @param {String} eventNames* - @param {Function} func - @return func - */ - function on(){ - var func = a_slice.call(arguments, -1)[0], - events = a_slice.call(arguments, 0, -1); - func.__ember_listens__ = events; - return func; - }; + /** + Initialize the application. This happens automatically. - __exports__.on = on; - __exports__.addListener = addListener; - __exports__.removeListener = removeListener; - __exports__.suspendListener = suspendListener; - __exports__.suspendListeners = suspendListeners; - __exports__.sendEvent = sendEvent; - __exports__.hasListeners = hasListeners; - __exports__.watchedEvents = watchedEvents; - __exports__.listenersFor = listenersFor; - __exports__.listenersDiff = listenersDiff; - __exports__.listenersUnion = listenersUnion; - }); -define("ember-metal/expand_properties", - ["ember-metal/error","ember-metal/enumerable_utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var EmberError = __dependency1__["default"]; - var EnumerableUtils = __dependency2__["default"]; + Run any initializers and run the application load hook. These hooks may + choose to defer readiness. For example, an authentication hook might want + to defer readiness until the auth token has been retrieved. - /** - @module ember-metal + @private + @method _initialize */ + _initialize: function() { + if (this.isDestroyed) { return; } - var forEach = EnumerableUtils.forEach, - BRACE_EXPANSION = /^((?:[^\.]*\.)*)\{(.*)\}$/; + // At this point, the App.Router must already be assigned + if (this.Router) { + var container = this.__container__; + container.unregister('router:main'); + container.register('router:main', this.Router); + } - /** - Expands `pattern`, invoking `callback` for each expansion. + this.runInitializers(); + runLoadHooks('application', this); - The only pattern supported is brace-expansion, anything else will be passed - once to `callback` directly. Brace expansion can only appear at the end of a - pattern, for an example see the last call below. + // At this point, any initializers or load hooks that would have wanted + // to defer readiness have fired. In general, advancing readiness here + // will proceed to didBecomeReady. + this.advanceReadiness(); - Example - ```js - function echo(arg){ console.log(arg); } + return this; + }, - Ember.expandProperties('foo.bar', echo); //=> 'foo.bar' - Ember.expandProperties('{foo,bar}', echo); //=> 'foo', 'bar' - Ember.expandProperties('foo.{bar,baz}', echo); //=> 'foo.bar', 'foo.baz' - Ember.expandProperties('{foo,bar}.baz', echo); //=> '{foo,bar}.baz' - ``` + /** + Reset the application. This is typically used only in tests. It cleans up + the application in the following order: - @method - @private - @param {string} pattern The property pattern to expand. - @param {function} callback The callback to invoke. It is invoked once per - expansion, and is passed the expansion. - */ - function expandProperties(pattern, callback) { - var match, prefix, list; + 1. Deactivate existing routes + 2. Destroy all objects in the container + 3. Create a new application container + 4. Re-route to the existing url - if (pattern.indexOf(' ') > -1) { - throw new EmberError('Brace expanded properties cannot contain spaces, ' + - 'e.g. `user.{firstName, lastName}` should be `user.{firstName,lastName}`'); - } + Typical Example: - if (match = BRACE_EXPANSION.exec(pattern)) { - prefix = match[1]; - list = match[2]; + ```javascript + var App; - forEach(list.split(','), function (suffix) { - callback(prefix + suffix); + run(function() { + App = Ember.Application.create(); }); - } else { - callback(pattern); - } - }; - __exports__["default"] = expandProperties; - }); -define("ember-metal/get_properties", - ["ember-metal/property_get","ember-metal/utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var get = __dependency1__.get; - var typeOf = __dependency2__.typeOf; + module('acceptance test', { + setup: function() { + App.reset(); + } + }); - /** - To get multiple properties at once, call `Ember.getProperties` - with an object followed by a list of strings or an array: + test('first test', function() { + // App is freshly reset + }); - ```javascript - Ember.getProperties(record, 'firstName', 'lastName', 'zipCode'); - // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } - ``` + test('second test', function() { + // App is again freshly reset + }); + ``` - is equivalent to: + Advanced Example: - ```javascript - Ember.getProperties(record, ['firstName', 'lastName', 'zipCode']); - // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } - ``` + Occasionally you may want to prevent the app from initializing during + setup. This could enable extra configuration, or enable asserting prior + to the app becoming ready. - @method getProperties - @param obj - @param {String...|Array} list of keys to get - @return {Hash} - */ - function getProperties(obj) { - var ret = {}, - propertyNames = arguments, - i = 1; + ```javascript + var App; - if (arguments.length === 2 && typeOf(arguments[1]) === 'array') { - i = 0; - propertyNames = arguments[1]; - } - for(var len = propertyNames.length; i < len; i++) { - ret[propertyNames[i]] = get(obj, propertyNames[i]); - } - return ret; - }; - - __exports__["default"] = getProperties; - }); -define("ember-metal/instrumentation", - ["ember-metal/core","ember-metal/utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - var tryCatchFinally = __dependency2__.tryCatchFinally; + run(function() { + App = Ember.Application.create(); + }); - /** - The purpose of the Ember Instrumentation module is - to provide efficient, general-purpose instrumentation - for Ember. + module('acceptance test', { + setup: function() { + run(function() { + App.reset(); + App.deferReadiness(); + }); + } + }); - Subscribe to a listener by using `Ember.subscribe`: + test('first test', function() { + ok(true, 'something before app is initialized'); - ```javascript - Ember.subscribe("render", { - before: function(name, timestamp, payload) { + run(function() { + App.advanceReadiness(); + }); + + ok(true, 'something after app is initialized'); + }); + ``` - }, + @method reset + **/ + reset: function() { + this._readinessDeferrals = 1; - after: function(name, timestamp, payload) { + function handleReset() { + var router = this.__container__.lookup('router:main'); + router.reset(); - } - }); - ``` + run(this.__container__, 'destroy'); - If you return a value from the `before` callback, that same - value will be passed as a fourth parameter to the `after` - callback. + this.buildContainer(); - Instrument a block of code by using `Ember.instrument`: + run.schedule('actions', this, '_initialize'); + } - ```javascript - Ember.instrument("render.handlebars", payload, function() { - // rendering logic - }, binding); - ``` + run.join(this, handleReset); + }, - Event names passed to `Ember.instrument` are namespaced - by periods, from more general to more specific. Subscribers - can listen for events by whatever level of granularity they - are interested in. + /** + @private + @method runInitializers + */ + runInitializers: function() { + var initializersByName = get(this.constructor, 'initializers'); + var initializers = props(initializersByName); + var container = this.__container__; + var graph = new DAG(); + var namespace = this; + var initializer; - In the above example, the event is `render.handlebars`, - and the subscriber listened for all events beginning with - `render`. It would receive callbacks for events named - `render`, `render.handlebars`, `render.container`, or - even `render.handlebars.layout`. + for (var i = 0; i < initializers.length; i++) { + initializer = initializersByName[initializers[i]]; + graph.addEdges(initializer.name, initializer.initialize, initializer.before, initializer.after); + } - @class Instrumentation - @namespace Ember - @static - */ - var subscribers = [], cache = {}; + graph.topsort(function (vertex) { + var initializer = vertex.value; + Ember.assert("No application initializer named '" + vertex.name + "'", !!initializer); + initializer(container, namespace); + }); + }, - var populateListeners = function(name) { - var listeners = [], subscriber; + /** + @private + @method didBecomeReady + */ + didBecomeReady: function() { + this.setupEventDispatcher(); + this.ready(); // user hook + this.startRouting(); - for (var i=0, l=subscribers.length; i Ember.TEMPLATES['post'] + 'template:posts/byline' //=> Ember.TEMPLATES['posts/byline'] + 'template:posts.byline' //=> Ember.TEMPLATES['posts/byline'] + 'template:blogPost' //=> Ember.TEMPLATES['blogPost'] + // OR + // Ember.TEMPLATES['blog_post'] + 'controller:post' //=> App.PostController + 'controller:posts.index' //=> App.PostsIndexController + 'controller:blog/post' //=> Blog.PostController + 'controller:basic' //=> Ember.Controller + 'route:post' //=> App.PostRoute + 'route:posts.index' //=> App.PostsIndexRoute + 'route:blog/post' //=> Blog.PostRoute + 'route:basic' //=> Ember.Route + 'view:post' //=> App.PostView + 'view:posts.index' //=> App.PostsIndexView + 'view:blog/post' //=> Blog.PostView + 'view:basic' //=> Ember.View + 'foo:post' //=> App.PostFoo + 'model:post' //=> App.Post + ``` - __exports__["default"] = Ember; - }); -define("ember-metal/map", - ["ember-metal/property_set","ember-metal/utils","ember-metal/array","ember-metal/platform","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - /** - @module ember-metal + @class DefaultResolver + @namespace Ember + @extends Ember.Object */ + var dictionary = __dependency8__["default"]; - /* - JavaScript (before ES6) does not have a Map implementation. Objects, - which are often used as dictionaries, may only have Strings as keys. + __exports__["default"] = EmberObject.extend({ + /** + This will be set to the Application instance when it is + created. - Because Ember has a way to get a unique identifier for every object - via `Ember.guidFor`, we can implement a performant Map with arbitrary - keys. Because it is commonly used in low-level bookkeeping, Map is - implemented as a pure JavaScript object for performance. + @property namespace + */ + namespace: null, - This implementation follows the current iteration of the ES6 proposal for - maps (http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets), - with two exceptions. First, because we need our implementation to be pleasant - on older browsers, we do not use the `delete` name (using `remove` instead). - Second, as we do not have the luxury of in-VM iteration, we implement a - forEach method for iteration. + init: function() { + this._parseNameCache = dictionary(null); + }, + normalize: function(fullName) { + var split = fullName.split(':', 2); + var type = split[0]; + var name = split[1]; - Map is mocked out to look like an Ember object, so you can do - `Ember.Map.create()` for symmetry with other Ember classes. - */ + Ember.assert("Tried to normalize a container name without a colon (:) in it." + + " You probably tried to lookup a name that did not contain a type," + + " a colon, and a name. A proper lookup name would be `view:post`.", split.length === 2); - var set = __dependency1__.set; - var guidFor = __dependency2__.guidFor; - var indexOf = __dependency3__.indexOf;var create = __dependency4__.create; + if (type !== 'template') { + var result = name; - var copy = function(obj) { - var output = {}; + if (result.indexOf('.') > -1) { + result = result.replace(/\.(.)/g, function(m) { + return m.charAt(1).toUpperCase(); + }); + } - for (var prop in obj) { - if (obj.hasOwnProperty(prop)) { output[prop] = obj[prop]; } - } + if (name.indexOf('_') > -1) { + result = result.replace(/_(.)/g, function(m) { + return m.charAt(1).toUpperCase(); + }); + } - return output; - }; + return type + ':' + result; + } else { + return fullName; + } + }, - var copyMap = function(original, newObject) { - var keys = original.keys.copy(), - values = copy(original.values); - newObject.keys = keys; - newObject.values = values; - newObject.length = original.length; + /** + This method is called via the container's resolver method. + It parses the provided `fullName` and then looks up and + returns the appropriate template or class. - return newObject; - }; + @method resolve + @param {String} fullName the lookup string + @return {Object} the resolved factory + */ + resolve: function(fullName) { + var parsedName = this.parseName(fullName); + var resolveMethodName = parsedName.resolveMethodName; + var resolved; - /** - This class is used internally by Ember and Ember Data. - Please do not use it at this time. We plan to clean it up - and add many tests soon. + if (!(parsedName.name && parsedName.type)) { + throw new TypeError('Invalid fullName: `' + fullName + '`, must be of the form `type:name` '); + } - @class OrderedSet - @namespace Ember - @constructor - @private - */ - function OrderedSet() { - this.clear(); - }; + if (this[resolveMethodName]) { + resolved = this[resolveMethodName](parsedName); + } - /** - @method create - @static - @return {Ember.OrderedSet} - */ - OrderedSet.create = function() { - return new OrderedSet(); - }; + if (!resolved) { + resolved = this.resolveOther(parsedName); + } + if (parsedName.root && parsedName.root.LOG_RESOLVER) { + this._logLookup(resolved, parsedName); + } - OrderedSet.prototype = { + return resolved; + }, /** - @method clear + Convert the string name of the form 'type:name' to + a Javascript object with the parsed aspects of the name + broken out. + + @protected + @param {String} fullName the lookup string + @method parseName */ - clear: function() { - this.presenceSet = {}; - this.list = []; + + parseName: function(fullName) { + return this._parseNameCache[fullName] || ( + this._parseNameCache[fullName] = this._parseName(fullName) + ); + }, + + _parseName: function(fullName) { + var nameParts = fullName.split(':'); + var type = nameParts[0], fullNameWithoutType = nameParts[1]; + var name = fullNameWithoutType; + var namespace = get(this, 'namespace'); + var root = namespace; + + if (type !== 'template' && name.indexOf('/') !== -1) { + var parts = name.split('/'); + name = parts[parts.length - 1]; + var namespaceName = capitalize(parts.slice(0, -1).join('.')); + root = Namespace.byName(namespaceName); + + Ember.assert('You are looking for a ' + name + ' ' + type + + ' in the ' + namespaceName + + ' namespace, but the namespace could not be found', root); + } + + return { + fullName: fullName, + type: type, + fullNameWithoutType: fullNameWithoutType, + name: name, + root: root, + resolveMethodName: 'resolve' + classify(type) + }; }, /** - @method add - @param obj + Returns a human-readable description for a fullName. Used by the + Application namespace in assertions to describe the + precise name of the class that Ember is looking for, rather than + container keys. + + @protected + @param {String} fullName the lookup string + @method lookupDescription */ - add: function(obj) { - var guid = guidFor(obj), - presenceSet = this.presenceSet, - list = this.list; + lookupDescription: function(fullName) { + var parsedName = this.parseName(fullName); + + if (parsedName.type === 'template') { + return 'template at ' + parsedName.fullNameWithoutType.replace(/\./g, '/'); + } - if (guid in presenceSet) { return; } + var description = parsedName.root + '.' + classify(parsedName.name); - presenceSet[guid] = true; - list.push(obj); + if (parsedName.type !== 'model') { + description += classify(parsedName.type); + } + + return description; }, + makeToString: function(factory, fullName) { + return factory.toString(); + }, /** - @method remove - @param obj + Given a parseName object (output from `parseName`), apply + the conventions expected by `Ember.Router` + + @protected + @param {Object} parsedName a parseName object with the parsed + fullName lookup string + @method useRouterNaming + */ + useRouterNaming: function(parsedName) { + parsedName.name = parsedName.name.replace(/\./g, '_'); + if (parsedName.name === 'basic') { + parsedName.name = ''; + } + }, + /** + Look up the template in Ember.TEMPLATES + + @protected + @param {Object} parsedName a parseName object with the parsed + fullName lookup string + @method resolveTemplate */ - remove: function(obj) { - var guid = guidFor(obj), - presenceSet = this.presenceSet, - list = this.list; + resolveTemplate: function(parsedName) { + var templateName = parsedName.fullNameWithoutType.replace(/\./g, '/'); - delete presenceSet[guid]; + if (Ember.TEMPLATES[templateName]) { + return Ember.TEMPLATES[templateName]; + } - var index = indexOf.call(list, obj); - if (index > -1) { - list.splice(index, 1); + templateName = decamelize(templateName); + if (Ember.TEMPLATES[templateName]) { + return Ember.TEMPLATES[templateName]; } }, /** - @method isEmpty - @return {Boolean} + Lookup the view using `resolveOther` + + @protected + @param {Object} parsedName a parseName object with the parsed + fullName lookup string + @method resolveView */ - isEmpty: function() { - return this.list.length === 0; + resolveView: function(parsedName) { + this.useRouterNaming(parsedName); + return this.resolveOther(parsedName); }, /** - @method has - @param obj - @return {Boolean} + Lookup the controller using `resolveOther` + + @protected + @param {Object} parsedName a parseName object with the parsed + fullName lookup string + @method resolveController */ - has: function(obj) { - var guid = guidFor(obj), - presenceSet = this.presenceSet; + resolveController: function(parsedName) { + this.useRouterNaming(parsedName); + return this.resolveOther(parsedName); + }, + /** + Lookup the route using `resolveOther` - return guid in presenceSet; + @protected + @param {Object} parsedName a parseName object with the parsed + fullName lookup string + @method resolveRoute + */ + resolveRoute: function(parsedName) { + this.useRouterNaming(parsedName); + return this.resolveOther(parsedName); }, /** - @method forEach - @param {Function} fn - @param self + Lookup the model on the Application namespace + + @protected + @param {Object} parsedName a parseName object with the parsed + fullName lookup string + @method resolveModel */ - forEach: function(fn, self) { - // allow mutation during iteration - var list = this.toArray(); + resolveModel: function(parsedName) { + var className = classify(parsedName.name); + var factory = get(parsedName.root, className); - for (var i = 0, j = list.length; i < j; i++) { - fn.call(self, list[i]); - } + if (factory) { return factory; } }, + /** + Look up the specified object (from parsedName) on the appropriate + namespace (usually on the Application) + @protected + @param {Object} parsedName a parseName object with the parsed + fullName lookup string + @method resolveHelper + */ + resolveHelper: function(parsedName) { + return this.resolveOther(parsedName) || helpers[parsedName.fullNameWithoutType]; + }, /** - @method toArray - @return {Array} + Look up the specified object (from parsedName) on the appropriate + namespace (usually on the Application) + + @protected + @param {Object} parsedName a parseName object with the parsed + fullName lookup string + @method resolveOther */ - toArray: function() { - return this.list.slice(); + resolveOther: function(parsedName) { + var className = classify(parsedName.name) + classify(parsedName.type); + var factory = get(parsedName.root, className); + if (factory) { return factory; } }, /** - @method copy - @return {Ember.OrderedSet} + @method _logLookup + @param {Boolean} found + @param {Object} parsedName + @private */ - copy: function() { - var set = new OrderedSet(); + _logLookup: function(found, parsedName) { + var symbol, padding; - set.presenceSet = copy(this.presenceSet); - set.list = this.toArray(); + if (found) { symbol = '[✓]'; } + else { symbol = '[ ]'; } - return set; - } - }; - - /** - A Map stores values indexed by keys. Unlike JavaScript's - default Objects, the keys of a Map can be any JavaScript - object. + if (parsedName.fullName.length > 60) { + padding = '.'; + } else { + padding = new Array(60 - parsedName.fullName.length).join('.'); + } - Internally, a Map has two data structures: + Logger.info(symbol, parsedName.fullName, padding, this.lookupDescription(parsedName.fullName)); + } + }); + }); +enifed("ember-debug", + ["ember-metal/core","ember-metal/error","ember-metal/logger","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + /*global __fail__*/ - 1. `keys`: an OrderedSet of all of the existing keys - 2. `values`: a JavaScript Object indexed by the `Ember.guidFor(key)` + var Ember = __dependency1__["default"]; + var EmberError = __dependency2__["default"]; + var Logger = __dependency3__["default"]; - When a key/value pair is added for the first time, we - add the key to the `keys` OrderedSet, and create or - replace an entry in `values`. When an entry is deleted, - we delete its entry in `keys` and `values`. + /** + Ember Debug - @class Map - @namespace Ember - @private - @constructor + @module ember + @submodule ember-debug */ - var Map = Ember.Map = function() { - this.keys = OrderedSet.create(); - this.values = {}; - }; /** - @method create - @static + @class Ember */ - Map.create = function() { - return new Map(); - }; - Map.prototype = { - /** - This property will change as the number of objects in the map changes. + /** + Define an assertion that will throw an exception if the condition is not + met. Ember build tools will remove any calls to `Ember.assert()` when + doing a production build. Example: - @property length - @type number - @default 0 - */ - length: 0, + ```javascript + // Test for truthiness + Ember.assert('Must pass a valid object', obj); + // Fail unconditionally + Ember.assert('This code path should never be run'); + ``` - /** - Retrieve the value associated with a given key. + @method assert + @param {String} desc A description of the assertion. This will become + the text of the Error thrown if the assertion fails. + @param {Boolean} test Must be truthy for the assertion to pass. If + falsy, an exception will be thrown. + */ + Ember.assert = function(desc, test) { + var throwAssertion; - @method get - @param {*} key - @return {*} the value associated with the key, or `undefined` - */ - get: function(key) { - var values = this.values, - guid = guidFor(key); + if (Ember.typeOf(test) === 'function') { + throwAssertion = !test(); + } else { + throwAssertion = !test; + } - return values[guid]; - }, + if (throwAssertion) { + throw new EmberError("Assertion Failed: " + desc); + } + }; - /** - Adds a value to the map. If a value for the given key has already been - provided, the new value will replace the old value. - @method set - @param {*} key - @param {*} value - */ - set: function(key, value) { - var keys = this.keys, - values = this.values, - guid = guidFor(key); + /** + Display a warning with the provided message. Ember build tools will + remove any calls to `Ember.warn()` when doing a production build. - keys.add(key); - values[guid] = value; - set(this, 'length', keys.list.length); - }, + @method warn + @param {String} message A warning to display. + @param {Boolean} test An optional boolean. If falsy, the warning + will be displayed. + */ + Ember.warn = function(message, test) { + if (!test) { + Logger.warn("WARNING: "+message); + if ('trace' in Logger) { + Logger.trace(); + } + } + }; - /** - Removes a value from the map for an associated key. + /** + Display a debug notice. Ember build tools will remove any calls to + `Ember.debug()` when doing a production build. - @method remove - @param {*} key - @return {Boolean} true if an item was removed, false otherwise - */ - remove: function(key) { - // don't use ES6 "delete" because it will be annoying - // to use in browsers that are not ES6 friendly; - var keys = this.keys, - values = this.values, - guid = guidFor(key); + ```javascript + Ember.debug('I\'m a debug notice!'); + ``` - if (values.hasOwnProperty(guid)) { - keys.remove(key); - delete values[guid]; - set(this, 'length', keys.list.length); - return true; - } else { - return false; - } - }, + @method debug + @param {String} message A debug message to display. + */ + Ember.debug = function(message) { + Logger.debug("DEBUG: "+message); + }; - /** - Check whether a key is present. + /** + Display a deprecation warning with the provided message and a stack trace + (Chrome and Firefox only). Ember build tools will remove any calls to + `Ember.deprecate()` when doing a production build. - @method has - @param {*} key - @return {Boolean} true if the item was present, false otherwise - */ - has: function(key) { - var values = this.values, - guid = guidFor(key); + @method deprecate + @param {String} message A description of the deprecation. + @param {Boolean} test An optional boolean. If falsy, the deprecation + will be displayed. + */ + Ember.deprecate = function(message, test) { + var noDeprecation; - return values.hasOwnProperty(guid); - }, + if (typeof test === 'function') { + noDeprecation = test(); + } else { + noDeprecation = test; + } - /** - Iterate over all the keys and values. Calls the function once - for each key, passing in the key and value, in that order. + if (noDeprecation) { return; } - The keys are guaranteed to be iterated over in insertion order. + if (Ember.ENV.RAISE_ON_DEPRECATION) { throw new EmberError(message); } - @method forEach - @param {Function} callback - @param {*} self if passed, the `this` value inside the - callback. By default, `this` is the map. - */ - forEach: function(callback, self) { - var keys = this.keys, - values = this.values; + var error; - keys.forEach(function(key) { - var guid = guidFor(key); - callback.call(self, key, values[guid]); - }); - }, + // When using new Error, we can't do the arguments check for Chrome. Alternatives are welcome + try { __fail__.fail(); } catch (e) { error = e; } - /** - @method copy - @return {Ember.Map} - */ - copy: function() { - return copyMap(this, new Map()); - } - }; + if (Ember.LOG_STACKTRACE_ON_DEPRECATION && error.stack) { + var stack; + var stackStr = ''; - /** - @class MapWithDefault - @namespace Ember - @extends Ember.Map - @private - @constructor - @param [options] - @param {*} [options.defaultValue] - */ - function MapWithDefault(options) { - Map.call(this); - this.defaultValue = options.defaultValue; - }; + if (error['arguments']) { + // Chrome + stack = error.stack.replace(/^\s+at\s+/gm, ''). + replace(/^([^\(]+?)([\n$])/gm, '{anonymous}($1)$2'). + replace(/^Object.\s*\(([^\)]+)\)/gm, '{anonymous}($1)').split('\n'); + stack.shift(); + } else { + // Firefox + stack = error.stack.replace(/(?:\n@:0)?\s+$/m, ''). + replace(/^\(/gm, '{anonymous}(').split('\n'); + } - /** - @method create - @static - @param [options] - @param {*} [options.defaultValue] - @return {Ember.MapWithDefault|Ember.Map} If options are passed, returns - `Ember.MapWithDefault` otherwise returns `Ember.Map` - */ - MapWithDefault.create = function(options) { - if (options) { - return new MapWithDefault(options); - } else { - return new Map(); + stackStr = "\n " + stack.slice(2).join("\n "); + message = message + stackStr; } + + Logger.warn("DEPRECATION: "+message); }; - MapWithDefault.prototype = create(Map.prototype); + /** - Retrieve the value associated with a given key. + Alias an old, deprecated method with its new counterpart. - @method get - @param {*} key - @return {*} the value associated with the key, or the default value - */ - MapWithDefault.prototype.get = function(key) { - var hasValue = this.has(key); + Display a deprecation warning with the provided message and a stack trace + (Chrome and Firefox only) when the assigned method is called. - if (hasValue) { - return Map.prototype.get.call(this, key); - } else { - var defaultValue = this.defaultValue(key); - this.set(key, defaultValue); - return defaultValue; - } - }; + Ember build tools will not remove calls to `Ember.deprecateFunc()`, though + no warnings will be shown in production. - /** - @method copy - @return {Ember.MapWithDefault} + ```javascript + Ember.oldMethod = Ember.deprecateFunc('Please use the new, updated method', Ember.newMethod); + ``` + + @method deprecateFunc + @param {String} message A description of the deprecation. + @param {Function} func The new function called to replace its deprecated counterpart. + @return {Function} a new function that wrapped the original function with a deprecation warning */ - MapWithDefault.prototype.copy = function() { - return copyMap(this, new MapWithDefault({ - defaultValue: this.defaultValue - })); + Ember.deprecateFunc = function(message, func) { + return function() { + Ember.deprecate(message); + return func.apply(this, arguments); + }; }; - __exports__.OrderedSet = OrderedSet; - __exports__.Map = Map; - __exports__.MapWithDefault = MapWithDefault; - }); -define("ember-metal/merge", - ["exports"], - function(__exports__) { - "use strict"; + /** - Merge the contents of two objects together into the first object. + Run a function meant for debugging. Ember build tools will remove any calls to + `Ember.runInDebug()` when doing a production build. ```javascript - Ember.merge({first: 'Tom'}, {last: 'Dale'}); // {first: 'Tom', last: 'Dale'} - var a = {first: 'Yehuda'}, b = {last: 'Katz'}; - Ember.merge(a, b); // a == {first: 'Yehuda', last: 'Katz'}, b == {last: 'Katz'} + Ember.runInDebug(function() { + Ember.Handlebars.EachView.reopen({ + didInsertElement: function() { + console.log('I\'m happy'); + } + }); + }); ``` - @method merge - @for Ember - @param {Object} original The object to merge into - @param {Object} updates The object to copy properties from - @return {Object} + @method runInDebug + @param {Function} func The function to be executed. + @since 1.5.0 */ - function merge(original, updates) { - for (var prop in updates) { - if (!updates.hasOwnProperty(prop)) { continue; } - original[prop] = updates[prop]; - } - return original; + Ember.runInDebug = function(func) { + func(); }; - __exports__["default"] = merge; - }); -define("ember-metal/mixin", - ["ember-metal/core","ember-metal/merge","ember-metal/array","ember-metal/platform","ember-metal/utils","ember-metal/expand_properties","ember-metal/properties","ember-metal/computed","ember-metal/binding","ember-metal/observer","ember-metal/events","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { - "use strict"; /** - @module ember - @submodule ember-metal - */ - - var Ember = __dependency1__["default"]; - // warn, assert, wrap, et; - var merge = __dependency2__["default"]; - var map = __dependency3__.map; - var indexOf = __dependency3__.indexOf; - var forEach = __dependency3__.forEach; - var create = __dependency4__.create; - var guidFor = __dependency5__.guidFor; - var meta = __dependency5__.meta; - var META_KEY = __dependency5__.META_KEY; - var wrap = __dependency5__.wrap; - var makeArray = __dependency5__.makeArray; - var apply = __dependency5__.apply; - var expandProperties = __dependency6__["default"]; - var Descriptor = __dependency7__.Descriptor; - var defineProperty = __dependency7__.defineProperty; - var ComputedProperty = __dependency8__.ComputedProperty; - var Binding = __dependency9__.Binding; - var addObserver = __dependency10__.addObserver; - var removeObserver = __dependency10__.removeObserver; - var addBeforeObserver = __dependency10__.addBeforeObserver; - var removeBeforeObserver = __dependency10__.removeBeforeObserver; - var addListener = __dependency11__.addListener; - var removeListener = __dependency11__.removeListener; - - var REQUIRED, Alias, - a_map = map, - a_indexOf = indexOf, - a_forEach = forEach, - a_slice = [].slice, - o_create = create, - defineProperty = defineProperty, - metaFor = meta; + Will call `Ember.warn()` if ENABLE_ALL_FEATURES, ENABLE_OPTIONAL_FEATURES, or + any specific FEATURES flag is truthy. - function superFunction(){ - var ret, func = this.__nextSuper; - if (func) { - this.__nextSuper = null; - ret = apply(this, func, arguments); - this.__nextSuper = func; - } - return ret; - } + This method is called automatically in debug canary builds. + + @private + @method _warnIfUsingStrippedFeatureFlags + @return {void} + */ + function _warnIfUsingStrippedFeatureFlags(FEATURES, featuresWereStripped) { + if (featuresWereStripped) { + Ember.warn('Ember.ENV.ENABLE_ALL_FEATURES is only available in canary builds.', !Ember.ENV.ENABLE_ALL_FEATURES); + Ember.warn('Ember.ENV.ENABLE_OPTIONAL_FEATURES is only available in canary builds.', !Ember.ENV.ENABLE_OPTIONAL_FEATURES); - function mixinsMeta(obj) { - var m = metaFor(obj, true), ret = m.mixins; - if (!ret) { - ret = m.mixins = {}; - } else if (!m.hasOwnProperty('mixins')) { - ret = m.mixins = o_create(ret); + for (var key in FEATURES) { + if (FEATURES.hasOwnProperty(key) && key !== 'isEnabled') { + Ember.warn('FEATURE["' + key + '"] is set as enabled, but FEATURE flags are only available in canary builds.', !FEATURES[key]); + } + } } - return ret; } - function initMixin(mixin, args) { - if (args && args.length > 0) { - mixin.mixins = a_map.call(args, function(x) { - if (x instanceof Mixin) { return x; } - - // Note: Manually setup a primitive mixin here. This is the only - // way to actually get a primitive mixin. This way normal creation - // of mixins will give you combined mixins... - var mixin = new Mixin(); - mixin.properties = x; - return mixin; - }); - } - return mixin; - } + __exports__._warnIfUsingStrippedFeatureFlags = _warnIfUsingStrippedFeatureFlags;if (!Ember.testing) { + // Complain if they're using FEATURE flags in builds other than canary + Ember.FEATURES['features-stripped-test'] = true; + var featuresWereStripped = true; + + + delete Ember.FEATURES['features-stripped-test']; + _warnIfUsingStrippedFeatureFlags(Ember.ENV.FEATURES, featuresWereStripped); - function isMethod(obj) { - return 'function' === typeof obj && - obj.isMethod !== false && - obj !== Boolean && obj !== Object && obj !== Number && obj !== Array && obj !== Date && obj !== String; - } + // Inform the developer about the Ember Inspector if not installed. + var isFirefox = typeof InstallTrigger !== 'undefined'; + var isChrome = !!window.chrome && !window.opera; - var CONTINUE = {}; + if (typeof window !== 'undefined' && (isFirefox || isChrome) && window.addEventListener) { + window.addEventListener("load", function() { + if (document.documentElement && document.documentElement.dataset && !document.documentElement.dataset.emberExtension) { + var downloadURL; - function mixinProperties(mixinsMeta, mixin) { - var guid; + if(isChrome) { + downloadURL = 'https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi'; + } else if(isFirefox) { + downloadURL = 'https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/'; + } - if (mixin instanceof Mixin) { - guid = guidFor(mixin); - if (mixinsMeta[guid]) { return CONTINUE; } - mixinsMeta[guid] = mixin; - return mixin.properties; - } else { - return mixin; // apply anonymous mixin properties + Ember.debug('For more advanced debugging, install the Ember Inspector from ' + downloadURL); + } + }, false); } } - function concatenatedMixinProperties(concatProp, props, values, base) { - var concats; - - // reset before adding each new mixin to pickup concats from previous - concats = values[concatProp] || base[concatProp]; - if (props[concatProp]) { - concats = concats ? concats.concat(props[concatProp]) : props[concatProp]; - } + /* + We are transitioning away from `ember.js` to `ember.debug.js` to make + it much clearer that it is only for local development purposes. - return concats; + This flag value is changed by the tooling (by a simple string replacement) + so that if `ember.js` (which must be output for backwards compat reasons) is + used a nice helpful warning message will be printed out. + */ + var runningNonEmberDebugJS = true; + __exports__.runningNonEmberDebugJS = runningNonEmberDebugJS;if (runningNonEmberDebugJS) { + Ember.warn('Please use `ember.debug.js` instead of `ember.js` for development and debugging.'); } + }); +enifed("ember-extension-support", + ["ember-metal/core","ember-extension-support/data_adapter","ember-extension-support/container_debug_adapter"], + function(__dependency1__, __dependency2__, __dependency3__) { + "use strict"; + /** + Ember Extension Support - function giveDescriptorSuper(meta, key, property, values, descs) { - var superProperty; - - // Computed properties override methods, and do not call super to them - if (values[key] === undefined) { - // Find the original descriptor in a parent mixin - superProperty = descs[key]; - } + @module ember + @submodule ember-extension-support + @requires ember-application + */ - // If we didn't find the original descriptor in a parent mixin, find - // it on the original object. - superProperty = superProperty || meta.descs[key]; + var Ember = __dependency1__["default"]; + var DataAdapter = __dependency2__["default"]; + var ContainerDebugAdapter = __dependency3__["default"]; - if (!superProperty || !(superProperty instanceof ComputedProperty)) { - return property; - } + Ember.DataAdapter = DataAdapter; + Ember.ContainerDebugAdapter = ContainerDebugAdapter; + }); +enifed("ember-extension-support/container_debug_adapter", + ["ember-metal/core","ember-runtime/system/native_array","ember-metal/utils","ember-runtime/system/string","ember-runtime/system/namespace","ember-runtime/system/object","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + var emberA = __dependency2__.A; + var typeOf = __dependency3__.typeOf; + var dasherize = __dependency4__.dasherize; + var classify = __dependency4__.classify; + var Namespace = __dependency5__["default"]; + var EmberObject = __dependency6__["default"]; - // Since multiple mixins may inherit from the same parent, we need - // to clone the computed property so that other mixins do not receive - // the wrapped version. - property = o_create(property); - property.func = wrap(property.func, superProperty.func); + /** + @module ember + @submodule ember-extension-support + */ - return property; - } + /** + The `ContainerDebugAdapter` helps the container and resolver interface + with tools that debug Ember such as the + [Ember Extension](https://github.com/tildeio/ember-extension) + for Chrome and Firefox. - function giveMethodSuper(obj, key, method, values, descs) { - var superMethod; + This class can be extended by a custom resolver implementer + to override some of the methods with library-specific code. - // Methods overwrite computed properties, and do not call super to them. - if (descs[key] === undefined) { - // Find the original method in a parent mixin - superMethod = values[key]; - } + The methods likely to be overridden are: - // If we didn't find the original value in a parent mixin, find it in - // the original object - superMethod = superMethod || obj[key]; + * `canCatalogEntriesByType` + * `catalogEntriesByType` - // Only wrap the new method if the original method was a function - if ('function' !== typeof superMethod) { - return method; - } + The adapter will need to be registered + in the application's container as `container-debug-adapter:main` - return wrap(method, superMethod); - } + Example: - function applyConcatenatedProperties(obj, key, value, values) { - var baseValue = values[key] || obj[key]; + ```javascript + Application.initializer({ + name: "containerDebugAdapter", - if (baseValue) { - if ('function' === typeof baseValue.concat) { - return baseValue.concat(value); - } else { - return makeArray(baseValue).concat(value); + initialize: function(container, application) { + application.register('container-debug-adapter:main', require('app/container-debug-adapter')); } - } else { - return makeArray(value); - } - } - - function applyMergedProperties(obj, key, value, values) { - var baseValue = values[key] || obj[key]; - - if (!baseValue) { return value; } + }); + ``` - var newBase = merge({}, baseValue), - hasFunction = false; + @class ContainerDebugAdapter + @namespace Ember + @extends Ember.Object + @since 1.5.0 + */ + __exports__["default"] = EmberObject.extend({ + /** + The container of the application being debugged. + This property will be injected + on creation. - for (var prop in value) { - if (!value.hasOwnProperty(prop)) { continue; } + @property container + @default null + */ + container: null, - var propValue = value[prop]; - if (isMethod(propValue)) { - // TODO: support for Computed Properties, etc? - hasFunction = true; - newBase[prop] = giveMethodSuper(obj, prop, propValue, baseValue, {}); - } else { - newBase[prop] = propValue; - } - } + /** + The resolver instance of the application + being debugged. This property will be injected + on creation. - if (hasFunction) { - newBase._super = superFunction; - } + @property resolver + @default null + */ + resolver: null, - return newBase; - } + /** + Returns true if it is possible to catalog a list of available + classes in the resolver for a given type. - function addNormalizedProperty(base, key, value, meta, descs, values, concats, mergings) { - if (value instanceof Descriptor) { - if (value === REQUIRED && descs[key]) { return CONTINUE; } + @method canCatalogEntriesByType + @param {String} type The type. e.g. "model", "controller", "route" + @return {boolean} whether a list is available for this type. + */ + canCatalogEntriesByType: function(type) { + if (type === 'model' || type === 'template') return false; + return true; + }, - // Wrap descriptor function to implement - // __nextSuper() if needed - if (value.func) { - value = giveDescriptorSuper(meta, key, value, values, descs); - } + /** + Returns the available classes a given type. - descs[key] = value; - values[key] = undefined; - } else { - if ((concats && a_indexOf.call(concats, key) >= 0) || - key === 'concatenatedProperties' || - key === 'mergedProperties') { - value = applyConcatenatedProperties(base, key, value, values); - } else if ((mergings && a_indexOf.call(mergings, key) >= 0)) { - value = applyMergedProperties(base, key, value, values); - } else if (isMethod(value)) { - value = giveMethodSuper(base, key, value, values, descs); - } + @method catalogEntriesByType + @param {String} type The type. e.g. "model", "controller", "route" + @return {Array} An array of strings. + */ + catalogEntriesByType: function(type) { + var namespaces = emberA(Namespace.NAMESPACES), types = emberA(); + var typeSuffixRegex = new RegExp(classify(type) + "$"); - descs[key] = undefined; - values[key] = value; + namespaces.forEach(function(namespace) { + if (namespace !== Ember) { + for (var key in namespace) { + if (!namespace.hasOwnProperty(key)) { continue; } + if (typeSuffixRegex.test(key)) { + var klass = namespace[key]; + if (typeOf(klass) === 'class') { + types.push(dasherize(key.replace(typeSuffixRegex, ''))); + } + } + } + } + }); + return types; } - } + }); + }); +enifed("ember-extension-support/data_adapter", + ["ember-metal/property_get","ember-metal/run_loop","ember-runtime/system/string","ember-runtime/system/namespace","ember-runtime/system/object","ember-runtime/system/native_array","ember-application/system/application","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; + var get = __dependency1__.get; + var run = __dependency2__["default"]; + var dasherize = __dependency3__.dasherize; + var Namespace = __dependency4__["default"]; + var EmberObject = __dependency5__["default"]; + var emberA = __dependency6__.A; + var Application = __dependency7__["default"]; - function mergeMixins(mixins, m, descs, values, base, keys) { - var mixin, props, key, concats, mergings, meta; + /** + @module ember + @submodule ember-extension-support + */ - function removeKeys(keyName) { - delete descs[keyName]; - delete values[keyName]; - } + /** + The `DataAdapter` helps a data persistence library + interface with tools that debug Ember such + as the [Ember Extension](https://github.com/tildeio/ember-extension) + for Chrome and Firefox. - for(var i=0, l=mixins.length; i= 0) { - if (_detect(mixins[loc], targetMixin, seen)) { return true; } - } - return false; - } + return release; + }, - /** - @method detect - @param obj - @return {Boolean} - */ - MixinPrototype.detect = function(obj) { - if (!obj) { return false; } - if (obj instanceof Mixin) { return _detect(obj, this, {}); } - var m = obj[META_KEY], - mixins = m && m.mixins; - if (mixins) { - return !!mixins[guidFor(this)]; - } - return false; - }; - MixinPrototype.without = function() { - var ret = new Mixin(this); - ret._without = a_slice.call(arguments); - return ret; - }; + /** + Wraps a given model type and observes changes to it. - function _keys(ret, mixin, seen) { - if (seen[guidFor(mixin)]) { return; } - seen[guidFor(mixin)] = true; + @private + @method wrapModelType + @param {Class} type A model class + @param {String} Optional name of the class + @return {Object} contains the wrapped type and the function to remove observers + Format: + type: {Object} the wrapped type + The wrapped type has the following format: + name: {String} name of the type + count: {Integer} number of records available + columns: {Columns} array of columns to describe the record + object: {Class} the actual Model type class + release: {Function} The function to remove observers + */ + wrapModelType: function(type, name) { + var records = this.getRecords(type); + var typeToSend; - if (mixin.properties) { - var props = mixin.properties; - for (var key in props) { - if (props.hasOwnProperty(key)) { ret[key] = true; } - } - } else if (mixin.mixins) { - a_forEach.call(mixin.mixins, function(x) { _keys(ret, x, seen); }); - } - } + typeToSend = { + name: name || type.toString(), + count: get(records, 'length'), + columns: this.columnsForType(type), + object: type + }; - MixinPrototype.keys = function() { - var keys = {}, seen = {}, ret = []; - _keys(keys, this, seen); - for(var key in keys) { - if (keys.hasOwnProperty(key)) { ret.push(key); } - } - return ret; - }; - // returns the mixins currently applied to the specified object - // TODO: Make Ember.mixin - Mixin.mixins = function(obj) { - var m = obj[META_KEY], - mixins = m && m.mixins, ret = []; + return typeToSend; + }, - if (!mixins) { return ret; } - for (var key in mixins) { - var mixin = mixins[key]; + /** + Fetches all models defined in the application. - // skip primitive mixins since these are always anonymous - if (!mixin.properties) { ret.push(mixin); } - } + @private + @method getModelTypes + @return {Array} Array of model types + */ + getModelTypes: function() { + var self = this; + var containerDebugAdapter = this.get('containerDebugAdapter'); + var types; - return ret; - }; + if (containerDebugAdapter.canCatalogEntriesByType('model')) { + types = containerDebugAdapter.catalogEntriesByType('model'); + } else { + types = this._getObjectsOnNamespaces(); + } - REQUIRED = new Descriptor(); - REQUIRED.toString = function() { return '(Required Property)'; }; + // New adapters return strings instead of classes + types = emberA(types).map(function(name) { + return { + klass: self._nameToClass(name), + name: name + }; + }); + types = emberA(types).filter(function(type) { + return self.detect(type.klass); + }); - /** - Denotes a required property for a mixin + return emberA(types); + }, - @method required - @for Ember - */ - function required() { - return REQUIRED; - }; + /** + Loops over all namespaces and all objects + attached to them - Alias = function(methodName) { - this.methodName = methodName; - }; - Alias.prototype = new Descriptor(); + @private + @method _getObjectsOnNamespaces + @return {Array} Array of model type strings + */ + _getObjectsOnNamespaces: function() { + var namespaces = emberA(Namespace.NAMESPACES); + var types = emberA(); + var self = this; - /** - Makes a method available via an additional name. + namespaces.forEach(function(namespace) { + for (var key in namespace) { + if (!namespace.hasOwnProperty(key)) { continue; } + // Even though we will filter again in `getModelTypes`, + // we should not call `lookupContainer` on non-models + // (especially when `Ember.MODEL_FACTORY_INJECTIONS` is `true`) + if (!self.detect(namespace[key])) { continue; } + var name = dasherize(key); + if (!(namespace instanceof Application) && namespace.toString()) { + name = namespace + '/' + name; + } + types.push(name); + } + }); + return types; + }, - ```javascript - App.Person = Ember.Object.extend({ - name: function() { - return 'Tomhuda Katzdale'; - }, - moniker: Ember.aliasMethod('name') - }); + /** + Fetches all loaded records for a given type. - var goodGuy = App.Person.create(); - - goodGuy.name(); // 'Tomhuda Katzdale' - goodGuy.moniker(); // 'Tomhuda Katzdale' - ``` + @private + @method getRecords + @return {Array} An array of records. + This array will be observed for changes, + so it should update when new records are added/removed. + */ + getRecords: function(type) { + return emberA(); + }, - @method aliasMethod - @for Ember - @param {String} methodName name of the method to alias - @return {Ember.Descriptor} - */ - function aliasMethod(methodName) { - return new Alias(methodName); - }; + /** + Wraps a record and observers changes to it. - // .......................................................... - // OBSERVER HELPER - // + @private + @method wrapRecord + @param {Object} record The record instance. + @return {Object} The wrapped record. Format: + columnValues: {Array} + searchKeywords: {Array} + */ + wrapRecord: function(record) { + var recordToSend = { object: record }; - /** - Specify a method that observes property changes. + recordToSend.columnValues = this.getRecordColumnValues(record); + recordToSend.searchKeywords = this.getRecordKeywords(record); + recordToSend.filterValues = this.getRecordFilterValues(record); + recordToSend.color = this.getRecordColor(record); - ```javascript - Ember.Object.extend({ - valueObserver: Ember.observer('value', function() { - // Executes whenever the "value" property changes - }) - }); - ``` + return recordToSend; + }, - In the future this method may become asynchronous. If you want to ensure - synchronous behavior, use `immediateObserver`. + /** + Gets the values for each column. - Also available as `Function.prototype.observes` if prototype extensions are - enabled. + @private + @method getRecordColumnValues + @return {Object} Keys should match column names defined + by the model type. + */ + getRecordColumnValues: function(record) { + return {}; + }, - @method observer - @for Ember - @param {String} propertyNames* - @param {Function} func - @return func - */ - function observer() { - var func = a_slice.call(arguments, -1)[0]; - var paths; + /** + Returns keywords to match when searching records. - var addWatchedProperty = function (path) { paths.push(path); }; - var _paths = a_slice.call(arguments, 0, -1); + @private + @method getRecordKeywords + @return {Array} Relevant keywords for search. + */ + getRecordKeywords: function(record) { + return emberA(); + }, - if (typeof func !== "function") { - // revert to old, soft-deprecated argument ordering + /** + Returns the values of filters defined by `getFilters`. - func = arguments[0]; - _paths = a_slice.call(arguments, 1); - } + @private + @method getRecordFilterValues + @param {Object} record The record instance + @return {Object} The filter values + */ + getRecordFilterValues: function(record) { + return {}; + }, - paths = []; + /** + Each record can have a color that represents its state. - for (var i=0; i<_paths.length; ++i) { - expandProperties(_paths[i], addWatchedProperty); - } + @private + @method getRecordColor + @param {Object} record The record instance + @return {String} The record's color + Possible options: black, red, blue, green + */ + getRecordColor: function(record) { + return null; + }, - if (typeof func !== "function") { - throw new Ember.Error("Ember.observer called without a function"); - } + /** + Observes all relevant properties and re-sends the wrapped record + when a change occurs. - func.__ember_observes__ = paths; - return func; - }; + @private + @method observerRecord + @param {Object} record The record instance + @param {Function} recordUpdated The callback to call when a record is updated. + @return {Function} The function to call to remove all observers. + */ + observeRecord: function(record, recordUpdated) { + return function(){}; + } + }); + }); +enifed("ember-htmlbars", + ["ember-metal/core","ember-template-compiler","ember-htmlbars/hooks/inline","ember-htmlbars/hooks/content","ember-htmlbars/hooks/component","ember-htmlbars/hooks/block","ember-htmlbars/hooks/element","ember-htmlbars/hooks/subexpr","ember-htmlbars/hooks/attribute","ember-htmlbars/hooks/concat","ember-htmlbars/hooks/get","ember-htmlbars/hooks/set","morph","ember-htmlbars/system/make-view-helper","ember-htmlbars/system/make_bound_helper","ember-htmlbars/helpers","ember-htmlbars/helpers/binding","ember-htmlbars/helpers/view","ember-htmlbars/helpers/yield","ember-htmlbars/helpers/with","ember-htmlbars/helpers/log","ember-htmlbars/helpers/debugger","ember-htmlbars/helpers/bind-attr","ember-htmlbars/helpers/if_unless","ember-htmlbars/helpers/loc","ember-htmlbars/helpers/partial","ember-htmlbars/helpers/template","ember-htmlbars/helpers/input","ember-htmlbars/helpers/text_area","ember-htmlbars/helpers/collection","ember-htmlbars/helpers/each","ember-htmlbars/helpers/unbound","ember-htmlbars/system/bootstrap","ember-htmlbars/compat","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__, __dependency22__, __dependency23__, __dependency24__, __dependency25__, __dependency26__, __dependency27__, __dependency28__, __dependency29__, __dependency30__, __dependency31__, __dependency32__, __dependency33__, __dependency34__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; - /** - Specify a method that observes property changes. + var precompile = __dependency2__.precompile; + var compile = __dependency2__.compile; + var template = __dependency2__.template; + var registerPlugin = __dependency2__.registerPlugin; + + var inline = __dependency3__["default"]; + var content = __dependency4__["default"]; + var component = __dependency5__["default"]; + var block = __dependency6__["default"]; + var element = __dependency7__["default"]; + var subexpr = __dependency8__["default"]; + var attribute = __dependency9__["default"]; + var concat = __dependency10__["default"]; + var get = __dependency11__["default"]; + var set = __dependency12__["default"]; + var DOMHelper = __dependency13__.DOMHelper; + var makeViewHelper = __dependency14__["default"]; + var makeBoundHelper = __dependency15__["default"]; + + var registerHelper = __dependency16__.registerHelper; + var helper = __dependency16__.helper; + var helpers = __dependency16__["default"]; + var bindHelper = __dependency17__.bindHelper; + var viewHelper = __dependency18__.viewHelper; + var yieldHelper = __dependency19__.yieldHelper; + var withHelper = __dependency20__.withHelper; + var logHelper = __dependency21__.logHelper; + var debuggerHelper = __dependency22__.debuggerHelper; + var bindAttrHelper = __dependency23__.bindAttrHelper; + var bindAttrHelperDeprecated = __dependency23__.bindAttrHelperDeprecated; + var ifHelper = __dependency24__.ifHelper; + var unlessHelper = __dependency24__.unlessHelper; + var unboundIfHelper = __dependency24__.unboundIfHelper; + var boundIfHelper = __dependency24__.boundIfHelper; + var locHelper = __dependency25__.locHelper; + var partialHelper = __dependency26__.partialHelper; + var templateHelper = __dependency27__.templateHelper; + var inputHelper = __dependency28__.inputHelper; + var textareaHelper = __dependency29__.textareaHelper; + var collectionHelper = __dependency30__.collectionHelper; + var eachHelper = __dependency31__.eachHelper; + var unboundHelper = __dependency32__.unboundHelper; + + // importing adds template bootstrapping + // initializer to enable embedded templates + + // importing ember-htmlbars/compat updates the + // Ember.Handlebars global if htmlbars is enabled + + registerHelper('bindHelper', bindHelper); + registerHelper('bind', bindHelper); + registerHelper('view', viewHelper); + registerHelper('yield', yieldHelper); + registerHelper('with', withHelper); + registerHelper('if', ifHelper); + registerHelper('unless', unlessHelper); + registerHelper('unboundIf', unboundIfHelper); + registerHelper('boundIf', boundIfHelper); + registerHelper('log', logHelper); + registerHelper('debugger', debuggerHelper); + registerHelper('loc', locHelper); + registerHelper('partial', partialHelper); + registerHelper('template', templateHelper); + registerHelper('bind-attr', bindAttrHelper); + registerHelper('bindAttr', bindAttrHelperDeprecated); + registerHelper('input', inputHelper); + registerHelper('textarea', textareaHelper); + registerHelper('collection', collectionHelper); + registerHelper('each', eachHelper); + registerHelper('unbound', unboundHelper); + registerHelper('concat', concat); - ```javascript - Ember.Object.extend({ - valueObserver: Ember.immediateObserver('value', function() { - // Executes whenever the "value" property changes - }) - }); - ``` + + Ember.HTMLBars = { + helpers: helpers, + helper: helper, + _registerHelper: registerHelper, + template: template, + compile: compile, + precompile: precompile, + makeViewHelper: makeViewHelper, + makeBoundHelper: makeBoundHelper, + registerPlugin: registerPlugin + }; - In the future, `Ember.observer` may become asynchronous. In this event, - `Ember.immediateObserver` will maintain the synchronous behavior. + - Also available as `Function.prototype.observesImmediately` if prototype extensions are - enabled. + var defaultEnv = { + dom: new DOMHelper(), - @method immediateObserver - @for Ember - @param {String} propertyNames* - @param {Function} func - @return func - */ - function immediateObserver() { - for (var i=0, l=arguments.length; i this.changingFrom ? 'green' : 'red'; - // logic - } - }), + var merge = __dependency1__["default"]; + var helpers = __dependency2__["default"]; + var View = __dependency3__["default"]; + var Component = __dependency4__["default"]; + var makeViewHelper = __dependency5__["default"]; + var makeBoundHelper = __dependency6__["default"]; + var isStream = __dependency7__.isStream; - friendsDidChange: Ember.observer('friends.@each.name', function(obj, keyName) { - // some logic - // obj.get(keyName) returns friends array - }) - }); - ``` + var slice = [].slice; - Also available as `Function.prototype.observesBefore` if prototype extensions are - enabled. + /** + Wraps an Handlebars helper with an HTMLBars helper for backwards compatibility. - @method beforeObserver - @for Ember - @param {String} propertyNames* - @param {Function} func - @return func + @class HandlebarsCompatibleHelper + @constructor + @private */ - function beforeObserver() { - var func = a_slice.call(arguments, -1)[0]; - var paths; + function HandlebarsCompatibleHelper(fn) { + this.helperFunction = function helperFunc(params, hash, options, env) { + var param; + var handlebarsOptions = {}; + merge(handlebarsOptions, options); + merge(handlebarsOptions, env); - var addWatchedProperty = function(path) { paths.push(path); }; - - var _paths = a_slice.call(arguments, 0, -1); + handlebarsOptions.hash = {}; + for (var prop in hash) { + param = hash[prop]; - if (typeof func !== "function") { - // revert to old, soft-deprecated argument ordering + if (isStream(param)) { + handlebarsOptions.hash[prop] = param._label; + } else { + handlebarsOptions.hash[prop] = param; + } + } - func = arguments[0]; - _paths = a_slice.call(arguments, 1); - } + var args = new Array(params.length); + for (var i = 0, l = params.length; i < l; i++) { + param = params[i]; - paths = []; + if (isStream(param)) { + args[i] = param._label; + } else { + args[i] = param; + } + } + args.push(handlebarsOptions); - for (var i=0; i<_paths.length; ++i) { - expandProperties(_paths[i], addWatchedProperty); - } + return fn.apply(this, args); + }; - if (typeof func !== "function") { - throw new Ember.Error("Ember.beforeObserver called without a function"); - } + this.isHTMLBars = true; + } - func.__ember_observesBefore__ = paths; - return func; + HandlebarsCompatibleHelper.prototype = { + preprocessArguments: function() { } }; - __exports__.IS_BINDING = IS_BINDING; - __exports__.mixin = mixin; - __exports__.Mixin = Mixin; - __exports__.required = required; - __exports__.aliasMethod = aliasMethod; - __exports__.observer = observer; - __exports__.immediateObserver = immediateObserver; - __exports__.beforeObserver = beforeObserver; - }); -define("ember-metal/observer", - ["ember-metal/watching","ember-metal/array","ember-metal/events","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var watch = __dependency1__.watch; - var unwatch = __dependency1__.unwatch; - var map = __dependency2__.map; - var listenersFor = __dependency3__.listenersFor; - var addListener = __dependency3__.addListener; - var removeListener = __dependency3__.removeListener; - var suspendListeners = __dependency3__.suspendListeners; - var suspendListener = __dependency3__.suspendListener; - /** - @module ember-metal - */ + function registerHandlebarsCompatibleHelper(name, value) { + helpers[name] = new HandlebarsCompatibleHelper(value); + } - var AFTER_OBSERVERS = ':change', - BEFORE_OBSERVERS = ':before'; + __exports__.registerHandlebarsCompatibleHelper = registerHandlebarsCompatibleHelper;function handlebarsHelper(name, value) { + Ember.assert("You tried to register a component named '" + name + + "', but component names must include a '-'", !Component.detect(value) || name.match(/-/)); - function changeEvent(keyName) { - return keyName+AFTER_OBSERVERS; - } + if (View.detect(value)) { + helpers[name] = makeViewHelper(value); + } else { + var boundHelperArgs = slice.call(arguments, 1); + var boundFn = makeBoundHelper.apply(this, boundHelperArgs); - function beforeEvent(keyName) { - return keyName+BEFORE_OBSERVERS; + helpers[name] = boundFn; + } } + __exports__.handlebarsHelper = handlebarsHelper;__exports__["default"] = HandlebarsCompatibleHelper; + }); +enifed("ember-htmlbars/compat/make-bound-helper", + ["ember-metal/core","ember-metal/mixin","ember-htmlbars/system/helper","ember-metal/streams/stream","ember-metal/streams/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; /** - @method addObserver - @for Ember - @param obj - @param {String} path - @param {Object|Function} targetOrMethod - @param {Function|String} [method] + @module ember + @submodule ember-htmlbars */ - function addObserver(obj, _path, target, method) { - addListener(obj, changeEvent(_path), target, method); - watch(obj, _path); - return this; - }; + var Ember = __dependency1__["default"]; + // Ember.FEATURES, Ember.assert, Ember.Handlebars, Ember.lookup + var IS_BINDING = __dependency2__.IS_BINDING; + var Helper = __dependency3__["default"]; - function observersFor(obj, path) { - return listenersFor(obj, changeEvent(path)); - }; + var Stream = __dependency4__["default"]; + var readArray = __dependency5__.readArray; + var scanArray = __dependency5__.scanArray; + var scanHash = __dependency5__.scanHash; + var readHash = __dependency5__.readHash; + var isStream = __dependency5__.isStream; /** - @method removeObserver - @for Ember - @param obj - @param {String} path - @param {Object|Function} targetOrMethod - @param {Function|String} [method] - */ - function removeObserver(obj, _path, target, method) { - unwatch(obj, _path); - removeListener(obj, changeEvent(_path), target, method); + A helper function used by `registerBoundHelper`. Takes the + provided Handlebars helper function fn and returns it in wrapped + bound helper form. - return this; - }; + The main use case for using this outside of `registerBoundHelper` + is for registering helpers on the container: - /** - @method addBeforeObserver - @for Ember - @param obj - @param {String} path - @param {Object|Function} targetOrMethod - @param {Function|String} [method] - */ - function addBeforeObserver(obj, _path, target, method) { - addListener(obj, beforeEvent(_path), target, method); - watch(obj, _path); + ```js + var boundHelperFn = Ember.Handlebars.makeBoundHelper(function(word) { + return word.toUpperCase(); + }); - return this; - }; + container.register('helper:my-bound-helper', boundHelperFn); + ``` - // Suspend observer during callback. - // - // This should only be used by the target of the observer - // while it is setting the observed path. - function _suspendBeforeObserver(obj, path, target, method, callback) { - return suspendListener(obj, beforeEvent(path), target, method, callback); - }; + In the above example, if the helper function hadn't been wrapped in + `makeBoundHelper`, the registered helper would be unbound. - function _suspendObserver(obj, path, target, method, callback) { - return suspendListener(obj, changeEvent(path), target, method, callback); - }; + @method makeBoundHelper + @for Ember.Handlebars + @param {Function} function + @param {String} dependentKeys* + @since 1.2.0 + @deprecated + */ + __exports__["default"] = function makeBoundHelper(fn, compatMode) { + var dependentKeys = []; + for (var i = 1; i < arguments.length; i++) { + dependentKeys.push(arguments[i]); + } - function _suspendBeforeObservers(obj, paths, target, method, callback) { - var events = map.call(paths, beforeEvent); - return suspendListeners(obj, events, target, method, callback); - }; + function helperFunc(params, hash, options, env) { + var view = this; + var numParams = params.length; + var param; - function _suspendObservers(obj, paths, target, method, callback) { - var events = map.call(paths, changeEvent); - return suspendListeners(obj, events, target, method, callback); - }; - - function beforeObserversFor(obj, path) { - return listenersFor(obj, beforeEvent(path)); - }; + Ember.assert("registerBoundHelper-generated helpers do not support use with Handlebars blocks.", !options.template); - /** - @method removeBeforeObserver - @for Ember - @param obj - @param {String} path - @param {Object|Function} targetOrMethod - @param {Function|String} [method] - */ - function removeBeforeObserver(obj, _path, target, method) { - unwatch(obj, _path); - removeListener(obj, beforeEvent(_path), target, method); + for (var prop in hash) { + if (IS_BINDING.test(prop)) { + hash[prop.slice(0, -7)] = view.getStream(hash[prop]); + delete hash[prop]; + } + } - return this; - }; + function valueFn() { + var args = readArray(params); + var properties = new Array(params.length); + for (var i = 0, l = params.length; i < l; i++) { + param = params[i]; - __exports__.addObserver = addObserver; - __exports__.observersFor = observersFor; - __exports__.removeObserver = removeObserver; - __exports__.addBeforeObserver = addBeforeObserver; - __exports__._suspendBeforeObserver = _suspendBeforeObserver; - __exports__._suspendObserver = _suspendObserver; - __exports__._suspendBeforeObservers = _suspendBeforeObservers; - __exports__._suspendObservers = _suspendObservers; - __exports__.beforeObserversFor = beforeObserversFor; - __exports__.removeBeforeObserver = removeBeforeObserver; - }); -define("ember-metal/observer_set", - ["ember-metal/utils","ember-metal/events","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var guidFor = __dependency1__.guidFor; - var sendEvent = __dependency2__.sendEvent; + if (isStream(param)) { + properties[i] = param._label; + } else { + properties[i] = param; + } + } - /* - this.observerSet = { - [senderGuid]: { // variable name: `keySet` - [keyName]: listIndex + args.push({ + hash: readHash(hash), + data: { properties: properties } + }); + return fn.apply(view, args); } - }, - this.observers = [ - { - sender: obj, - keyName: keyName, - eventName: eventName, - listeners: [ - [target, method, flags] - ] - }, - ... - ] - */ - function ObserverSet() { - this.clear(); - }; - ObserverSet.prototype.add = function(sender, keyName, eventName) { - var observerSet = this.observerSet, - observers = this.observers, - senderGuid = guidFor(sender), - keySet = observerSet[senderGuid], - index; + // If none of the hash parameters are bound, act as an unbound helper. + // This prevents views from being unnecessarily created + var hasStream = scanArray(params) || scanHash(hash); - if (!keySet) { - observerSet[senderGuid] = keySet = {}; - } - index = keySet[keyName]; - if (index === undefined) { - index = observers.push({ - sender: sender, - keyName: keyName, - eventName: eventName, - listeners: [] - }) - 1; - keySet[keyName] = index; - } - return observers[index].listeners; - }; + if (env.data.isUnbound || !hasStream){ + return valueFn(); + } else { + var lazyValue = new Stream(valueFn); - ObserverSet.prototype.flush = function() { - var observers = this.observers, i, len, observer, sender; - this.clear(); - for (i=0, len=observers.length; i < len; ++i) { - observer = observers[i]; - sender = observer.sender; - if (sender.isDestroying || sender.isDestroyed) { continue; } - sendEvent(sender, observer.eventName, [sender, observer.keyName], observer.listeners); - } - }; + for (i = 0; i < numParams; i++) { + param = params[i]; + if (isStream(param)) { + param.subscribe(lazyValue.notify, lazyValue); + } + } - ObserverSet.prototype.clear = function() { - this.observerSet = {}; - this.observers = []; - }; + for (prop in hash) { + param = hash[prop]; + if (isStream(param)) { + param.subscribe(lazyValue.notify, lazyValue); + } + } - __exports__["default"] = ObserverSet; + if (numParams > 0) { + var firstParam = params[0]; + // Only bother with subscriptions if the first argument + // is a stream itself, and not a primitive. + if (isStream(firstParam)) { + var onDependentKeyNotify = function onDependentKeyNotify(stream) { + stream.value(); + lazyValue.notify(); + }; + for (i = 0; i < dependentKeys.length; i++) { + var childParam = firstParam.get(dependentKeys[i]); + childParam.value(); + childParam.subscribe(onDependentKeyNotify); + } + } + } + + return lazyValue; + } + } + + return new Helper(helperFunc); + } }); -define("ember-metal/platform", - ["ember-metal/core","exports"], +enifed("ember-htmlbars/compat/precompile", + ["htmlbars-compiler/compiler","exports"], function(__dependency1__, __exports__) { "use strict"; - /*globals Node */ + /** + @module ember + @submodule ember-htmlbars + */ - var Ember = __dependency1__["default"]; + var compile = __dependency1__.compile; + var compileSpec = __dependency1__.compileSpec; + __exports__["default"] = function(string) { + var asObject = arguments[1] === undefined ? true : arguments[1]; + var compileFunc = asObject ? compile : compileSpec; + + return compileFunc(string); + } + }); +enifed("ember-htmlbars/compat/register-bound-helper", + ["ember-htmlbars/helpers","ember-htmlbars/compat/make-bound-helper","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; /** - @module ember-metal + @module ember + @submodule ember-htmlbars */ - /** - Platform specific methods and feature detectors needed by the framework. + var helpers = __dependency1__["default"]; + var makeBoundHelper = __dependency2__["default"]; - @class platform - @namespace Ember - @static - */ - // TODO remove this - var platform = {}; + var slice = [].slice; /** - Identical to `Object.create()`. Implements if not available natively. + Register a bound handlebars helper. Bound helpers behave similarly to regular + handlebars helpers, with the added ability to re-render when the underlying data + changes. - @method create - @for Ember - */ - var create = Object.create; + ## Simple example - // IE8 has Object.create but it couldn't treat property descriptors. - if (create) { - if (create({a: 1}, {a: {value: 2}}).a !== 2) { - create = null; - } - } + ```javascript + Ember.Handlebars.registerBoundHelper('capitalize', function(value) { + return Ember.String.capitalize(value); + }); + ``` - // STUB_OBJECT_CREATE allows us to override other libraries that stub - // Object.create different than we would prefer - if (!create || Ember.ENV.STUB_OBJECT_CREATE) { - var K = function() {}; + The above bound helper can be used inside of templates as follows: - create = function(obj, props) { - K.prototype = obj; - obj = new K(); - if (props) { - K.prototype = obj; - for (var prop in props) { - K.prototype[prop] = props[prop].value; - } - obj = new K(); - } - K.prototype = null; + ```handlebars + {{capitalize name}} + ``` - return obj; - }; + 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. - create.isSimulated = true; - } + ## Example with options - var defineProperty = Object.defineProperty; - var canRedefineProperties, canDefinePropertyOnDOM; + Like normal handlebars helpers, bound helpers have access to the options + passed into the helper call. - // Catch IE8 where Object.defineProperty exists but only works on DOM elements - if (defineProperty) { - try { - defineProperty({}, 'a',{get:function() {}}); - } catch (e) { - defineProperty = null; - } - } + ```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 (defineProperty) { - // Detects a bug in Android <3.2 where you cannot redefine a property using - // Object.defineProperty once accessors have already been set. - canRedefineProperties = (function() { - var obj = {}; + This helper could be used in a template as follows: - defineProperty(obj, 'a', { - configurable: true, - enumerable: true, - get: function() { }, - set: function() { } - }); + ```handlebars + {{repeat text count=3}} + ``` - defineProperty(obj, 'a', { - configurable: true, - enumerable: true, - writable: true, - value: true - }); + ## Example with bound options - return obj.a === true; - })(); + Bound hash options are also supported. Example: - // This is for Safari 5.0, which supports Object.defineProperty, but not - // on DOM nodes. - canDefinePropertyOnDOM = (function() { - try { - defineProperty(document.createElement('div'), 'definePropertyOnDOM', {}); - return true; - } catch(e) { } + ```handlebars + {{repeat text count=numRepeats}} + ``` - return false; - })(); + 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. - if (!canRedefineProperties) { - defineProperty = null; - } else if (!canDefinePropertyOnDOM) { - defineProperty = function(obj, keyName, desc) { - var isNode; + ## Example with extra dependencies - if (typeof Node === "object") { - isNode = obj instanceof Node; - } else { - isNode = typeof obj === "object" && typeof obj.nodeType === "number" && typeof obj.nodeName === "string"; - } + 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. - if (isNode) { - // TODO: Should we have a warning here? - return (obj[keyName] = desc.value); - } else { - return Object.defineProperty(obj, keyName, desc); - } - }; - } - } + ```javascript + Ember.Handlebars.registerBoundHelper('capitalizeName', function(value) { + return value.get('name').toUpperCase(); + }, 'name'); + ``` - /** - @class platform - @namespace Ember - */ + ## Example with multiple bound properties - /** - Identical to `Object.defineProperty()`. Implements as much functionality - as possible if not available natively. + `Ember.Handlebars.registerBoundHelper` supports binding to + multiple properties, e.g.: - @method defineProperty - @param {Object} obj The object to modify - @param {String} keyName property name to modify - @param {Object} desc descriptor hash - @return {void} - */ - platform.defineProperty = defineProperty; + ```javascript + Ember.Handlebars.registerBoundHelper('concatenate', function() { + var values = Array.prototype.slice.call(arguments, 0, -1); + return values.join('||'); + }); + ``` - /** - Set to true if the platform supports native getters and setters. + 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. - @property hasPropertyAccessors - @final - */ - platform.hasPropertyAccessors = true; + ## Use with unbound helper - if (!platform.defineProperty) { - platform.hasPropertyAccessors = false; + The `{{unbound}}` helper can be used with bound helper invocations + to render them in their unbound form, e.g. - platform.defineProperty = function(obj, keyName, desc) { - if (!desc.get) { obj[keyName] = desc.value; } - }; + ```handlebars + {{unbound capitalize name}} + ``` - platform.defineProperty.isSimulated = true; - } + In this example, if the name property changes, the helper + will not re-render. - if (Ember.ENV.MANDATORY_SETTER && !platform.hasPropertyAccessors) { - Ember.ENV.MANDATORY_SETTER = false; - } + ## Use with blocks not supported - __exports__.create = create; - __exports__.platform = platform; + 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* + */ + __exports__["default"] = function registerBoundHelper(name, fn) { + var boundHelperArgs = slice.call(arguments, 1); + var boundFn = makeBoundHelper.apply(this, boundHelperArgs); + + helpers[name] = boundFn; + } }); -define("ember-metal/properties", - ["ember-metal/core","ember-metal/utils","ember-metal/platform","ember-metal/property_events","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { +enifed("ember-htmlbars/helpers", + ["ember-metal/platform","ember-views/views/view","ember-views/views/component","ember-htmlbars/system/make-view-helper","ember-htmlbars/system/helper","ember-htmlbars/system/make_bound_helper","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { "use strict"; /** - @module ember-metal + @module ember + @submodule ember-htmlbars */ - var Ember = __dependency1__["default"]; - var META_KEY = __dependency2__.META_KEY; - var meta = __dependency2__.meta; - var platform = __dependency3__.platform; - var overrideChains = __dependency4__.overrideChains; - var metaFor = meta, - objectDefineProperty = platform.defineProperty; - - var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER; + var o_create = __dependency1__.create; - // .......................................................... - // DESCRIPTOR - // + /** + @private + @property helpers + */ + var helpers = o_create(null); /** - Objects of this type can implement an interface to respond to requests to - get and set. The default implementation handles simple properties. + @module ember + @submodule ember-htmlbars + */ - You generally won't need to create or subclass this directly. + var View = __dependency2__["default"]; + var Component = __dependency3__["default"]; + var makeViewHelper = __dependency4__["default"]; + var Helper = __dependency5__["default"]; + var makeBoundHelper = __dependency6__["default"]; - @class Descriptor - @namespace Ember - @private - @constructor - */ - function Descriptor() {}; + /** + Register a bound helper or custom view helper. - // .......................................................... - // DEFINING PROPERTIES API - // + ## Simple bound helper example - var MANDATORY_SETTER_FUNCTION = Ember.MANDATORY_SETTER_FUNCTION = function(value) { - Ember.assert("You must use Ember.set() to access this property (of " + this + ")", false); - }; + ```javascript + Ember.HTMLBars.helper('capitalize', function(value) { + return value.toUpperCase(); + }); + ``` - var DEFAULT_GETTER_FUNCTION = Ember.DEFAULT_GETTER_FUNCTION = function(name) { - return function() { - var meta = this[META_KEY]; - return meta && meta.values[name]; - }; - }; + The above bound helper can be used inside of templates as follows: - /** - NOTE: This is a low-level method used by other parts of the API. You almost - never want to call this method directly. Instead you should use - `Ember.mixin()` to define new properties. + ```handlebars + {{capitalize name}} + ``` - Defines a property on an object. This method works much like the ES5 - `Object.defineProperty()` method except that it can also accept computed - properties and other special descriptors. + 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. - Normally this method takes only three parameters. However if you pass an - instance of `Ember.Descriptor` as the third param then you can pass an - optional value as the fourth parameter. This is often more efficient than - creating new descriptor hashes for each property. + For more examples of bound helpers, see documentation for + `Ember.HTMLBars.registerBoundHelper`. - ## Examples + ## Custom view helper example + + Assuming a view subclass named `App.CalendarView` were defined, a helper + for rendering instances of this view could be registered as follows: ```javascript - // ES5 compatible mode - Ember.defineProperty(contact, 'firstName', { - writable: true, - configurable: false, - enumerable: true, - value: 'Charles' - }); + Ember.HTMLBars.helper('calendar', App.CalendarView): + ``` - // define a simple property - Ember.defineProperty(contact, 'lastName', undefined, 'Jolley'); + The above bound helper can be used inside of templates as follows: - // define a computed property - Ember.defineProperty(contact, 'fullName', Ember.computed(function() { - return this.firstName+' '+this.lastName; - }).property('firstName', 'lastName')); + ```handlebars + {{calendar}} ``` - @private - @method defineProperty - @for Ember - @param {Object} obj the object to define this property on. This may be a prototype. - @param {String} keyName the name of the property - @param {Ember.Descriptor} [desc] an instance of `Ember.Descriptor` (typically a - computed property) or an ES5 descriptor. - You must provide this or `data` but not both. - @param {*} [data] something other than a descriptor, that will - become the explicit value of this property. - */ - function defineProperty(obj, keyName, desc, data, meta) { - var descs, existingDesc, watching, value; + Which is functionally equivalent to: - if (!meta) meta = metaFor(obj); - descs = meta.descs; - existingDesc = meta.descs[keyName]; - watching = meta.watching[keyName] > 0; + ```handlebars + {{view 'calendar'}} + ``` - if (existingDesc instanceof Descriptor) { - existingDesc.teardown(obj, keyName); - } + Options in the helper will be passed to the view in exactly the same + manner as with the `view` helper. - if (desc instanceof Descriptor) { - value = desc; + @private + @method helper + @for Ember.HTMLBars + @param {String} name + @param {Function|Ember.View} function or view class constructor + */ + function helper(name, value) { + Ember.assert("You tried to register a component named '" + name + + "', but component names must include a '-'", !Component.detect(value) || name.match(/-/)); - descs[keyName] = desc; - if (MANDATORY_SETTER && watching) { - objectDefineProperty(obj, keyName, { - configurable: true, - enumerable: true, - writable: true, - value: undefined // make enumerable - }); - } else { - obj[keyName] = undefined; // make enumerable - } + if (View.detect(value)) { + helpers[name] = makeViewHelper(value); } else { - descs[keyName] = undefined; // shadow descriptor in proto - if (desc == null) { - value = data; - - if (MANDATORY_SETTER && watching) { - meta.values[keyName] = data; - objectDefineProperty(obj, keyName, { - configurable: true, - enumerable: true, - set: MANDATORY_SETTER_FUNCTION, - get: DEFAULT_GETTER_FUNCTION(keyName) - }); - } else { - obj[keyName] = data; - } - } else { - value = desc; - - // compatibility with ES5 - objectDefineProperty(obj, keyName, desc); - } + registerBoundHelper(name, value); } + } - // if key is being watched, override chains that - // were initialized with the prototype - if (watching) { overrideChains(obj, keyName, meta); } + __exports__.helper = helper;/** + @private + @method registerHelper + @for Ember.HTMLBars + @param {String} name + @param {Function} helperFunc the helper function to add + */ + function registerHelper(name, helperFunc, preprocessFunction) { + helpers[name] = new Helper(helperFunc, preprocessFunction); + } - // The `value` passed to the `didDefineProperty` hook is - // either the descriptor or data, whichever was passed. - if (obj.didDefineProperty) { obj.didDefineProperty(obj, keyName, value); } + __exports__.registerHelper = registerHelper;/** + Register a bound helper. Bound helpers behave similarly to regular + helpers, with the added ability to re-render when the underlying data + changes. - return this; - }; + ## Simple example - __exports__.Descriptor = Descriptor; - __exports__.defineProperty = defineProperty; - }); -define("ember-metal/property_events", - ["ember-metal/utils","ember-metal/events","ember-metal/observer_set","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var META_KEY = __dependency1__.META_KEY; - var guidFor = __dependency1__.guidFor; - var tryFinally = __dependency1__.tryFinally; - var sendEvent = __dependency2__.sendEvent; - var listenersUnion = __dependency2__.listenersUnion; - var listenersDiff = __dependency2__.listenersDiff; - var ObserverSet = __dependency3__["default"]; + ```javascript + Ember.HTMLBars.registerBoundHelper('capitalize', function(params, hash, options, env) { + return Ember.String.capitalize(params[0]); + }); + ``` - var beforeObserverSet = new ObserverSet(), - observerSet = new ObserverSet(), - deferred = 0; + The above bound helper can be used inside of templates as follows: - // .......................................................... - // PROPERTY CHANGES - // + ```handlebars + {{capitalize name}} + ``` - /** - This function is called just before an object property is about to change. - It will notify any before observers and prepare caches among other things. + 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. - Normally you will not need to call this method directly but if for some - reason you can't directly watch a property you can invoke this method - manually along with `Ember.propertyDidChange()` which you should call just - after the property value changes. + ## Example with hash parameters - @method propertyWillChange - @for Ember - @param {Object} obj The object with the property that will change - @param {String} keyName The property key (or path) that will change. - @return {void} - */ - function propertyWillChange(obj, keyName) { - var m = obj[META_KEY], - watching = (m && m.watching[keyName] > 0) || keyName === 'length', - proto = m && m.proto, - desc = m && m.descs[keyName]; - - if (!watching) { return; } - if (proto === obj) { return; } - if (desc && desc.willChange) { desc.willChange(obj, keyName); } - dependentKeysWillChange(obj, keyName, m); - chainsWillChange(obj, keyName, m); - notifyBeforeObservers(obj, keyName); - } + Like normal helpers, bound helpers have access to the hash parameters + passed into the helper call. - /** - This function is called just after an object property has changed. - It will notify any observers and clear caches among other things. + ```javascript + Ember.HTMLBars.registerBoundHelper('repeat', function(params, hash) { + var count = hash.count; + var value = params[0]; - Normally you will not need to call this method directly but if for some - reason you can't directly watch a property you can invoke this method - manually along with `Ember.propertyWillChange()` which you should call just - before the property value changes. + return new Array( count + 1).join( value ); + }); + ``` - @method propertyDidChange - @for Ember - @param {Object} obj The object with the property that will change - @param {String} keyName The property key (or path) that will change. - @return {void} - */ - function propertyDidChange(obj, keyName) { - var m = obj[META_KEY], - watching = (m && m.watching[keyName] > 0) || keyName === 'length', - proto = m && m.proto, - desc = m && m.descs[keyName]; + This helper could be used in a template as follows: - if (proto === obj) { return; } + ```handlebars + {{repeat text count=3}} + ``` - // shouldn't this mean that we're watching this key? - if (desc && desc.didChange) { desc.didChange(obj, keyName); } - if (!watching && keyName !== 'length') { return; } + ## Example with bound hash parameters - dependentKeysDidChange(obj, keyName, m); - chainsDidChange(obj, keyName, m, false); - notifyObservers(obj, keyName); - } + Bound hash params are also supported. Example: - var WILL_SEEN, DID_SEEN; + ```handlebars + {{repeat text count=numRepeats}} + ``` - // called whenever a property is about to change to clear the cache of any dependent keys (and notify those properties of changes, etc...) - function dependentKeysWillChange(obj, depKey, meta) { - if (obj.isDestroying) { return; } + 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. - var seen = WILL_SEEN, top = !seen; - if (top) { seen = WILL_SEEN = {}; } - iterDeps(propertyWillChange, obj, depKey, seen, meta); - if (top) { WILL_SEEN = null; } - } + ## Example with multiple bound properties - // called whenever a property has just changed to update dependent keys - function dependentKeysDidChange(obj, depKey, meta) { - if (obj.isDestroying) { return; } + `Ember.HTMLBars.registerBoundHelper` supports binding to + multiple properties, e.g.: - var seen = DID_SEEN, top = !seen; - if (top) { seen = DID_SEEN = {}; } - iterDeps(propertyDidChange, obj, depKey, seen, meta); - if (top) { DID_SEEN = null; } - } + ```javascript + Ember.HTMLBars.registerBoundHelper('concatenate', function(params) { + return params.join('||'); + }); + ``` - function iterDeps(method, obj, depKey, seen, meta) { - var guid = guidFor(obj); - if (!seen[guid]) seen[guid] = {}; - if (seen[guid][depKey]) return; - seen[guid][depKey] = true; + 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. - var deps = meta.deps; - deps = deps && deps[depKey]; - if (deps) { - for(var key in deps) { - var desc = meta.descs[key]; - if (desc && desc._suspended === obj) continue; - method(obj, key); - } - } - } - - function chainsWillChange(obj, keyName, m) { - if (!(m.hasOwnProperty('chainWatchers') && - m.chainWatchers[keyName])) { - return; - } - - var nodes = m.chainWatchers[keyName], - events = [], - i, l; - - for(i = 0, l = nodes.length; i < l; i++) { - nodes[i].willChange(events); - } - - for (i = 0, l = events.length; i < l; i += 2) { - propertyWillChange(events[i], events[i+1]); - } - } - - function chainsDidChange(obj, keyName, m, suppressEvents) { - if (!(m && m.hasOwnProperty('chainWatchers') && - m.chainWatchers[keyName])) { - return; - } + ## Use with unbound helper - var nodes = m.chainWatchers[keyName], - events = suppressEvents ? null : [], - i, l; + The `{{unbound}}` helper can be used with bound helper invocations + to render them in their unbound form, e.g. - for(i = 0, l = nodes.length; i < l; i++) { - nodes[i].didChange(events); - } + ```handlebars + {{unbound capitalize name}} + ``` - if (suppressEvents) { - return; - } + In this example, if the name property changes, the helper + will not re-render. - for (i = 0, l = events.length; i < l; i += 2) { - propertyDidChange(events[i], events[i+1]); - } - } + ## Use with blocks not supported - function overrideChains(obj, keyName, m) { - chainsDidChange(obj, keyName, m, true); - }; + Bound helpers do not support use with blocks or the addition of + child views of any kind. - /** - @method beginPropertyChanges - @chainable @private + @method registerBoundHelper + @for Ember.HTMLBars + @param {String} name + @param {Function} function */ - function beginPropertyChanges() { - deferred++; + function registerBoundHelper(name, fn) { + var boundFn = makeBoundHelper(fn); + + helpers[name] = boundFn; } + __exports__.registerBoundHelper = registerBoundHelper;__exports__["default"] = helpers; + }); +enifed("ember-htmlbars/helpers/bind-attr", + ["ember-metal/core","ember-runtime/system/string","ember-views/attr_nodes/attr_node","ember-views/attr_nodes/legacy_bind","ember-metal/keys","ember-htmlbars/helpers","ember-metal/enumerable_utils","ember-metal/streams/utils","ember-views/streams/class_name_binding","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __exports__) { + "use strict"; /** - @method endPropertyChanges - @private + @module ember + @submodule ember-htmlbars */ - function endPropertyChanges() { - deferred--; - if (deferred<=0) { - beforeObserverSet.clear(); - observerSet.flush(); - } - } + + var Ember = __dependency1__["default"]; + // Ember.assert + + var fmt = __dependency2__.fmt; + var AttrNode = __dependency3__["default"]; + var LegacyBindAttrNode = __dependency4__["default"]; + var keys = __dependency5__["default"]; + var helpers = __dependency6__["default"]; + var map = __dependency7__.map; + var isStream = __dependency8__.isStream; + var concat = __dependency8__.concat; + var streamifyClassNameBinding = __dependency9__.streamifyClassNameBinding; /** - Make a series of property changes together in an - exception-safe way. + `bind-attr` allows you to create a binding between DOM element attributes and + Ember objects. For example: - ```javascript - Ember.changeProperties(function() { - obj1.set('foo', mayBlowUpWhenSet); - obj2.set('bar', baz); - }); + ```handlebars + imageTitle}} ``` - @method changeProperties - @param {Function} callback - @param [binding] - */ - function changeProperties(cb, binding) { - beginPropertyChanges(); - tryFinally(cb, endPropertyChanges, binding); - }; + The above handlebars template will fill the ``'s `src` attribute with + the value of the property referenced with `imageUrl` and its `alt` + attribute with the value of the property referenced with `imageTitle`. - function notifyBeforeObservers(obj, keyName) { - if (obj.isDestroying) { return; } + If the rendering context of this template is the following object: - var eventName = keyName + ':before', listeners, diff; - if (deferred) { - listeners = beforeObserverSet.add(obj, keyName, eventName); - diff = listenersDiff(obj, eventName, listeners); - sendEvent(obj, eventName, [obj, keyName], diff); - } else { - sendEvent(obj, eventName, [obj, keyName]); + ```javascript + { + imageUrl: 'http://lolcats.info/haz-a-funny', + imageTitle: 'A humorous image of a cat' } - } - - function notifyObservers(obj, keyName) { - if (obj.isDestroying) { return; } + ``` - var eventName = keyName + ':change', listeners; - if (deferred) { - listeners = observerSet.add(obj, keyName, eventName); - listenersUnion(obj, eventName, listeners); - } else { - sendEvent(obj, eventName, [obj, keyName]); - } - } + The resulting HTML output will be: - __exports__.propertyWillChange = propertyWillChange; - __exports__.propertyDidChange = propertyDidChange; - __exports__.overrideChains = overrideChains; - __exports__.beginPropertyChanges = beginPropertyChanges; - __exports__.endPropertyChanges = endPropertyChanges; - __exports__.changeProperties = changeProperties; - }); -define("ember-metal/property_get", - ["ember-metal/core","ember-metal/utils","ember-metal/error","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - /** - @module ember-metal - */ + ```html + A humorous image of a cat + ``` - var Ember = __dependency1__["default"]; - var META_KEY = __dependency2__.META_KEY; - var EmberError = __dependency3__["default"]; + `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: - var get; + ```handlebars + imageTitle}} + ``` - var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER; + ### `bind-attr` and the `class` attribute - var IS_GLOBAL_PATH = /^([A-Z$]|([0-9][A-Z$])).*[\.]/; - var HAS_THIS = 'this.'; - var FIRST_KEY = /^([^\.]+)/; + `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: - // .......................................................... - // GET AND SET - // - // If we are on a platform that supports accessors we can use those. - // Otherwise simulate accessors by looking up the property directly on the - // object. + * a string return value of an object's property. + * a boolean return value of an object's property + * a hard-coded value - /** - Gets the value of a property on an object. If the property is computed, - the function will be invoked. If the property is not defined but the - object implements the `unknownProperty` method then that will be invoked. + 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: - If you plan to run on IE8 and older browsers then you should use this - method anytime you want to retrieve a property on an object that you don't - know for sure is private. (Properties beginning with an underscore '_' - are considered private.) + ```javascript + AView = View.extend({ + someProperty: function() { + return "aValue"; + }.property() + }) + ``` - On all newer browsers, you only need to use this method to retrieve - properties if the property might not be defined on the object and you want - to respect the `unknownProperty` handler. Otherwise you can ignore this - method. + ```handlebars + + ``` - Note that if the object itself is `undefined`, this method will throw - an error. + Result in the following rendered output: - @method get - @for Ember - @param {Object} obj The object to retrieve from. - @param {String} keyName The property key to retrieve - @return {Object} the property value or `null`. - */ - get = function get(obj, keyName) { - // Helpers that operate with 'this' within an #each - if (keyName === '') { - return obj; - } + ```html + + ``` - if (!keyName && 'string'===typeof obj) { - keyName = obj; - obj = null; - } + 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("Cannot call get with "+ keyName +" key.", !!keyName); - Ember.assert("Cannot call get with '"+ keyName +"' on an undefined object.", obj !== undefined); + A class name is provided via the syntax + `somePropertyName:class-name-if-true`. - if (obj === null) { return _getPath(obj, keyName); } + ```javascript + AView = View.extend({ + someBool: true + }) + ``` - var meta = obj[META_KEY], desc = meta && meta.descs[keyName], ret; + ```handlebars + + ``` - if (desc === undefined && keyName.indexOf('.') !== -1) { - return _getPath(obj, keyName); - } + Result in the following rendered output: - if (desc) { - return desc.get(obj, keyName); - } else { - if (MANDATORY_SETTER && meta && meta.watching[keyName] > 0) { - ret = meta.values[keyName]; - } else { - ret = obj[keyName]; - } + ```html + + ``` - if (ret === undefined && - 'object' === typeof obj && !(keyName in obj) && 'function' === typeof obj.unknownProperty) { - return obj.unknownProperty(keyName); - } + 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: - return ret; - } - }; + ```handlebars + + ``` - // Currently used only by Ember Data tests - if (Ember.config.overrideAccessors) { - Ember.get = get; - Ember.config.overrideAccessors(); - get = Ember.get; - } + A hard-coded value can be used by prepending `:` to the desired + class name: `:class-name-to-always-apply`. - /** - Normalizes a target/path pair to reflect that actual target/path that should - be observed, etc. This takes into account passing in global property - paths (i.e. a path beginning with a captial letter not defined on the - target). + ```handlebars + + ``` - @private - @method normalizeTuple - @for Ember - @param {Object} target The current target. May be `null`. - @param {String} path A path on the target or a global property path. - @return {Array} a temporary array with the normalized target/path pair. - */ - function normalizeTuple(target, path) { - var hasThis = path.indexOf(HAS_THIS) === 0, - isGlobal = !hasThis && IS_GLOBAL_PATH.test(path), - key; + Results in the following rendered output: - if (!target || isGlobal) target = Ember.lookup; - if (hasThis) path = path.slice(5); + ```html + + ``` - if (target === Ember.lookup) { - key = path.match(FIRST_KEY)[0]; - target = get(target, key); - path = path.slice(key.length+1); - } + All three strategies - string return value, boolean return value, and + hard-coded value – can be combined in a single declaration: - // must return some kind of path to be valid else other things will break. - if (!path || path.length===0) throw new EmberError('Path cannot be empty'); + ```handlebars + + ``` - return [ target, path ]; - }; + @method bind-attr + @for Ember.Handlebars.helpers + @param {Hash} options + @return {String} HTML string + */ + function bindAttrHelper(params, hash, options, env) { + var element = options.element; - function _getPath(root, path) { - var hasThis, parts, tuple, idx, len; + Ember.assert("You must specify at least one hash argument to bind-attr", !!keys(hash).length); - // If there is no root and path is a key name, return that - // property from the global object. - // E.g. get('Ember') -> Ember - if (root === null && path.indexOf('.') === -1) { return get(Ember.lookup, path); } + var view = this; - // detect complicated paths and normalize them - hasThis = path.indexOf(HAS_THIS) === 0; + // Handle classes differently, as we can bind multiple classes + var classNameBindings = hash['class']; + if (classNameBindings !== null && classNameBindings !== undefined) { + if (!isStream(classNameBindings)) { + classNameBindings = applyClassNameBindings(classNameBindings, view); + } - if (!root || hasThis) { - tuple = normalizeTuple(root, path); - root = tuple[0]; - path = tuple[1]; - tuple.length = 0; + var classView = new AttrNode('class', classNameBindings); + classView._morph = env.dom.createAttrMorph(element, 'class'); + view.appendChild(classView); } - parts = path.split("."); - len = parts.length; - for (idx = 0; root != null && idx < len; idx++) { - root = get(root, parts[idx], true); - if (root && root.isDestroyed) { return undefined; } + var attrKeys = keys(hash); + + var attr, path, lazyValue, attrView; + for (var i=0, l=attrKeys.length;i 0) { - if (MANDATORY_SETTER) { - currentValue = meta.values[keyName]; - } else { - currentValue = obj[keyName]; - } - // only trigger a change if the value has changed - if (value !== currentValue) { - propertyWillChange(obj, keyName); - if (MANDATORY_SETTER) { - if ((currentValue === undefined && !(keyName in obj)) || !obj.propertyIsEnumerable(keyName)) { - defineProperty(obj, keyName, null, value); // setup mandatory setter - } else { - meta.values[keyName] = value; - } - } else { - obj[keyName] = value; - } - propertyDidChange(obj, keyName); - } - } else { - obj[keyName] = value; - } + if (options.keywords) { + viewOptions._keywords = options.keywords; } - return value; - }; - // Currently used only by Ember Data tests - // ES6TODO: Verify still true - if (Ember.config.overrideAccessors) { - Ember.set = set; - Ember.config.overrideAccessors(); - set = Ember.set; + // Create the view that will wrap the output of this template/property + // and add it to the nearest view's childViews array. + // See the documentation of Ember._BoundView for more. + var bindView = this.createChildView(viewClass, viewOptions); + + this.appendChild(bindView); + + lazyValue.subscribe(this._wrapAsScheduled(function() { + run.scheduleOnce('render', bindView, 'rerenderIfNeeded'); + })); } - function setPath(root, path, value, tolerant) { - var keyName; + /** + `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`: - // get the last part of the path - keyName = path.slice(path.lastIndexOf('.') + 1); + ```handlebars + {{bind "content.title"}} + ``` - // get the first part of the part - path = (path === keyName) ? keyName : path.slice(0, path.length-(keyName.length+1)); + 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. - // unless the path is this, look up the first part to - // get the root - if (path !== 'this') { - root = getPath(root, path); - } + @private + @method bind + @for Ember.Handlebars.helpers + @param {String} property Property to bind + @param {Function} render Context to provide for rendering + @return {String} HTML string + */ + function bindHelper(params, hash, options, env) { + Ember.assert("You must pass exactly one argument to the bind helper", params.length === 1); - if (!keyName || keyName.length === 0) { - throw new EmberError('Property set failed: You passed an empty path'); - } + var property = params[0]; - if (!root) { - if (tolerant) { return; } - else { throw new EmberError('Property set failed: object in path "'+path+'" could not be found or was destroyed.'); } + if (typeof property === 'string') { + property = this.getStream(property); } - return set(root, keyName, value); + if (options.template) { + options.helperName = 'bind'; + Ember.deprecate("The block form of bind, {{#bind foo}}{{/bind}}, has been deprecated and will be removed."); + bind.call(this, property, hash, options, env, false, exists); + } else { + Ember.deprecate("The `{{bind}}` helper has been deprecated and will be removed."); + + return property; + } } + __exports__.bind = bind; + __exports__.bindHelper = bindHelper; + }); +enifed("ember-htmlbars/helpers/collection", + ["ember-metal/core","ember-metal/mixin","ember-runtime/system/string","ember-metal/property_get","ember-htmlbars/helpers/view","ember-views/views/collection_view","ember-views/streams/utils","ember-metal/enumerable_utils","ember-views/streams/class_name_binding","ember-metal/binding","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __exports__) { + "use strict"; /** - Error-tolerant form of `Ember.set`. Will not blow up if any part of the - chain is `undefined`, `null`, or destroyed. - - This is primarily used when syncing bindings, which may try to update after - an object has been destroyed. - - @method trySet - @for Ember - @param {Object} obj The object to modify. - @param {String} path The property path to set - @param {Object} value The value to set + @module ember + @submodule ember-htmlbars */ - function trySet(root, path, value) { - return set(root, path, value, true); - }; - __exports__.set = set; - __exports__.trySet = trySet; - }); -define("ember-metal/run_loop", - ["ember-metal/core","ember-metal/utils","ember-metal/array","ember-metal/property_events","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; var Ember = __dependency1__["default"]; - var apply = __dependency2__.apply; - var indexOf = __dependency3__.indexOf; - var beginPropertyChanges = __dependency4__.beginPropertyChanges; - var endPropertyChanges = __dependency4__.endPropertyChanges; + // Ember.assert, Ember.deprecate + var IS_BINDING = __dependency2__.IS_BINDING; + var fmt = __dependency3__.fmt; + var get = __dependency4__.get; + var ViewHelper = __dependency5__.ViewHelper; + var CollectionView = __dependency6__["default"]; + var readViewFactory = __dependency7__.readViewFactory; + var map = __dependency8__.map; + var streamifyClassNameBinding = __dependency9__.streamifyClassNameBinding; + var Binding = __dependency10__.Binding; - var onBegin = function(current) { - run.currentRunLoop = current; - }; + /** + `{{collection}}` is a `Ember.Handlebars` helper for adding instances of + `Ember.CollectionView` to a template. See [Ember.CollectionView](/api/classes/Ember.CollectionView.html) + for additional information on how a `CollectionView` functions. - var onEnd = function(current, next) { - run.currentRunLoop = next; - }; + `{{collection}}`'s primary use is as a block helper with a `contentBinding` + option pointing towards an `Ember.Array`-compatible object. An `Ember.View` + instance will be created for each item in its `content` property. Each view + will have its own `content` property set to the appropriate item in the + collection. - // ES6TODO: should Backburner become es6? - var Backburner = requireModule('backburner').Backburner, - backburner = new Backburner(['sync', 'actions', 'destroy'], { - sync: { - before: beginPropertyChanges, - after: endPropertyChanges - }, - defaultQueue: 'actions', - onBegin: onBegin, - onEnd: onEnd, - onErrorTarget: Ember, - onErrorMethod: 'onerror' - }), - slice = [].slice, - concat = [].concat; + The provided block will be applied as the template for each item's view. - // .......................................................... - // run - this is ideally the only public API the dev sees - // + Given an empty `` the following template: - /** - Runs the passed target and method inside of a RunLoop, ensuring any - deferred actions including bindings and views updates are flushed at the - end. + ```handlebars + {{! application.hbs }} + {{#collection content=model}} + Hi {{view.content.name}} + {{/collection}} + ``` - Normally you should not need to invoke this method yourself. However if - you are implementing raw event handlers when interfacing with other - libraries or plugins, you should probably wrap all of your code inside this - call. + And the following application code ```javascript - run(function() { - // code to be execute within a RunLoop + App = Ember.Application.create(); + App.ApplicationRoute = Ember.Route.extend({ + model: function(){ + return [{name: 'Yehuda'},{name: 'Tom'},{name: 'Peter'}]; + } }); ``` - @class run - @namespace Ember - @static - @constructor - @param {Object} [target] target of method to call - @param {Function|String} method Method to invoke. - May be a function or a string. If you pass a string - then it will be looked up on the passed target. - @param {Object} [args*] Any additional arguments you wish to pass to the method. - @return {Object} return value from invoking the passed function. - */ - var run = function() { - return apply(backburner, backburner.run, arguments); - }; + The following HTML will result: - /** - If no run-loop is present, it creates a new one. If a run loop is - present it will queue itself to run on the existing run-loops action - queue. + ```html +
+
Hi Yehuda
+
Hi Tom
+
Hi Peter
+
+ ``` - Please note: This is not for normal usage, and should be used sparingly. + ### Non-block version of collection - If invoked when not within a run loop: + If you provide an `itemViewClass` option that has its own `template` you may + omit the block. - ```javascript - run.join(function() { - // creates a new run-loop - }); + The following template: + + ```handlebars + {{! application.hbs }} + {{collection content=model itemViewClass="an-item"}} ``` - Alternatively, if called within an existing run loop: + And application code ```javascript - run(function() { - // creates a new run-loop - run.join(function() { - // joins with the existing run-loop, and queues for invocation on - // the existing run-loops action queue. - }); + App = Ember.Application.create(); + App.ApplicationRoute = Ember.Route.extend({ + model: function(){ + return [{name: 'Yehuda'},{name: 'Tom'},{name: 'Peter'}]; + } + }); + + App.AnItemView = Ember.View.extend({ + template: Ember.Handlebars.compile("Greetings {{view.content.name}}") }); ``` - @method join - @namespace Ember - @param {Object} [target] target of method to call - @param {Function|String} method Method to invoke. - May be a function or a string. If you pass a string - then it will be looked up on the passed target. - @param {Object} [args*] Any additional arguments you wish to pass to the method. - @return {Object} Return value from invoking the passed function. Please note, - when called within an existing loop, no return value is possible. - */ - run.join = function(target, method /* args */) { - if (!run.currentRunLoop) { - return apply(Ember, run, arguments); - } + Will result in the HTML structure below - var args = slice.call(arguments); - args.unshift('actions'); - apply(run, run.schedule, args); - }; + ```html +
+
Greetings Yehuda
+
Greetings Tom
+
Greetings Peter
+
+ ``` - /** - Provides a useful utility for when integrating with non-Ember libraries - that provide asynchronous callbacks. + ### Specifying a CollectionView subclass - Ember utilizes a run-loop to batch and coalesce changes. This works by - marking the start and end of Ember-related Javascript execution. + By default the `{{collection}}` helper will create an instance of + `Ember.CollectionView`. You can supply a `Ember.CollectionView` subclass to + the helper by passing it as the first argument: - When using events such as a View's click handler, Ember wraps the event - handler in a run-loop, but when integrating with non-Ember libraries this - can be tedious. + ```handlebars + {{#collection "my-custom-collection" content=model}} + Hi {{view.content.name}} + {{/collection}} + ``` - For example, the following is rather verbose but is the correct way to combine - third-party events and Ember code. + This example would look for the class `App.MyCustomCollection`. - ```javascript - var that = this; - jQuery(window).on('resize', function(){ - run(function(){ - that.handleResize(); - }); - }); + ### Forwarded `item.*`-named Options + + As with the `{{view}}`, helper options passed to the `{{collection}}` will be + set on the resulting `Ember.CollectionView` as properties. Additionally, + options prefixed with `item` will be applied to the views rendered for each + item (note the camelcasing): + + ```handlebars + {{#collection content=model + itemTagName="p" + itemClassNames="greeting"}} + Howdy {{view.content.name}} + {{/collection}} ``` - To reduce the boilerplate, the following can be used to construct a - run-loop-wrapped callback handler. + Will result in the following HTML structure: - ```javascript - jQuery(window).on('resize', run.bind(this, this.handleResize)); + ```html +
+

Howdy Yehuda

+

Howdy Tom

+

Howdy Peter

+
``` - @method bind - @namespace run - @param {Object} [target] target of method to call - @param {Function|String} method Method to invoke. - May be a function or a string. If you pass a string - then it will be looked up on the passed target. - @param {Object} [args*] Any additional arguments you wish to pass to the method. - @return {Object} return value from invoking the passed function. Please note, - when called within an existing loop, no return value is possible. - @since 1.4.0 + @method collection + @for Ember.Handlebars.helpers + @param {String} path + @param {Hash} options + @return {String} HTML string + @deprecated Use `{{each}}` helper instead. */ - run.bind = function(target, method /* args*/) { - var args = slice.call(arguments); - return function() { - return apply(run, run.join, args.concat(slice.call(arguments))); - }; - }; + function collectionHelper(params, hash, options, env) { + var path = params[0]; - run.backburner = backburner; - run.currentRunLoop = null; - run.queues = backburner.queueNames; + Ember.deprecate("Using the {{collection}} helper without specifying a class has been" + + " deprecated as the {{each}} helper now supports the same functionality.", path !== 'collection'); - /** - Begins a new RunLoop. Any deferred actions invoked after the begin will - be buffered until you invoke a matching call to `run.end()`. This is - a lower-level way to use a RunLoop instead of using `run()`. + Ember.assert("You cannot pass more than one argument to the collection helper", params.length <= 1); - ```javascript - run.begin(); - // code to be execute within a RunLoop - run.end(); - ``` + var data = env.data; + var template = options.template; + var inverse = options.inverse; + var view = data.view; - @method begin - @return {void} - */ - run.begin = function() { - backburner.begin(); - }; + // This should be deterministic, and should probably come from a + // parent view and not the controller. + var controller = get(view, 'controller'); + var container = (controller && controller.container ? controller.container : view.container); - /** - Ends a RunLoop. This must be called sometime after you call - `run.begin()` to flush any deferred actions. This is a lower-level way - to use a RunLoop instead of using `run()`. + // If passed a path string, convert that into an object. + // Otherwise, just default to the standard class. + var collectionClass; + if (path) { + collectionClass = readViewFactory(path, container); + Ember.assert(fmt("%@ #collection: Could not find collection class %@", [data.view, path]), !!collectionClass); + } + else { + collectionClass = CollectionView; + } - ```javascript - run.begin(); - // code to be execute within a RunLoop - run.end(); - ``` + var itemHash = {}; + var match; - @method end - @return {void} - */ - run.end = function() { - backburner.end(); - }; + // Extract item view class if provided else default to the standard class + var collectionPrototype = collectionClass.proto(); + var itemViewClass; - /** - Array of named queues. This array determines the order in which queues - are flushed at the end of the RunLoop. You can define your own queues by - simply adding the queue name to this array. Normally you should not need - to inspect or modify this property. + if (hash.itemView) { + itemViewClass = readViewFactory(hash.itemView, container); + } else if (hash.itemViewClass) { + itemViewClass = readViewFactory(hash.itemViewClass, container); + } else { + itemViewClass = collectionPrototype.itemViewClass; + } - @property queues - @type Array - @default ['sync', 'actions', 'destroy'] - */ + if (typeof itemViewClass === 'string') { + itemViewClass = container.lookupFactory('view:'+itemViewClass); + } - /** - Adds the passed target/method and any optional arguments to the named - queue to be executed at the end of the RunLoop. If you have not already - started a RunLoop when calling this method one will be started for you - automatically. + Ember.assert(fmt("%@ #collection: Could not find itemViewClass %@", [data.view, itemViewClass]), !!itemViewClass); - At the end of a RunLoop, any methods scheduled in this way will be invoked. - Methods will be invoked in an order matching the named queues defined in - the `run.queues` property. + delete hash.itemViewClass; + delete hash.itemView; - ```javascript - run.schedule('sync', this, function() { - // this will be executed in the first RunLoop queue, when bindings are synced - console.log("scheduled on sync queue"); - }); + // Go through options passed to the {{collection}} helper and extract options + // that configure item views instead of the collection itself. + for (var prop in hash) { + if (prop === 'itemController' || prop === 'itemClassBinding') { + continue; + } + if (hash.hasOwnProperty(prop)) { + match = prop.match(/^item(.)(.*)$/); + if (match) { + var childProp = match[1].toLowerCase() + match[2]; - run.schedule('actions', this, function() { - // this will be executed in the 'actions' queue, after bindings have synced. - console.log("scheduled on actions queue"); - }); + if (IS_BINDING.test(prop)) { + itemHash[childProp] = view._getBindingForStream(hash[prop]); + } else { + itemHash[childProp] = hash[prop]; + } + delete hash[prop]; + } + } + } - // Note the functions will be run in order based on the run queues order. - // Output would be: - // scheduled on sync queue - // scheduled on actions queue - ``` + if (template) { + itemHash.template = template; + delete options.template; + } - @method schedule - @param {String} queue The name of the queue to schedule against. - Default queues are 'sync' and 'actions' - @param {Object} [target] target object to use as the context when invoking a method. - @param {String|Function} method The method to invoke. If you pass a string it - will be resolved on the target object at the time the scheduled item is - invoked allowing you to change the target function. - @param {Object} [arguments*] Optional arguments to be passed to the queued method. - @return {void} - */ - run.schedule = function(queue, target, method) { - checkAutoRun(); - apply(backburner, backburner.schedule, arguments); - }; + var emptyViewClass; + if (inverse) { + emptyViewClass = get(collectionPrototype, 'emptyViewClass'); + emptyViewClass = emptyViewClass.extend({ + template: inverse, + tagName: itemHash.tagName + }); + } else if (hash.emptyViewClass) { + emptyViewClass = readViewFactory(hash.emptyViewClass, container); + } + if (emptyViewClass) { hash.emptyView = emptyViewClass; } - // Used by global test teardown - run.hasScheduledTimers = function() { - return backburner.hasTimers(); - }; + if (hash.keyword) { + itemHash._contextBinding = Binding.oneWay('_parentView.context'); + } else { + itemHash._contextBinding = Binding.oneWay('content'); + } - // Used by global test teardown - run.cancelTimers = function () { - backburner.cancelTimers(); - }; + var viewOptions = ViewHelper.propertiesFromHTMLOptions(itemHash, {}, { data: data }); - /** - Immediately flushes any events scheduled in the 'sync' queue. Bindings - use this queue so this method is a useful way to immediately force all - bindings in the application to sync. + if (hash.itemClassBinding) { + var itemClassBindings = hash.itemClassBinding.split(' '); + viewOptions.classNameBindings = map(itemClassBindings, function(classBinding){ + return streamifyClassNameBinding(view, classBinding); + }); + } - You should call this method anytime you need any changed state to propagate - throughout the app immediately without repainting the UI (which happens - in the later 'render' queue added by the `ember-views` package). + hash.itemViewClass = itemViewClass; + hash._itemViewProps = viewOptions; - ```javascript - run.sync(); - ``` + options.helperName = options.helperName || 'collection'; - @method sync - @return {void} + return env.helpers.view.helperFunction.call(this, [collectionClass], hash, options, env); + } + + __exports__.collectionHelper = collectionHelper; + }); +enifed("ember-htmlbars/helpers/debugger", + ["ember-metal/logger","exports"], + function(__dependency1__, __exports__) { + "use strict"; + /*jshint debug:true*/ + + /** + @module ember + @submodule ember-htmlbars */ - run.sync = function() { - if (backburner.currentInstance) { - backburner.currentInstance.queues.sync.flush(); - } - }; + var Logger = __dependency1__["default"]; /** - Invokes the passed target/method and optional arguments after a specified - period if time. The last parameter of this method must always be a number - of milliseconds. + Execute the `debugger` statement in the current context. - You should use this method whenever you need to run some action after a - period of time instead of using `setTimeout()`. This method will ensure that - items that expire during the same script execution cycle all execute - together, which is often more efficient than using a real setTimeout. + ```handlebars + {{debugger}} + ``` - ```javascript - run.later(myContext, function() { - // code here will execute within a RunLoop in about 500ms with this == myContext - }, 500); + Before invoking the `debugger` statement, there + are a few helpful variables defined in the + body of this helper that you can inspect while + debugging that describe how and where this + helper was invoked: + + - templateContext: this is most likely a controller + from which this template looks up / displays properties + - typeOfTemplateContext: a string description of + what the templateContext is + + For example, if you're wondering why a value `{{foo}}` + isn't rendering as expected within a template, you + could place a `{{debugger}}` statement, and when + the `debugger;` breakpoint is hit, you can inspect + `templateContext`, determine if it's the object you + expect, and/or evaluate expressions in the console + to perform property lookups on the `templateContext`: + + ``` + > templateContext.get('foo') // -> "" ``` - @method later - @param {Object} [target] target of method to invoke - @param {Function|String} method The method to invoke. - If you pass a string it will be resolved on the - target at the time the method is invoked. - @param {Object} [args*] Optional arguments to pass to the timeout. - @param {Number} wait Number of milliseconds to wait. - @return {String} a string you can use to cancel the timer in - `run.cancel` later. + @method debugger + @for Ember.Handlebars.helpers + @param {String} property */ - run.later = function(target, method) { - return apply(backburner, backburner.later, arguments); - }; + function debuggerHelper() { - /** - Schedule a function to run one time during the current RunLoop. This is equivalent - to calling `scheduleOnce` with the "actions" queue. + // These are helpful values you can inspect while debugging. + /* jshint unused: false */ + var view = this; + Logger.info('Use `this` to access the view context.'); - @method once - @param {Object} [target] The target of the method to invoke. - @param {Function|String} method The method to invoke. - If you pass a string it will be resolved on the - target at the time the method is invoked. - @param {Object} [args*] Optional arguments to pass to the timeout. - @return {Object} Timer information for use in cancelling, see `run.cancel`. + debugger; + } + + __exports__.debuggerHelper = debuggerHelper; + }); +enifed("ember-htmlbars/helpers/each", + ["ember-metal/core","ember-views/views/each","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + + /** + @module ember + @submodule ember-htmlbars */ - run.once = function(target, method) { - checkAutoRun(); - var args = slice.call(arguments); - args.unshift('actions'); - return apply(backburner, backburner.scheduleOnce, args); - }; + var Ember = __dependency1__["default"]; + // Ember.assert; + var EachView = __dependency2__["default"]; /** - Schedules a function to run one time in a given queue of the current RunLoop. - Calling this method with the same queue/target/method combination will have - no effect (past the initial call). + The `{{#each}}` helper loops over elements in a collection. It is an extension + of the base Handlebars `{{#each}}` helper. - Note that although you can pass optional arguments these will not be - considered when looking for duplicates. New arguments will replace previous - calls. + The default behavior of `{{#each}}` is to yield its inner block once for every + item in an array. ```javascript - run(function() { - var sayHi = function() { console.log('hi'); } - run.scheduleOnce('afterRender', myContext, sayHi); - run.scheduleOnce('afterRender', myContext, sayHi); - // sayHi will only be executed once, in the afterRender queue of the RunLoop - }); + var developers = [{name: 'Yehuda'},{name: 'Tom'}, {name: 'Paul'}]; ``` - Also note that passing an anonymous function to `run.scheduleOnce` will - not prevent additional calls with an identical anonymous function from - scheduling the items multiple times, e.g.: + ```handlebars + {{#each person in developers}} + {{person.name}} + {{! `this` is whatever it was outside the #each }} + {{/each}} + ``` + + The same rules apply to arrays of primitives, but the items may need to be + references with `{{this}}`. ```javascript - function scheduleIt() { - run.scheduleOnce('actions', myContext, function() { console.log("Closure"); }); - } - scheduleIt(); - scheduleIt(); - // "Closure" will print twice, even though we're using `run.scheduleOnce`, - // because the function we pass to it is anonymous and won't match the - // previously scheduled operation. + var developerNames = ['Yehuda', 'Tom', 'Paul'] ``` - Available queues, and their order, can be found at `run.queues` + ```handlebars + {{#each name in developerNames}} + {{name}} + {{/each}} + ``` - @method scheduleOnce - @param {String} [queue] The name of the queue to schedule against. Default queues are 'sync' and 'actions'. - @param {Object} [target] The target of the method to invoke. - @param {Function|String} method The method to invoke. - If you pass a string it will be resolved on the - target at the time the method is invoked. - @param {Object} [args*] Optional arguments to pass to the timeout. - @return {Object} Timer information for use in cancelling, see `run.cancel`. - */ - run.scheduleOnce = function(queue, target, method) { - checkAutoRun(); - return apply(backburner, backburner.scheduleOnce, arguments); - }; + ### {{else}} condition - /** - Schedules an item to run from within a separate run loop, after - control has been returned to the system. This is equivalent to calling - `run.later` with a wait time of 1ms. + `{{#each}}` can have a matching `{{else}}`. The contents of this block will render + if the collection is empty. + + ``` + {{#each person in developers}} + {{person.name}} + {{else}} +

Sorry, nobody is available for this task.

+ {{/each}} + ``` + + ### Specifying an alternative view for each item + + `itemViewClass` can control which view will be used during the render of each + item's template. + + The following template: + + ```handlebars +
    + {{#each developer in developers itemViewClass="person"}} + {{developer.name}} + {{/each}} +
+ ``` + + Will use the following view for each item ```javascript - run.next(myContext, function() { - // code to be executed in the next run loop, - // which will be scheduled after the current one + App.PersonView = Ember.View.extend({ + tagName: 'li' }); ``` - Multiple operations scheduled with `run.next` will coalesce - into the same later run loop, along with any other operations - scheduled by `run.later` that expire right around the same - time that `run.next` operations will fire. + Resulting in HTML output that looks like the following: - Note that there are often alternatives to using `run.next`. - For instance, if you'd like to schedule an operation to happen - after all DOM element operations have completed within the current - run loop, you can make use of the `afterRender` run loop queue (added - by the `ember-views` package, along with the preceding `render` queue - where all the DOM element operations happen). Example: + ```html +
    +
  • Yehuda
  • +
  • Tom
  • +
  • Paul
  • +
+ ``` + + `itemViewClass` also enables a non-block form of `{{each}}`. The view + must {{#crossLink "Ember.View/toc_templates"}}provide its own template{{/crossLink}}, + and then the block should be dropped. An example that outputs the same HTML + as the previous one: ```javascript - App.MyCollectionView = Ember.CollectionView.extend({ - didInsertElement: function() { - run.scheduleOnce('afterRender', this, 'processChildElements'); - }, - processChildElements: function() { - // ... do something with collectionView's child view - // elements after they've finished rendering, which - // can't be done within the CollectionView's - // `didInsertElement` hook because that gets run - // before the child elements have been added to the DOM. - } + App.PersonView = Ember.View.extend({ + tagName: 'li', + template: '{{developer.name}}' }); ``` - One benefit of the above approach compared to using `run.next` is - that you will be able to perform DOM/CSS operations before unprocessed - elements are rendered to the screen, which may prevent flickering or - other artifacts caused by delaying processing until after rendering. - - The other major benefit to the above approach is that `run.next` - introduces an element of non-determinism, which can make things much - harder to test, due to its reliance on `setTimeout`; it's much harder - to guarantee the order of scheduled operations when they are scheduled - outside of the current run loop, i.e. with `run.next`. + ```handlebars +
    + {{each developer in developers itemViewClass="person"}} +
+ ``` - @method next - @param {Object} [target] target of method to invoke - @param {Function|String} method The method to invoke. - If you pass a string it will be resolved on the - target at the time the method is invoked. - @param {Object} [args*] Optional arguments to pass to the timeout. - @return {Object} Timer information for use in cancelling, see `run.cancel`. - */ - run.next = function() { - var args = slice.call(arguments); - args.push(1); - return apply(backburner, backburner.later, args); - }; + ### Specifying an alternative view for no items (else) - /** - Cancels a scheduled item. Must be a value returned by `run.later()`, - `run.once()`, `run.next()`, `run.debounce()`, or - `run.throttle()`. + The `emptyViewClass` option provides the same flexibility to the `{{else}}` + case of the each helper. ```javascript - var runNext = run.next(myContext, function() { - // will not be executed + App.NoPeopleView = Ember.View.extend({ + tagName: 'li', + template: 'No person is available, sorry' }); - run.cancel(runNext); + ``` - var runLater = run.later(myContext, function() { - // will not be executed - }, 500); - run.cancel(runLater); + ```handlebars +
    + {{#each developer in developers emptyViewClass="no-people"}} +
  • {{developer.name}}
  • + {{/each}} +
+ ``` - var runOnce = run.once(myContext, function() { - // will not be executed - }); - run.cancel(runOnce); + ### Wrapping each item in a controller - var throttle = run.throttle(myContext, function() { - // will not be executed - }, 1, false); - run.cancel(throttle); + Controllers in Ember manage state and decorate data. In many cases, + providing a controller for each item in a list can be useful. + Specifically, an {{#crossLink "Ember.ObjectController"}}Ember.ObjectController{{/crossLink}} + should probably be used. Item controllers are passed the item they + will present as a `model` property, and an object controller will + proxy property lookups to `model` for us. - var debounce = run.debounce(myContext, function() { - // will not be executed - }, 1); - run.cancel(debounce); + This allows state and decoration to be added to the controller + while any other property lookups are delegated to the model. An example: - var debounceImmediate = run.debounce(myContext, function() { - // will be executed since we passed in true (immediate) - }, 100, true); - // the 100ms delay until this method can be called again will be cancelled - run.cancel(debounceImmediate); + ```javascript + App.RecruitController = Ember.ObjectController.extend({ + isAvailableForHire: function() { + return !this.get('isEmployed') && this.get('isSeekingWork'); + }.property('isEmployed', 'isSeekingWork') + }) ``` - @method cancel - @param {Object} timer Timer object to cancel - @return {Boolean} true if cancelled or false/undefined if it wasn't found - */ - run.cancel = function(timer) { - return backburner.cancel(timer); - }; - - /** - Delay calling the target method until the debounce period has elapsed - with no additional debounce calls. If `debounce` is called again before - the specified time has elapsed, the timer is reset and the entire period - must pass again before the target method is called. - - This method should be used when an event may be called multiple times - but the action should only be called once when the event is done firing. - A common example is for scroll events where you only want updates to - happen once scrolling has ceased. - - ```javascript - var myFunc = function() { console.log(this.name + ' ran.'); }; - var myContext = {name: 'debounce'}; + ```handlebars + {{#each person in developers itemController="recruit"}} + {{person.name}} {{#if person.isAvailableForHire}}Hire me!{{/if}} + {{/each}} + ``` - run.debounce(myContext, myFunc, 150); + @method each + @for Ember.Handlebars.helpers + @param [name] {String} name for item (used with `in`) + @param [path] {String} path + @param [options] {Object} Handlebars key/value pairs of options + @param [options.itemViewClass] {String} a path to a view class used for each item + @param [options.emptyViewClass] {String} a path to a view class used for each item + @param [options.itemController] {String} name of a controller to be created for each item + */ + function eachHelper(params, hash, options, env) { + var helperName = 'each'; + var path = params[0] || this.getStream(''); - // less than 150ms passes + Ember.assert( + "If you pass more than one argument to the each helper, " + + "it must be in the form #each foo in bar", + params.length <= 1 + ); - run.debounce(myContext, myFunc, 150); + if (options.template && options.template.blockParams) { + hash.keyword = true; + } - // 150ms passes - // myFunc is invoked with context myContext - // console logs 'debounce ran.' one time. - ``` + Ember.deprecate( + "Using the context switching form of {{each}} is deprecated. " + + "Please use the keyword form (`{{#each foo in bar}}`) instead. " + + "See http://emberjs.com/guides/deprecations/#toc_more-consistent-handlebars-scope " + + "for more details.", + hash.keyword === true || typeof hash.keyword === 'string' + ); - Immediate allows you to run the function immediately, but debounce - other calls for this function until the wait time has elapsed. If - `debounce` is called again before the specified time has elapsed, - the timer is reset and the entire period must pass again before - the method can be called again. + hash.emptyViewClass = Ember._MetamorphView; + hash.dataSource = path; + options.helperName = options.helperName || helperName; - ```javascript - var myFunc = function() { console.log(this.name + ' ran.'); }; - var myContext = {name: 'debounce'}; + return env.helpers.collection.helperFunction.call(this, [EachView], hash, options, env); + } - run.debounce(myContext, myFunc, 150, true); + __exports__.EachView = EachView; + __exports__.eachHelper = eachHelper; + }); +enifed("ember-htmlbars/helpers/if_unless", + ["ember-metal/core","ember-htmlbars/helpers/binding","ember-metal/property_get","ember-metal/utils","ember-views/streams/conditional_stream","ember-metal/streams/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-htmlbars + */ - // console logs 'debounce ran.' one time immediately. - // 100ms passes + var Ember = __dependency1__["default"]; + // Ember.assert + var bind = __dependency2__.bind; - run.debounce(myContext, myFunc, 150, true); + var get = __dependency3__.get; + var isArray = __dependency4__.isArray; + var ConditionalStream = __dependency5__["default"]; + var isStream = __dependency6__.isStream; - // 150ms passes and nothing else is logged to the console and - // the debouncee is no longer being watched + function shouldDisplayIfHelperContent(result) { + var truthy = result && get(result, 'isTruthy'); + if (typeof truthy === 'boolean') { return truthy; } - run.debounce(myContext, myFunc, 150, true); + if (isArray(result)) { + return get(result, 'length') !== 0; + } else { + return !!result; + } + } - // console logs 'debounce ran.' one time immediately. - // 150ms passes and nothing else is logged to the console and - // the debouncee is no longer being watched + var EMPTY_TEMPLATE = { + isHTMLBars: true, + render: function() { + return ''; + } + }; + /** + Use the `boundIf` helper to create a conditional that re-evaluates + whenever the truthiness of the bound value changes. + ```handlebars + {{#boundIf "content.shouldDisplayTitle"}} + {{content.title}} + {{/boundIf}} ``` - @method debounce - @param {Object} [target] target of method to invoke - @param {Function|String} method The method to invoke. - May be a function or a string. If you pass a string - then it will be looked up on the passed target. - @param {Object} [args*] Optional arguments to pass to the timeout. - @param {Number} wait Number of milliseconds to wait. - @param {Boolean} immediate Trigger the function on the leading instead - of the trailing edge of the wait interval. Defaults to false. - @return {Array} Timer information for use in cancelling, see `run.cancel`. + @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 */ - run.debounce = function() { - return apply(backburner, backburner.debounce, arguments); - }; + function boundIfHelper(params, hash, options, env) { + options.helperName = options.helperName || 'boundIf'; + return bind.call(this, params[0], hash, options, env, true, shouldDisplayIfHelperContent, shouldDisplayIfHelperContent, [ + 'isTruthy', + 'length' + ]); + } /** - Ensure that the target method is never called more frequently than - the specified spacing period. The target method is called immediately. - - ```javascript - var myFunc = function() { console.log(this.name + ' ran.'); }; - var myContext = {name: 'throttle'}; - - run.throttle(myContext, myFunc, 150); - // myFunc is invoked with context myContext - // console logs 'throttle ran.' - - // 50ms passes - run.throttle(myContext, myFunc, 150); + @private - // 50ms passes - run.throttle(myContext, myFunc, 150); + Use the `unboundIf` helper to create a conditional that evaluates once. - // 150ms passes - run.throttle(myContext, myFunc, 150); - // myFunc is invoked with context myContext - // console logs 'throttle ran.' + ```handlebars + {{#unboundIf "content.shouldDisplayTitle"}} + {{content.title}} + {{/unboundIf}} ``` - @method throttle - @param {Object} [target] target of method to invoke - @param {Function|String} method The method to invoke. - May be a function or a string. If you pass a string - then it will be looked up on the passed target. - @param {Object} [args*] Optional arguments to pass to the timeout. - @param {Number} spacing Number of milliseconds to space out requests. - @param {Boolean} immediate Trigger the function on the leading instead - of the trailing edge of the wait interval. Defaults to true. - @return {Array} Timer information for use in cancelling, see `run.cancel`. + @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 */ - run.throttle = function() { - return apply(backburner, backburner.throttle, arguments); - }; + function unboundIfHelper(params, hash, options, env) { + var template = options.template; + var value = params[0]; - // Make sure it's not an autorun during testing - function checkAutoRun() { - if (!run.currentRunLoop) { - Ember.assert("You have turned on testing mode, which disabled the run-loop's autorun. You will need to wrap any code with asynchronous side-effects in an run", !Ember.testing); + if (isStream(params[0])) { + value = params[0].value(); + } + + if (!shouldDisplayIfHelperContent(value)) { + template = options.inverse; } + + return template.render(this, env, options.morph.contextualElement); } - /** - Add a new named queue after the specified queue. + function _inlineIfAssertion(params) { + Ember.assert("If helper in inline form expects between two and three arguments", params.length === 2 || params.length === 3); + } - The queue to add will only be added once. + /** + See [boundIf](/api/classes/Ember.Handlebars.helpers.html#method_boundIf) + and [unboundIf](/api/classes/Ember.Handlebars.helpers.html#method_unboundIf) - @method _addQueue - @param {String} name the name of the queue to add. - @param {String} after the name of the queue to add after. - @private + @method if + @for Ember.Handlebars.helpers + @param {Function} context + @param {Hash} options + @return {String} HTML string */ - run._addQueue = function(name, after) { - if (indexOf.call(run.queues, name) === -1) { - run.queues.splice(indexOf.call(run.queues, after)+1, 0, name); + function ifHelper(params, hash, options, env) { + Ember.assert("If helper in block form expect exactly one argument", !options.template || params.length === 1); + + options.inverse = options.inverse || EMPTY_TEMPLATE; + + options.helperName = options.helperName || ('if '); + + if (env.data.isUnbound) { + env.data.isUnbound = false; + return env.helpers.unboundIf.helperFunction.call(this, params, hash, options, env); + } else { + return env.helpers.boundIf.helperFunction.call(this, params, hash, options, env); } } - __exports__["default"] = run - }); -define("ember-metal/set_properties", - ["ember-metal/property_events","ember-metal/property_set","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var changeProperties = __dependency1__.changeProperties; - var set = __dependency2__.set; - /** - Set a list of properties on an object. These properties are set inside - a single `beginPropertyChanges` and `endPropertyChanges` batch, so - observers will be buffered. + @method unless + @for Ember.Handlebars.helpers + @param {Function} context + @param {Hash} options + @return {String} HTML string + */ + function unlessHelper(params, hash, options, env) { + Ember.assert("You must pass exactly one argument to the unless helper", params.length === 1); + Ember.assert("You must pass a block to the unless helper", !!options.template); - ```javascript - var anObject = Ember.Object.create(); + var template = options.template; + var inverse = options.inverse || EMPTY_TEMPLATE; + var helperName = 'unless'; - anObject.setProperties({ - firstName: 'Stanley', - lastName: 'Stuart', - age: 21 - }); - ``` + options.template = inverse; + options.inverse = template; - @method setProperties - @param self - @param {Object} hash - @return self - */ - function setProperties(self, hash) { - changeProperties(function() { - for(var prop in hash) { - if (hash.hasOwnProperty(prop)) { set(self, prop, hash[prop]); } - } - }); - return self; - }; + options.helperName = options.helperName || helperName; + + if (env.data.isUnbound) { + env.data.isUnbound = false; + return env.helpers.unboundIf.helperFunction.call(this, params, hash, options, env); + } else { + return env.helpers.boundIf.helperFunction.call(this, params, hash, options, env); + } + } - __exports__["default"] = setProperties; + __exports__.ifHelper = ifHelper; + __exports__.boundIfHelper = boundIfHelper; + __exports__.unboundIfHelper = unboundIfHelper; + __exports__.unlessHelper = unlessHelper; }); -define("ember-metal/utils", - ["ember-metal/core","ember-metal/platform","ember-metal/array","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { +enifed("ember-htmlbars/helpers/input", + ["ember-views/views/checkbox","ember-views/views/text_field","ember-metal/streams/utils","ember-metal/core","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; - var Ember = __dependency1__["default"]; - var platform = __dependency2__.platform; - var create = __dependency2__.create; - var forEach = __dependency3__.forEach; + var Checkbox = __dependency1__["default"]; + var TextField = __dependency2__["default"]; + var read = __dependency3__.read; + + var Ember = __dependency4__["default"]; + // Ember.assert /** - @module ember-metal + @module ember + @submodule ember-htmlbars */ /** - Prefix used for guids through out Ember. - @private - @property GUID_PREFIX - @for Ember - @type String - @final - */ - var GUID_PREFIX = 'ember'; + 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. - var o_defineProperty = platform.defineProperty, - o_create = create, - // Used for guid generation... - numberCache = [], - stringCache = {}, - uuid = 0; + ## 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: - var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER; + + + + + + + + + + + +
`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` 
- /** - A unique key used to assign guids and other private metadata to objects. - If you inspect an object in your browser debugger you will often see these. - They can be safely ignored. - On browsers that support it, these properties are added with enumeration - disabled so they won't show up when you iterate over your properties. + 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). - @private - @property GUID_KEY - @for Ember - @type String - @final - */ - var GUID_KEY = '__ember' + (+ new Date()); + ## Unbound: - var GUID_DESC = { - writable: false, - configurable: false, - enumerable: false, - value: null - }; + ```handlebars + {{input value="http://www.facebook.com"}} + ``` - /** - Generates a new guid, optionally saving the guid to the object that you - pass in. You will rarely need to use this method. Instead you should - call `Ember.guidFor(obj)`, which return an existing guid if available. - @private - @method generateGuid - @for Ember - @param {Object} [obj] Object the guid will be used for. If passed in, the guid will - be saved on the object and reused whenever you pass the same object - again. + ```html + + ``` - If no object is passed, just generate a new guid. - @param {String} [prefix] Prefix to place in front of the guid. Useful when you want to - separate the guid into separate namespaces. - @return {String} the guid - */ - function generateGuid(obj, prefix) { - if (!prefix) prefix = GUID_PREFIX; - var ret = (prefix + (uuid++)); - if (obj) { - if (obj[GUID_KEY] === null) { - obj[GUID_KEY] = ret; - } else { - GUID_DESC.value = ret; - o_defineProperty(obj, GUID_KEY, GUID_DESC); - } - } - return ret; - } + ## Bound: - /** - Returns a unique id for the object. If the object does not yet have a guid, - one will be assigned to it. You can call this on any object, - `Ember.Object`-based or not, but be aware that it will add a `_guid` - property. + ```javascript + App.ApplicationController = Ember.Controller.extend({ + firstName: "Stanley", + entryNotAllowed: true + }); + ``` - You can also use this method on DOM Element objects. - @private - @method guidFor - @for Ember - @param {Object} obj any object, string, number, Element, or primitive - @return {String} the unique guid for this instance. - */ - function guidFor(obj) { + ```handlebars + {{input type="text" value=firstName disabled=entryNotAllowed size="50"}} + ``` - // special cases where we don't want to add a key to object - if (obj === undefined) return "(undefined)"; - if (obj === null) return "(null)"; - var ret; - var type = typeof obj; + ```html + + ``` - // Don't allow prototype changes to String etc. to change the guidFor - switch(type) { - case 'number': - ret = numberCache[obj]; - if (!ret) ret = numberCache[obj] = 'nu'+obj; - return ret; + ## Actions - case 'string': - ret = stringCache[obj]; - if (!ret) ret = stringCache[obj] = 'st'+(uuid++); - return ret; + The helper can send multiple actions based on user events. - case 'boolean': - return obj ? '(true)' : '(false)'; + The action property defines the action which is sent when + the user presses the return key. - default: - if (obj[GUID_KEY]) return obj[GUID_KEY]; - if (obj === Object) return '(Object)'; - if (obj === Array) return '(Array)'; - ret = 'ember' + (uuid++); + ```handlebars + {{input action="submit"}} + ``` - if (obj[GUID_KEY] === null) { - obj[GUID_KEY] = ret; - } else { - GUID_DESC.value = ret; - o_defineProperty(obj, GUID_KEY, GUID_DESC); - } - return ret; - } - }; + The helper allows some user events to send actions. - // .......................................................... - // META - // + * `enter` + * `insert-newline` + * `escape-press` + * `focus-in` + * `focus-out` + * `key-press` - var META_DESC = { - writable: true, - configurable: false, - enumerable: false, - value: null - }; + For example, if you desire an action to be sent when the input is blurred, + you only need to setup the action name to the event name property. - /** - The key used to store meta information on object for property observing. + ```handlebars + {{input focus-in="alertMessage"}} + ``` - @property META_KEY - @for Ember - @private - @final - @type String - */ - var META_KEY = '__ember_meta__'; + See more about [Text Support Actions](/api/classes/Ember.TextField.html) - var isDefinePropertySimulated = platform.defineProperty.isSimulated; + ## Extension - function Meta(obj) { - this.descs = {}; - this.watching = {}; - this.cache = {}; - this.cacheMeta = {}; - this.source = obj; - } + 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: - Meta.prototype = { - descs: null, - deps: null, - watching: null, - listeners: null, - cache: null, - cacheMeta: null, - source: null, - mixins: null, - bindings: null, - chains: null, - chainWatchers: null, - values: null, - proto: null - }; - if (isDefinePropertySimulated) { - // on platforms that don't support enumerable false - // make meta fail jQuery.isPlainObject() to hide from - // jQuery.extend() by having a property that fails - // hasOwnProperty check. - Meta.prototype.__preventPlainObject__ = true; + ```javascript + Ember.TextField.reopen({ + attributeBindings: ['data-error'] + }); + ``` - // Without non-enumerable properties, meta objects will be output in JSON - // unless explicitly suppressed - Meta.prototype.toJSON = function () { }; - } + 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. - // Placeholder for non-writable metas. - var EMPTY_META = new Meta(null); + See more about [Ember components](/api/classes/Ember.Component.html) - if (MANDATORY_SETTER) { EMPTY_META.values = {}; } - /** - Retrieves the meta hash for an object. If `writable` is true ensures the - hash is writable for this object as well. + ## Use as checkbox - The meta object contains information about computed property descriptors as - well as any watched properties and other information. You generally will - not access this information directly but instead work with higher level - methods that manipulate this hash indirectly. + An `{{input}}` with a `type` of `checkbox` will render an HTML checkbox input. + The following HTML attributes can be set via the helper: - @method meta - @for Ember - @private + * `checked` + * `disabled` + * `tabindex` + * `indeterminate` + * `name` + * `autofocus` + * `form` - @param {Object} obj The object to retrieve meta for - @param {Boolean} [writable=true] Pass `false` if you do not intend to modify - the meta hash, allowing the method to avoid making an unnecessary copy. - @return {Object} the meta hash for an object - */ - function meta(obj, writable) { - var ret = obj[META_KEY]; - if (writable===false) return ret || EMPTY_META; + 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). - if (!ret) { - if (!isDefinePropertySimulated) o_defineProperty(obj, META_KEY, META_DESC); + ## Unbound: - ret = new Meta(obj); + ```handlebars + {{input type="checkbox" name="isAdmin"}} + ``` - if (MANDATORY_SETTER) { ret.values = {}; } + ```html + + ``` - obj[META_KEY] = ret; + ## Bound: - // make sure we don't accidentally try to create constructor like desc - ret.descs.constructor = null; + ```javascript + App.ApplicationController = Ember.Controller.extend({ + isAdmin: true + }); + ``` - } else if (ret.source !== obj) { - if (!isDefinePropertySimulated) o_defineProperty(obj, META_KEY, META_DESC); - ret = o_create(ret); - ret.descs = o_create(ret.descs); - ret.watching = o_create(ret.watching); - ret.cache = {}; - ret.cacheMeta = {}; - ret.source = obj; + ```handlebars + {{input type="checkbox" checked=isAdmin }} + ``` - if (MANDATORY_SETTER) { ret.values = o_create(ret.values); } - obj[META_KEY] = ret; - } - return ret; - }; + ```html + + ``` - function getMeta(obj, property) { - var _meta = meta(obj, false); - return _meta[property]; - }; + ## Extension - function setMeta(obj, property, value) { - var _meta = meta(obj, true); - _meta[property] = value; - return value; - }; + 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: - /** - @deprecated - @private - In order to store defaults for a class, a prototype may need to create - a default meta object, which will be inherited by any objects instantiated - from the class's constructor. + ```javascript + Ember.Checkbox.reopen({ + classNames: ['my-app-checkbox'] + }); + ``` - However, the properties of that meta object are only shallow-cloned, - so if a property is a hash (like the event system's `listeners` hash), - it will by default be shared across all instances of that class. - This method allows extensions to deeply clone a series of nested hashes or - other complex objects. For instance, the event system might pass - `['listeners', 'foo:change', 'ember157']` to `prepareMetaPath`, which will - walk down the keys provided. + @method input + @for Ember.Handlebars.helpers + @param {Hash} options + */ + function inputHelper(params, hash, options, env) { + Ember.assert('You can only pass attributes to the `input` helper, not arguments', params.length === 0); - For each key, if the key does not exist, it is created. If it already - exists and it was inherited from its constructor, the constructor's - key is cloned. + var onEvent = hash.on; + var inputType; - You can also pass false for `writable`, which will simply return - undefined if `prepareMetaPath` discovers any part of the path that - shared or undefined. + inputType = read(hash.type); - @method metaPath - @for Ember - @param {Object} obj The object whose meta we are examining - @param {Array} path An array of keys to walk down - @param {Boolean} writable whether or not to create a new meta - (or meta property) if one does not already exist or if it's - shared with its constructor - */ - function metaPath(obj, path, writable) { - Ember.deprecate("Ember.metaPath is deprecated and will be removed from future releases."); - var _meta = meta(obj, writable), keyName, value; + if (inputType === 'checkbox') { + delete hash.type; - for (var i=0, l=path.length; i + {{loc '_welcome_'}} + + ``` - ```javascript - Ember.isArray(); // false - Ember.isArray([]); // true - Ember.isArray(Ember.ArrayProxy.create({ content: [] })); // true + ```html +
+ Bonjour +
``` - @method isArray - @for Ember - @param {Object} obj The object to test - @return {Boolean} true if the passed object is an array or Array-like - */ - // ES6TODO: Move up to runtime? This is only use in ember-metal by concatenatedProperties - function isArray(obj) { - var modulePath, type; + See [Ember.String.loc](/api/classes/Ember.String.html#method_loc) for how to + set up localized string references. - if (typeof EmberArray === "undefined") { - modulePath = 'ember-runtime/mixins/array'; - if (requirejs._eak_seen[modulePath]) { - EmberArray = requireModule(modulePath)['default']; + @method loc + @for Ember.Handlebars.helpers + @param {String} str The string to format + @see {Ember.String#loc} + */ + function locHelper(params, hash, options, env) { + Ember.assert('You cannot pass bindings to `loc` helper', (function ifParamsContainBindings() { + for (var i = 0, l = params.length; i < l; i++) { + if (isStream(params[i])) { + return false; + } } - } - - if (!obj || obj.setInterval) { return false; } - if (Array.isArray && Array.isArray(obj)) { return true; } - if (EmberArray && EmberArray.detect(obj)) { return true; } + return true; + })()); - type = typeOf(obj); - if ('array' === type) { return true; } - if ((obj.length !== undefined) && 'object' === type) { return true; } - return false; - }; + return loc.apply(this, params); + } + __exports__.locHelper = locHelper; + }); +enifed("ember-htmlbars/helpers/log", + ["ember-metal/logger","ember-metal/streams/utils","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; /** - Forces the passed object to be part of an array. If the object is already - an array or array-like, returns the object. Otherwise adds the object to - an array. If obj is `null` or `undefined`, returns an empty array. - - ```javascript - Ember.makeArray(); // [] - Ember.makeArray(null); // [] - Ember.makeArray(undefined); // [] - Ember.makeArray('lindsay'); // ['lindsay'] - Ember.makeArray([1, 2, 42]); // [1, 2, 42] + @module ember + @submodule ember-htmlbars + */ + var Logger = __dependency1__["default"]; + var read = __dependency2__.read; - var controller = Ember.ArrayProxy.create({ content: [] }); + /** + `log` allows you to output the value of variables in the current rendering + context. `log` also accepts primitive types such as strings or numbers. - Ember.makeArray(controller) === controller; // true + ```handlebars + {{log "myVariable:" myVariable }} ``` - @method makeArray - @for Ember - @param {Object} obj the object - @return {Array} + @method log + @for Ember.Handlebars.helpers + @param {String} property */ - function makeArray(obj) { - if (obj === null || obj === undefined) { return []; } - return isArray(obj) ? obj : [obj]; - }; + function logHelper(params, hash, options, env) { + var logger = Logger.log; + var values = []; - /** - Checks to see if the `methodName` exists on the `obj`. + for (var i = 0; i < params.length; i++) { + values.push(read(params[i])); + } - ```javascript - var foo = { bar: Ember.K, baz: null }; + logger.apply(logger, values); + } - Ember.canInvoke(foo, 'bar'); // true - Ember.canInvoke(foo, 'baz'); // false - Ember.canInvoke(foo, 'bat'); // false - ``` + __exports__.logHelper = logHelper; + }); +enifed("ember-htmlbars/helpers/partial", + ["ember-metal/core","ember-metal/is_none","./binding","ember-metal/streams/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // Ember.assert - @method canInvoke - @for Ember - @param {Object} obj The object to check for the method - @param {String} methodName The method name to check for - @return {Boolean} - */ - function canInvoke(obj, methodName) { - return !!(obj && typeof obj[methodName] === 'function'); - } + var isNone = __dependency2__["default"]; + var bind = __dependency3__.bind; + var isStream = __dependency4__.isStream; /** - Checks to see if the `methodName` exists on the `obj`, - and if it does, invokes it with the arguments passed. + @module ember + @submodule ember-htmlbars + */ - ```javascript - var d = new Date('03/15/2013'); + /** + The `partial` helper renders another template without + changing the template context: - Ember.tryInvoke(d, 'getTime'); // 1363320000000 - Ember.tryInvoke(d, 'setFullYear', [2014]); // 1394856000000 - Ember.tryInvoke(d, 'noSuchMethod', [2014]); // undefined + ```handlebars + {{foo}} + {{partial "nav"}} ``` - @method tryInvoke - @for Ember - @param {Object} obj The object to check for the method - @param {String} methodName The method name to check for - @param {Array} [args] The arguments to pass to the method - @return {*} the return value of the invoked method or undefined if it cannot be invoked - */ - function tryInvoke(obj, methodName, args) { - if (canInvoke(obj, methodName)) { - return args ? applyStr(obj, methodName, args) : applyStr(obj, methodName); - } - }; - - // https://github.com/emberjs/ember.js/pull/1617 - var needsFinallyFix = (function() { - var count = 0; - try{ - try { } - finally { - count++; - throw new Error('needsFinallyFixTest'); - } - } catch (e) {} - - return count !== 1; - })(); + 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. - /** - Provides try/finally functionality, while working - around Safari's double finally bug. + If a "_nav" template isn't found, the `partial` helper will + fall back to a template named "nav". - ```javascript - var tryable = function() { - someResource.lock(); - runCallback(); // May throw error. - }; + ## Bound template names - var finalizer = function() { - someResource.unlock(); - }; + The parameter supplied to `partial` can also be a path + to a property containing a template name, e.g.: - Ember.tryFinally(tryable, finalizer); + ```handlebars + {{partial someTemplateName}} ``` - @method tryFinally - @for Ember - @param {Function} tryable The function to run the try callback - @param {Function} finalizer The function to run the finally callback - @param {Object} [binding] The optional calling object. Defaults to 'this' - @return {*} The return value is the that of the finalizer, - unless that value is undefined, in which case it is the return value - of the tryable - */ + 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. - var tryFinally; - if (needsFinallyFix) { - tryFinally = function(tryable, finalizer, binding) { - var result, finalResult, finalError; - binding = binding || this; + @method partial + @for Ember.Handlebars.helpers + @param {String} partialName the name of the template to render minus the leading underscore + */ - try { - result = tryable.call(binding); - } finally { - try { - finalResult = finalizer.call(binding); - } catch (e) { - finalError = e; - } - } + function partialHelper(params, hash, options, env) { + options.helperName = options.helperName || 'partial'; - if (finalError) { throw finalError; } + var name = params[0]; - return (finalResult === undefined) ? result : finalResult; - }; - } else { - tryFinally = function(tryable, finalizer, binding) { - var result, finalResult; + if (isStream(name)) { + options.template = createPartialTemplate(name); + bind.call(this, name, hash, options, env, true, exists); + } else { + return renderPartial(name, this, env, options.morph.contextualElement); + } + } - binding = binding || this; + __exports__.partialHelper = partialHelper;function exists(value) { + return !isNone(value); + } - try { - result = tryable.call(binding); - } finally { - finalResult = finalizer.call(binding); - } + function lookupPartial(view, templateName) { + var nameParts = templateName.split("/"); + var lastPart = nameParts[nameParts.length - 1]; - return (finalResult === undefined) ? result : finalResult; - }; - } + nameParts[nameParts.length - 1] = "_" + lastPart; - /** - Provides try/catch/finally functionality, while working - around Safari's double finally bug. + var underscoredName = nameParts.join('/'); + var template = view.templateForName(underscoredName); + if (!template) { + template = view.templateForName(templateName); + } - ```javascript - var tryable = function() { - for (i = 0, l = listeners.length; i < l; i++) { - listener = listeners[i]; - beforeValues[i] = listener.before(name, time(), payload); - } + Ember.assert('Unable to find partial with name "'+templateName+'"', !!template); - return callback.call(binding); - }; + return template; + } - var catchable = function(e) { - payload = payload || {}; - payload.exception = e; - }; + function renderPartial(name, view, env, contextualElement) { + var template = lookupPartial(view, name); + return template.render(view, env, contextualElement); + } - var finalizer = function() { - for (i = 0, l = listeners.length; i < l; i++) { - listener = listeners[i]; - listener.after(name, time(), payload, beforeValues[i]); + function createPartialTemplate(nameStream) { + return { + isHTMLBars: true, + render: function(view, env, contextualElement) { + return renderPartial(nameStream.value(), view, env, contextualElement); } }; + } + }); +enifed("ember-htmlbars/helpers/template", + ["ember-metal/core","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // Ember.deprecate; - Ember.tryCatchFinally(tryable, catchable, finalizer); - ``` - - @method tryCatchFinally - @for Ember - @param {Function} tryable The function to run the try callback - @param {Function} catchable The function to run the catchable callback - @param {Function} finalizer The function to run the finally callback - @param {Object} [binding] The optional calling object. Defaults to 'this' - @return {*} The return value is the that of the finalizer, - unless that value is undefined, in which case it is the return value - of the tryable. + /** + @module ember + @submodule ember-htmlbars */ - var tryCatchFinally; - if (needsFinallyFix) { - tryCatchFinally = function(tryable, catchable, finalizer, binding) { - var result, finalResult, finalError; - binding = binding || this; + /** + @deprecated + @method template + @for Ember.Handlebars.helpers + @param {String} templateName the template to render + */ + function templateHelper(params, hash, options, env) { + Ember.deprecate("The `template` helper has been deprecated in favor of the `partial` helper." + + " Please use `partial` instead, which will work the same way."); - try { - result = tryable.call(binding); - } catch(error) { - result = catchable.call(binding, error); - } finally { - try { - finalResult = finalizer.call(binding); - } catch (e) { - finalError = e; - } - } + options.helperName = options.helperName || 'template'; - if (finalError) { throw finalError; } + return env.helpers.partial.helperFunction.call(this, params, hash, options, env); + } - return (finalResult === undefined) ? result : finalResult; - }; - } else { - tryCatchFinally = function(tryable, catchable, finalizer, binding) { - var result, finalResult; + __exports__.templateHelper = templateHelper; + }); +enifed("ember-htmlbars/helpers/text_area", + ["ember-metal/core","ember-views/views/text_area","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-htmlbars + */ - binding = binding || this; + var Ember = __dependency1__["default"]; + // Ember.assert + var TextArea = __dependency2__["default"]; - try { - result = tryable.call(binding); - } catch(error) { - result = catchable.call(binding, error); - } finally { - finalResult = finalizer.call(binding); - } + /** + `{{textarea}}` inserts a new instance of ` + ``` - | Return Value | Meaning | - |---------------|------------------------------------------------------| - | 'string' | String primitive or String object. | - | 'number' | Number primitive or Number object. | - | 'boolean' | Boolean primitive or Boolean object. | - | 'null' | Null value | - | 'undefined' | Undefined value | - | 'function' | A function | - | 'array' | An instance of Array | - | 'regexp' | An instance of RegExp | - | 'date' | An instance of Date | - | 'class' | An Ember class (created using Ember.Object.extend()) | - | 'instance' | An Ember object instance | - | 'error' | An instance of the Error object | - | 'object' | A JavaScript object not inheriting from Ember.Object | + Bound: - Examples: + 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 - Ember.typeOf(); // 'undefined' - Ember.typeOf(null); // 'null' - Ember.typeOf(undefined); // 'undefined' - Ember.typeOf('michael'); // 'string' - Ember.typeOf(new String('michael')); // 'string' - Ember.typeOf(101); // 'number' - Ember.typeOf(new Number(101)); // 'number' - Ember.typeOf(true); // 'boolean' - Ember.typeOf(new Boolean(true)); // 'boolean' - Ember.typeOf(Ember.makeArray); // 'function' - Ember.typeOf([1, 2, 90]); // 'array' - Ember.typeOf(/abc/); // 'regexp' - Ember.typeOf(new Date()); // 'date' - Ember.typeOf(Ember.Object.extend()); // 'class' - Ember.typeOf(Ember.Object.create()); // 'instance' - Ember.typeOf(new Error('teamocil')); // 'error' + App.ApplicationController = Ember.Controller.extend({ + writtenWords: "Lots of text that IS bound" + }); + ``` - // 'normal' JavaScript object - Ember.typeOf({ a: 'b' }); // 'object' + ```handlebars + {{textarea value=writtenWords}} ``` - @method typeOf - @for Ember - @param {Object} item the item to check - @return {String} the type - */ - function typeOf(item) { - var ret, modulePath; + Would result in the following HTML: - // ES6TODO: Depends on Ember.Object which is defined in runtime. - if (typeof EmberObject === "undefined") { - modulePath = 'ember-runtime/system/object'; - if (requirejs._eak_seen[modulePath]) { - EmberObject = requireModule(modulePath)['default']; - } - } + ```html + + ``` - ret = (item === null || item === undefined) ? String(item) : TYPE_MAP[toString.call(item)] || 'object'; + 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`: - if (ret === 'function') { - if (EmberObject && EmberObject.detect(item)) ret = 'class'; - } else if (ret === 'object') { - if (item instanceof Error) ret = 'error'; - else if (EmberObject && item instanceof EmberObject) ret = 'instance'; - else if (item instanceof Date) ret = 'date'; - } - - return ret; - }; + ```javascript + App.ApplicationController = Ember.Controller.extend({ + writtenWords: "Lots of text that IS bound", + outputWrittenWords: Ember.computed.oneWay("writtenWords") + }); + ``` - /** - Convenience method to inspect an object. This method will attempt to - convert the object into a useful string description. + ```handlebars + {{textarea value=writtenWords}} - It is a pretty simple implementation. If you want something more robust, - use something like JSDump: https://github.com/NV/jsDump +
+ {{outputWrittenWords}} +
+ ``` - @method inspect - @for Ember - @param {Object} obj The object you want to inspect. - @return {String} A description of the object - @since 1.4.0 - */ - function inspect(obj) { - var type = typeOf(obj); - if (type === 'array') { - return '[' + obj + ']'; - } - if (type !== 'object') { - return obj + ''; - } + Would result in the following HTML: - var v, ret = []; - for(var key in obj) { - if (obj.hasOwnProperty(key)) { - v = obj[key]; - if (v === 'toString') { continue; } // ignore useless items - if (typeOf(v) === 'function') { v = "function() { ... }"; } - ret.push(key + ": " + v); - } - } - return "{" + ret.join(", ") + "}"; - }; + ```html + - // The following functions are intentionally minified to keep the functions - // below Chrome's function body size inlining limit of 600 chars. + <-- the following div will be updated in real time as you type --> - function apply(t /* target */, m /* method */, a /* args */) { - var l = a && a.length; - if (!a || !l) { return m.call(t); } - switch (l) { - case 1: return m.call(t, a[0]); - case 2: return m.call(t, a[0], a[1]); - case 3: return m.call(t, a[0], a[1], a[2]); - case 4: return m.call(t, a[0], a[1], a[2], a[3]); - case 5: return m.call(t, a[0], a[1], a[2], a[3], a[4]); - default: return m.apply(t, a); - } - }; +
+ Lots of text that IS bound +
+ ``` - function applyStr(t /* target */, m /* method */, a /* args */) { - var l = a && a.length; - if (!a || !l) { return t[m](); } - switch (l) { - case 1: return t[m](a[0]); - case 2: return t[m](a[0], a[1]); - case 3: return t[m](a[0], a[1], a[2]); - case 4: return t[m](a[0], a[1], a[2], a[3]); - case 5: return t[m](a[0], a[1], a[2], a[3], a[4]); - default: return t[m].apply(t, a); - } - }; + 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: - __exports__.generateGuid = generateGuid; - __exports__.GUID_KEY = GUID_KEY; - __exports__.GUID_PREFIX = GUID_PREFIX; - __exports__.guidFor = guidFor; - __exports__.META_DESC = META_DESC; - __exports__.EMPTY_META = EMPTY_META; - __exports__.META_KEY = META_KEY; - __exports__.meta = meta; - __exports__.getMeta = getMeta; - __exports__.setMeta = setMeta; - __exports__.metaPath = metaPath; - __exports__.inspect = inspect; - __exports__.typeOf = typeOf; - __exports__.tryCatchFinally = tryCatchFinally; - __exports__.isArray = isArray; - __exports__.makeArray = makeArray; - __exports__.canInvoke = canInvoke; - __exports__.tryInvoke = tryInvoke; - __exports__.tryFinally = tryFinally; - __exports__.wrap = wrap; - __exports__.applyStr = applyStr; - __exports__.apply = apply; - }); -define("backburner", - ["backburner/utils","backburner/deferred_action_queues","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var Utils = __dependency1__["default"]; - var DeferredActionQueues = __dependency2__.DeferredActionQueues; + ```javascript + App.ApplicationController = Ember.Controller.extend({ + writtenWords: "Lots of text that IS bound", + twoWayWrittenWords: Ember.computed.alias("writtenWords") + }); + ``` - var slice = [].slice, - pop = [].pop, - each = Utils.each, - isString = Utils.isString, - isFunction = Utils.isFunction, - isNumber = Utils.isNumber, - timers = [], - global = this, - NUMBER = /\d+/; + ```handlebars + {{textarea value=writtenWords}} + {{textarea value=twoWayWrittenWords}} + ``` - // In IE 6-8, try/finally doesn't work without a catch. - // Unfortunately, this is impossible to test for since wrapping it in a parent try/catch doesn't trigger the bug. - // This tests for another broken try/catch behavior that only exhibits in the same versions of IE. - var needsIETryCatchFix = (function(e,x){ - try{ x(); } - catch(e) { } // jshint ignore:line - return !!e; - })(); + ```html + - function isCoercableNumber(number) { - return isNumber(number) || NUMBER.test(number); - } + <-- both updated in real time --> - function Backburner(queueNames, options) { - this.queueNames = queueNames; - this.options = options || {}; - if (!this.options.defaultQueue) { - this.options.defaultQueue = queueNames[0]; - } - this.instanceStack = []; - this._debouncees = []; - this._throttlers = []; - } + + ``` - Backburner.prototype = { - queueNames: null, - options: null, - currentInstance: null, - instanceStack: null, + ## Actions - begin: function() { - var options = this.options, - onBegin = options && options.onBegin, - previousInstance = this.currentInstance; + The helper can send multiple actions based on user events. - if (previousInstance) { - this.instanceStack.push(previousInstance); - } + The action property defines the action which is send when + the user presses the return key. - this.currentInstance = new DeferredActionQueues(this.queueNames, options); - if (onBegin) { - onBegin(this.currentInstance, previousInstance); - } - }, + ```handlebars + {{input action="submit"}} + ``` - end: function() { - var options = this.options, - onEnd = options && options.onEnd, - currentInstance = this.currentInstance, - nextInstance = null; + The helper allows some user events to send actions. - // Prevent double-finally bug in Safari 6.0.2 and iOS 6 - // This bug appears to be resolved in Safari 6.0.5 and iOS 7 - var finallyAlreadyCalled = false; - try { - currentInstance.flush(); - } finally { - if (!finallyAlreadyCalled) { - finallyAlreadyCalled = true; + * `enter` + * `insert-newline` + * `escape-press` + * `focus-in` + * `focus-out` + * `key-press` - this.currentInstance = null; + For example, if you desire an action to be sent when the input is blurred, + you only need to setup the action name to the event name property. - if (this.instanceStack.length) { - nextInstance = this.instanceStack.pop(); - this.currentInstance = nextInstance; - } + ```handlebars + {{textarea focus-in="alertMessage"}} + ``` - if (onEnd) { - onEnd(currentInstance, nextInstance); - } - } - } - }, + See more about [Text Support Actions](/api/classes/Ember.TextArea.html) - run: function(target, method /*, args */) { - var onError = getOnError(this.options); + ## Extension - this.begin(); + 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: - if (!method) { - method = target; - target = null; - } + ```javascript + Ember.TextArea.reopen({ + attributeBindings: ['data-error'] + }); + ``` - if (isString(method)) { - method = target[method]; - } + 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. - var args = slice.call(arguments, 2); + See more about [Ember components](/api/classes/Ember.Component.html) - // guard against Safari 6's double-finally bug - var didFinally = false; + @method textarea + @for Ember.Handlebars.helpers + @param {Hash} options + */ + function textareaHelper(params, hash, options, env) { + Ember.assert('You can only pass attributes to the `textarea` helper, not arguments', params.length === 0); - if (onError) { - try { - return method.apply(target, args); - } catch(error) { - onError(error); - } finally { - if (!didFinally) { - didFinally = true; - this.end(); - } - } - } else { - try { - return method.apply(target, args); - } finally { - if (!didFinally) { - didFinally = true; - this.end(); - } - } - } - }, + return env.helpers.view.helperFunction.call(this, [TextArea], hash, options, env); + } - defer: function(queueName, target, method /* , args */) { - if (!method) { - method = target; - target = null; - } + __exports__.textareaHelper = textareaHelper; + }); +enifed("ember-htmlbars/helpers/unbound", + ["ember-htmlbars/system/lookup-helper","ember-metal/streams/utils","ember-metal/error","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var lookupHelper = __dependency1__["default"]; + var read = __dependency2__.read; + var EmberError = __dependency3__["default"]; - if (isString(method)) { - method = target[method]; - } + /** + @module ember + @submodule ember-htmlbars + */ - var stack = this.DEBUG ? new Error() : undefined, - args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; - if (!this.currentInstance) { createAutorun(this); } - return this.currentInstance.schedule(queueName, target, method, args, false, stack); - }, + /** + `unbound` allows you to output a property without binding. *Important:* The + output will not be updated if the property changes. Use with caution. - deferOnce: function(queueName, target, method /* , args */) { - if (!method) { - method = target; - target = null; - } + ```handlebars +
{{unbound somePropertyThatDoesntChange}}
+ ``` - if (isString(method)) { - method = target[method]; - } + `unbound` can also be used in conjunction with a bound helper to + render it in its unbound form: - var stack = this.DEBUG ? new Error() : undefined, - args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; - if (!this.currentInstance) { createAutorun(this); } - return this.currentInstance.schedule(queueName, target, method, args, true, stack); - }, + ```handlebars +
{{unbound helperName somePropertyThatDoesntChange}}
+ ``` - setTimeout: function() { - var args = slice.call(arguments), - length = args.length, - method, wait, target, - methodOrTarget, methodOrWait, methodOrArgs; + @method unbound + @for Ember.Handlebars.helpers + @param {String} property + @return {String} HTML string + */ + function unboundHelper(params, hash, options, env) { + var length = params.length; + var result; - if (length === 0) { - return; - } else if (length === 1) { - method = args.shift(); - wait = 0; - } else if (length === 2) { - methodOrTarget = args[0]; - methodOrWait = args[1]; + options.helperName = options.helperName || 'unbound'; - if (isFunction(methodOrWait) || isFunction(methodOrTarget[methodOrWait])) { - target = args.shift(); - method = args.shift(); - wait = 0; - } else if (isCoercableNumber(methodOrWait)) { - method = args.shift(); - wait = args.shift(); - } else { - method = args.shift(); - wait = 0; - } - } else { - var last = args[args.length - 1]; + if (length === 1) { + result = read(params[0]); + } else if (length >= 2) { + env.data.isUnbound = true; - if (isCoercableNumber(last)) { - wait = args.pop(); - } else { - wait = 0; - } + var helperName = params[0]._label; + var args = []; - methodOrTarget = args[0]; - methodOrArgs = args[1]; + for (var i = 1, l = params.length; i < l; i++) { + var value = read(params[i]); - if (isFunction(methodOrArgs) || (isString(methodOrArgs) && - methodOrTarget !== null && - methodOrArgs in methodOrTarget)) { - target = args.shift(); - method = args.shift(); - } else { - method = args.shift(); - } + args.push(value); } - var executeAt = (+new Date()) + parseInt(wait, 10); + var helper = lookupHelper(helperName, this, env); - if (isString(method)) { - method = target[method]; + if (!helper) { + throw new EmberError('HTMLBars error: Could not find component or helper named ' + helperName + '.'); } - var onError = getOnError(this.options); - - function fn() { - if (onError) { - try { - method.apply(target, args); - } catch (e) { - onError(e); - } - } else { - method.apply(target, args); - } - } + result = helper.helperFunction.call(this, args, hash, options, env); - // find position to insert - var i = searchTimer(executeAt, timers); + delete env.data.isUnbound; + } - timers.splice(i, 0, executeAt, fn); + return result; + } - updateLaterTimer(this, executeAt, wait); + __exports__.unboundHelper = unboundHelper; + }); +enifed("ember-htmlbars/helpers/view", + ["ember-metal/core","ember-runtime/system/object","ember-metal/property_get","ember-metal/streams/simple","ember-metal/keys","ember-metal/mixin","ember-metal/streams/utils","ember-views/streams/utils","ember-views/views/view","ember-metal/enumerable_utils","ember-views/streams/class_name_binding","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-htmlbars + */ - return fn; - }, + var Ember = __dependency1__["default"]; + // Ember.warn, Ember.assert - throttle: function(target, method /* , args, wait, [immediate] */) { - var self = this, - args = arguments, - immediate = pop.call(args), - wait, - throttler, - index, - timer; + var EmberObject = __dependency2__["default"]; + var get = __dependency3__.get; + var SimpleStream = __dependency4__["default"]; + var keys = __dependency5__["default"]; + var IS_BINDING = __dependency6__.IS_BINDING; + var read = __dependency7__.read; + var isStream = __dependency7__.isStream; + var readViewFactory = __dependency8__.readViewFactory; + var View = __dependency9__["default"]; + + var map = __dependency10__.map; + var streamifyClassNameBinding = __dependency11__.streamifyClassNameBinding; + + function makeBindings(hash, options, view) { + for (var prop in hash) { + var value = hash[prop]; - if (isNumber(immediate) || isString(immediate)) { - wait = immediate; - immediate = true; - } else { - wait = pop.call(args); + // Classes are processed separately + if (prop === 'class' && isStream(value)) { + hash.classBinding = value._label; + delete hash['class']; + continue; } - wait = parseInt(wait, 10); - - index = findThrottler(target, method, this._throttlers); - if (index > -1) { return this._throttlers[index]; } // throttled + if (prop === 'classBinding') { + continue; + } - timer = global.setTimeout(function() { - if (!immediate) { - self.run.apply(self, args); + if (IS_BINDING.test(prop)) { + if (isStream(value)) { + Ember.warn("You're attempting to render a view by passing " + + prop + " " + + "to a view helper without a quoted value, " + + "but this syntax is ambiguous. You should either surround " + + prop + "'s value in quotes or remove `Binding` " + + "from " + prop + "."); + } else if (typeof value === 'string') { + hash[prop] = view._getBindingForStream(value); } - var index = findThrottler(target, method, self._throttlers); - if (index > -1) { - self._throttlers.splice(index, 1); + } else { + if (isStream(value) && prop !== 'id') { + hash[prop + 'Binding'] = view._getBindingForStream(value); + delete hash[prop]; } - }, wait); - - if (immediate) { - self.run.apply(self, args); } + } + } - throttler = [target, method, timer]; - - this._throttlers.push(throttler); + var ViewHelper = EmberObject.create({ + propertiesFromHTMLOptions: function(hash, options, env) { + var view = env.data.view; + var classes = read(hash['class']); - return throttler; - }, + var extensions = { + helperName: options.helperName || '' + }; - debounce: function(target, method /* , args, wait, [immediate] */) { - var self = this, - args = arguments, - immediate = pop.call(args), - wait, - index, - debouncee, - timer; + if (hash.id) { + extensions.elementId = read(hash.id); + } - if (isNumber(immediate) || isString(immediate)) { - wait = immediate; - immediate = false; - } else { - wait = pop.call(args); + if (hash.tag) { + extensions.tagName = hash.tag; } - wait = parseInt(wait, 10); - // Remove debouncee - index = findDebouncee(target, method, this._debouncees); + if (classes) { + classes = classes.split(' '); + extensions.classNames = classes; + } - if (index > -1) { - debouncee = this._debouncees[index]; - this._debouncees.splice(index, 1); - clearTimeout(debouncee[2]); + if (hash.classBinding) { + extensions.classNameBindings = hash.classBinding.split(' '); } - timer = global.setTimeout(function() { - if (!immediate) { - self.run.apply(self, args); - } - var index = findDebouncee(target, method, self._debouncees); - if (index > -1) { - self._debouncees.splice(index, 1); + if (hash.classNameBindings) { + if (extensions.classNameBindings === undefined) { + extensions.classNameBindings = []; } - }, wait); + extensions.classNameBindings = extensions.classNameBindings.concat(hash.classNameBindings.split(' ')); + } - if (immediate && index === -1) { - self.run.apply(self, args); + if (hash.attributeBindings) { + Ember.assert("Setting 'attributeBindings' via template helpers is not allowed." + + " Please subclass Ember.View and set it there instead."); + extensions.attributeBindings = null; } - debouncee = [target, method, timer]; + // Set the proper context for all bindings passed to the helper. This applies to regular attribute bindings + // as well as class name bindings. If the bindings are local, make them relative to the current context + // instead of the view. + + var hashKeys = keys(hash); - self._debouncees.push(debouncee); + for (var i = 0, l = hashKeys.length; i < l; i++) { + var prop = hashKeys[i]; - return debouncee; + if (prop !== 'classNameBindings') { + extensions[prop] = hash[prop]; + } + } + + if (extensions.classNameBindings) { + extensions.classNameBindings = map(extensions.classNameBindings, function(classNameBinding){ + var binding = streamifyClassNameBinding(view, classNameBinding); + if (isStream(binding)) { + return binding; + } else { + // returning a stream informs the classNameBindings logic + // in views/view that this value is already processed. + return new SimpleStream(binding); + } + }); + } + + return extensions; }, - cancelTimers: function() { - var clearItems = function(item) { - clearTimeout(item[2]); - }; + helper: function(newView, hash, options, env) { + var data = env.data; + var template = options.template; + var newViewProto; - each(this._throttlers, clearItems); - this._throttlers = []; + makeBindings(hash, options, env.data.view); - each(this._debouncees, clearItems); - this._debouncees = []; + var viewOptions = this.propertiesFromHTMLOptions(hash, options, env); + var currentView = data.view; - if (this._laterTimer) { - clearTimeout(this._laterTimer); - this._laterTimer = null; + if (View.detectInstance(newView)) { + newViewProto = newView; + } else { + newViewProto = newView.proto(); } - timers = []; - if (this._autorun) { - clearTimeout(this._autorun); - this._autorun = null; + if (template) { + Ember.assert( + "You cannot provide a template block if you also specified a templateName", + !get(viewOptions, 'templateName') && !get(newViewProto, 'templateName') + ); + viewOptions.template = template; } - }, - hasTimers: function() { - return !!timers.length || !!this._debouncees.length || !!this._throttlers.length || this._autorun; - }, + // We only want to override the `_context` computed property if there is + // no specified controller. See View#_context for more information. + if (!newViewProto.controller && !newViewProto.controllerBinding && !viewOptions.controller && !viewOptions.controllerBinding) { + viewOptions._context = get(currentView, 'context'); // TODO: is this right?! + } - cancel: function(timer) { - var timerType = typeof timer; + viewOptions._morph = options.morph; - if (timer && timerType === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce - return timer.queue.cancel(timer); - } else if (timerType === 'function') { // we're cancelling a setTimeout - for (var i = 0, l = timers.length; i < l; i += 2) { - if (timers[i + 1] === timer) { - timers.splice(i, 2); // remove the two elements - return true; - } - } - } else if (Object.prototype.toString.call(timer) === "[object Array]"){ // we're cancelling a throttle or debounce - return this._cancelItem(findThrottler, this._throttlers, timer) || - this._cancelItem(findDebouncee, this._debouncees, timer); - } else { - return; // timer was null or not a timer - } + currentView.appendChild(newView, viewOptions); }, - _cancelItem: function(findMethod, array, timer){ - var item, - index; + instanceHelper: function(newView, hash, options, env) { + var data = env.data; + var template = options.template; - if (timer.length < 3) { return false; } + makeBindings(hash, options, env.data.view); - index = findMethod(timer[0], timer[1], array); + Ember.assert( + 'Only a instance of a view may be passed to the ViewHelper.instanceHelper', + View.detectInstance(newView) + ); - if(index > -1) { + var viewOptions = this.propertiesFromHTMLOptions(hash, options, env); + var currentView = data.view; - item = array[index]; + if (template) { + Ember.assert( + "You cannot provide a template block if you also specified a templateName", + !get(viewOptions, 'templateName') && !get(newView, 'templateName') + ); + viewOptions.template = template; + } - if(item[2] === timer[2]){ - array.splice(index, 1); - clearTimeout(timer[2]); - return true; - } + // We only want to override the `_context` computed property if there is + // no specified controller. See View#_context for more information. + if (!newView.controller && !newView.controllerBinding && + !viewOptions.controller && !viewOptions.controllerBinding) { + viewOptions._context = get(currentView, 'context'); // TODO: is this right?! } - return false; - } - }; + viewOptions._morph = options.morph; - Backburner.prototype.schedule = Backburner.prototype.defer; - Backburner.prototype.scheduleOnce = Backburner.prototype.deferOnce; - Backburner.prototype.later = Backburner.prototype.setTimeout; + currentView.appendChild(newView, viewOptions); + } + }); + __exports__.ViewHelper = ViewHelper; + /** + `{{view}}` inserts a new instance of an `Ember.View` into a template passing its + options to the `Ember.View`'s `create` method and using the supplied block as + the view's own template. - if (needsIETryCatchFix) { - var originalRun = Backburner.prototype.run; - Backburner.prototype.run = wrapInTryCatch(originalRun); + An empty `` and the following template: - var originalEnd = Backburner.prototype.end; - Backburner.prototype.end = wrapInTryCatch(originalEnd); - } + ```handlebars + A span: + {{#view tagName="span"}} + hello. + {{/view}} + ``` - function wrapInTryCatch(func) { - return function () { - try { - return func.apply(this, arguments); - } catch (e) { - throw e; - } - }; - } + Will result in HTML structure: - function getOnError(options) { - return options.onError || (options.onErrorTarget && options.onErrorTarget[options.onErrorMethod]); - } - - - function createAutorun(backburner) { - backburner.begin(); - backburner._autorun = global.setTimeout(function() { - backburner._autorun = null; - backburner.end(); - }); - } - - function updateLaterTimer(self, executeAt, wait) { - if (!self._laterTimer || executeAt < self._laterTimerExpiresAt) { - self._laterTimer = global.setTimeout(function() { - self._laterTimer = null; - self._laterTimerExpiresAt = null; - executeTimers(self); - }, wait); - self._laterTimerExpiresAt = executeAt; - } - } + ```html + + - function executeTimers(self) { - var now = +new Date(), - time, fns, i, l; +
+ A span: + + Hello. + +
+ + ``` - self.run(function() { - i = searchTimer(now, timers); + ### `parentView` setting - fns = timers.splice(0, i); + The `parentView` property of the new `Ember.View` instance created through + `{{view}}` will be set to the `Ember.View` instance of the template where + `{{view}}` was called. - for (i = 1, l = fns.length; i < l; i += 2) { - self.schedule(self.options.defaultQueue, null, fns[i]); - } + ```javascript + aView = Ember.View.create({ + template: Ember.Handlebars.compile("{{#view}} my parent: {{parentView.elementId}} {{/view}}") }); - if (timers.length) { - updateLaterTimer(self, timers[0], timers[0] - now); - } - } + aView.appendTo('body'); + ``` - function findDebouncee(target, method, debouncees) { - return findItem(target, method, debouncees); - } + Will result in HTML structure: - function findThrottler(target, method, throttlers) { - return findItem(target, method, throttlers); - } + ```html +
+
+ my parent: ember1 +
+
+ ``` - function findItem(target, method, collection) { - var item, - index = -1; + ### Setting CSS id and class attributes - for (var i = 0, l = collection.length; i < l; i++) { - item = collection[i]; - if (item[0] === target && item[1] === method) { - index = i; - break; - } - } + The HTML `id` attribute can be set on the `{{view}}`'s resulting element with + the `id` option. This option will _not_ be passed to `Ember.View.create`. - return index; - } + ```handlebars + {{#view tagName="span" id="a-custom-id"}} + hello. + {{/view}} + ``` - function searchTimer(time, timers) { - var start = 0, - end = timers.length - 2, - middle, l; + Results in the following HTML structure: - while (start < end) { - // since timers is an array of pairs 'l' will always - // be an integer - l = (end - start) / 2; + ```html +
+ + hello. + +
+ ``` - // compensate for the index in case even number - // of pairs inside timers - middle = start + l - (l % 2); + The HTML `class` attribute can be set on the `{{view}}`'s resulting element + with the `class` or `classNameBindings` options. The `class` option will + directly set the CSS `class` attribute and will not be passed to + `Ember.View.create`. `classNameBindings` will be passed to `create` and use + `Ember.View`'s class name binding functionality: - if (time >= timers[middle]) { - start = middle + 2; - } else { - end = middle; - } - } + ```handlebars + {{#view tagName="span" class="a-custom-class"}} + hello. + {{/view}} + ``` - return (time >= timers[start]) ? start + 2 : start; - } + Results in the following HTML structure: - __exports__.Backburner = Backburner; - }); -define("backburner/deferred_action_queues", - ["backburner/utils","backburner/queue","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var Utils = __dependency1__["default"]; - var Queue = __dependency2__.Queue; + ```html +
+ + hello. + +
+ ``` - var each = Utils.each, - isString = Utils.isString; + ### Supplying a different view class - function DeferredActionQueues(queueNames, options) { - var queues = this.queues = {}; - this.queueNames = queueNames = queueNames || []; + `{{view}}` can take an optional first argument before its supplied options to + specify a path to a custom view class. - this.options = options; + ```handlebars + {{#view "custom"}}{{! will look up App.CustomView }} + hello. + {{/view}} + ``` - each(queueNames, function(queueName) { - queues[queueName] = new Queue(this, queueName, options); - }); - } + The first argument can also be a relative path accessible from the current + context. - DeferredActionQueues.prototype = { - queueNames: null, - queues: null, - options: null, + ```javascript + MyApp = Ember.Application.create({}); + MyApp.OuterView = Ember.View.extend({ + innerViewClass: Ember.View.extend({ + classNames: ['a-custom-view-class-as-property'] + }), + template: Ember.Handlebars.compile('{{#view view.innerViewClass}} hi {{/view}}') + }); - schedule: function(queueName, target, method, args, onceFlag, stack) { - var queues = this.queues, - queue = queues[queueName]; + MyApp.OuterView.create().appendTo('body'); + ``` - if (!queue) { throw new Error("You attempted to schedule an action in a queue (" + queueName + ") that doesn't exist"); } + Will result in the following HTML: - if (onceFlag) { - return queue.pushUnique(target, method, args, stack); - } else { - return queue.push(target, method, args, stack); - } - }, + ```html +
+
+ hi +
+
+ ``` - invoke: function(target, method, args, _) { - if (args && args.length > 0) { - method.apply(target, args); - } else { - method.call(target); - } - }, + ### Blockless use - invokeWithOnError: function(target, method, args, onError) { - try { - if (args && args.length > 0) { - method.apply(target, args); - } else { - method.call(target); - } - } catch(error) { - onError(error); - } - }, + If you supply a custom `Ember.View` subclass that specifies its own template + or provide a `templateName` option to `{{view}}` it can be used without + supplying a block. Attempts to use both a `templateName` option and supply a + block will throw an error. - flush: function() { - var queues = this.queues, - queueNames = this.queueNames, - queueName, queue, queueItems, priorQueueNameIndex, - queueNameIndex = 0, numberOfQueues = queueNames.length, - options = this.options, - onError = options.onError || (options.onErrorTarget && options.onErrorTarget[options.onErrorMethod]), - invoke = onError ? this.invokeWithOnError : this.invoke; - - outerloop: - while (queueNameIndex < numberOfQueues) { - queueName = queueNames[queueNameIndex]; - queue = queues[queueName]; - queueItems = queue._queueBeingFlushed = queue._queue.slice(); - queue._queue = []; + ```javascript + var App = Ember.Application.create(); + App.WithTemplateDefinedView = Ember.View.extend({ + templateName: 'defined-template' + }); + ``` - var queueOptions = queue.options, // TODO: write a test for this - before = queueOptions && queueOptions.before, - after = queueOptions && queueOptions.after, - target, method, args, stack, - queueIndex = 0, numberOfQueueItems = queueItems.length; + ```handlebars + {{! application.hbs }} + {{view 'with-template-defined'}} + ``` - if (numberOfQueueItems && before) { before(); } + ```handlebars + {{! defined-template.hbs }} + Some content for the defined template view. + ``` - while (queueIndex < numberOfQueueItems) { - target = queueItems[queueIndex]; - method = queueItems[queueIndex+1]; - args = queueItems[queueIndex+2]; - stack = queueItems[queueIndex+3]; // Debugging assistance + ### `viewName` property - if (isString(method)) { method = target[method]; } + You can supply a `viewName` option to `{{view}}`. The `Ember.View` instance + will be referenced as a property of its parent view by this name. - // method could have been nullified / canceled during flush - if (method) { - invoke(target, method, args, onError); - } + ```javascript + aView = Ember.View.create({ + template: Ember.Handlebars.compile('{{#view viewName="aChildByName"}} hi {{/view}}') + }); - queueIndex += 4; - } + aView.appendTo('body'); + aView.get('aChildByName') // the instance of Ember.View created by {{view}} helper + ``` - queue._queueBeingFlushed = null; - if (numberOfQueueItems && after) { after(); } + @method view + @for Ember.Handlebars.helpers + @param {String} path + @param {Hash} options + @return {String} HTML string + */ + function viewHelper(params, hash, options, env) { + Ember.assert("The view helper only takes a single argument", params.length <= 2); - if ((priorQueueNameIndex = indexOfPriorQueueWithActions(this, queueNameIndex)) !== -1) { - queueNameIndex = priorQueueNameIndex; - continue outerloop; - } + var container = this.container || this._keywords.view.value().container; + var viewClass; - queueNameIndex++; + // If no path is provided, treat path param as options + // and get an instance of the registered `view:toplevel` + if (params.length === 0) { + if (container) { + viewClass = container.lookupFactory('view:toplevel'); + } else { + viewClass = View; } + } else { + var pathStream = params[0]; + viewClass = readViewFactory(pathStream, container); } - }; - function indexOfPriorQueueWithActions(daq, currentQueueIndex) { - var queueName, queue; - - for (var i = 0, l = currentQueueIndex; i <= l; i++) { - queueName = daq.queueNames[i]; - queue = daq.queues[queueName]; - if (queue._queue.length) { return i; } - } + options.helperName = options.helperName || 'view'; - return -1; + return ViewHelper.helper(viewClass, hash, options, env); } - __exports__.DeferredActionQueues = DeferredActionQueues; + __exports__.viewHelper = viewHelper; }); -define("backburner/queue", - ["exports"], - function(__exports__) { +enifed("ember-htmlbars/helpers/with", + ["ember-metal/core","ember-metal/is_none","ember-htmlbars/helpers/binding","ember-views/views/with_view","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; - function Queue(daq, name, options) { - this.daq = daq; - this.name = name; - this.globalOptions = options; - this.options = options[name]; - this._queue = []; - } + /** + @module ember + @submodule ember-htmlbars + */ - Queue.prototype = { - daq: null, - name: null, - options: null, - onError: null, - _queue: null, + var Ember = __dependency1__["default"]; + // Ember.assert + var isNone = __dependency2__["default"]; + var bind = __dependency3__.bind; + var WithView = __dependency4__["default"]; - push: function(target, method, args, stack) { - var queue = this._queue; - queue.push(target, method, args, stack); - return {queue: this, target: target, method: method}; - }, + /** + Use the `{{with}}` helper when you want to aliases the to a new name. It's helpful + for semantic clarity and to retain default scope or to reference from another + `{{with}}` block. - pushUnique: function(target, method, args, stack) { - var queue = this._queue, currentTarget, currentMethod, i, l; + ```handlebars + // posts might not be + {{#with user.posts as blogPosts}} +
+ There are {{blogPosts.length}} blog posts written by {{user.name}}. +
- for (i = 0, l = queue.length; i < l; i += 4) { - currentTarget = queue[i]; - currentMethod = queue[i+1]; + {{#each post in blogPosts}} +
  • {{post.title}}
  • + {{/each}} + {{/with}} + ``` - if (currentTarget === target && currentMethod === method) { - queue[i+2] = args; // replace args - queue[i+3] = stack; // replace stack - return {queue: this, target: target, method: method}; - } - } + Without the `as` operator, it would be impossible to reference `user.name` in the example above. - queue.push(target, method, args, stack); - return {queue: this, target: target, method: method}; - }, + 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}}`. - // TODO: remove me, only being used for Ember.run.sync - flush: function() { - var queue = this._queue, - globalOptions = this.globalOptions, - options = this.options, - before = options && options.before, - after = options && options.after, - onError = globalOptions.onError || (globalOptions.onErrorTarget && globalOptions.onErrorTarget[globalOptions.onErrorMethod]), - target, method, args, stack, i, l = queue.length; - - if (l && before) { before(); } - for (i = 0; i < l; i += 4) { - target = queue[i]; - method = queue[i+1]; - args = queue[i+2]; - stack = queue[i+3]; // Debugging assistance - - // TODO: error handling - if (args && args.length > 0) { - if (onError) { - try { - method.apply(target, args); - } catch (e) { - onError(e); - } - } else { - method.apply(target, args); - } - } else { - if (onError) { - try { - method.call(target); - } catch(e) { - onError(e); - } - } else { - method.call(target); - } - } - } - if (l && after) { after(); } + ### `controller` option - // check if new items have been added - if (queue.length > l) { - this._queue = queue.slice(l); - this.flush(); - } else { - this._queue.length = 0; - } - }, + Adding `controller='something'` instructs the `{{with}}` helper to create and use an instance of + the specified controller wrapping the aliased keyword. - cancel: function(actionToCancel) { - var queue = this._queue, currentTarget, currentMethod, i, l; + This is very similar to using an `itemController` option with the `{{each}}` helper. - for (i = 0, l = queue.length; i < l; i += 4) { - currentTarget = queue[i]; - currentMethod = queue[i+1]; + ```handlebars + {{#with users.posts as posts controller='userBlogPosts'}} + {{!- `posts` is wrapped in our controller instance }} + {{/with}} + ``` - if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.method) { - queue.splice(i, 4); - return true; - } - } + In the above example, the `posts` keyword is now wrapped in the `userBlogPost` controller, + which provides an elegant way to decorate the context with custom + functions/properties. - // if not found in current queue - // could be in the queue that is being flushed - queue = this._queueBeingFlushed; - if (!queue) { - return; - } - for (i = 0, l = queue.length; i < l; i += 4) { - currentTarget = queue[i]; - currentMethod = queue[i+1]; + @method with + @for Ember.Handlebars.helpers + @param {Function} context + @param {Hash} options + @return {String} HTML string + */ + function withHelper(params, hash, options, env) { + Ember.assert( + "{{#with foo}} must be called with a single argument or the use the " + + "{{#with foo as bar}} syntax", + params.length === 1 + ); - if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.method) { - // don't mess with array during flush - // just nullify the method - queue[i+1] = null; - return true; - } - } + Ember.assert( + "The {{#with}} helper must be called with a block", + !!options.template + ); + + var preserveContext; + + if (options.template.blockParams) { + preserveContext = true; + } else { + Ember.deprecate( + "Using the context switching form of `{{with}}` is deprecated. " + + "Please use the keyword form (`{{with foo as bar}}`) instead. " + + "See http://emberjs.com/guides/deprecations/#toc_more-consistent-handlebars-scope " + + "for more details." + ); + preserveContext = false; } - }; - __exports__.Queue = Queue; + bind.call(this, params[0], hash, options, env, preserveContext, exists, undefined, undefined, WithView); + } + + __exports__.withHelper = withHelper;function exists(value) { + return !isNone(value); + } }); -define("backburner/utils", - ["exports"], - function(__exports__) { +enifed("ember-htmlbars/helpers/yield", + ["ember-metal/core","ember-metal/property_get","exports"], + function(__dependency1__, __dependency2__, __exports__) { "use strict"; - __exports__["default"] = { - each: function(collection, callback) { - for (var i = 0; i < collection.length; i++) { - callback(collection[i]); - } - }, + /** + @module ember + @submodule ember-htmlbars + */ - isString: function(suspect) { - return typeof suspect === 'string'; - }, + var Ember = __dependency1__["default"]; - isFunction: function(suspect) { - return typeof suspect === 'function'; - }, + var get = __dependency2__.get; - isNumber: function(suspect) { - return typeof suspect === 'number'; - } - }; - }); + /** + `{{yield}}` denotes an area of a template that will be rendered inside + of another template. It has two main uses: -define("ember-metal/watch_key", - ["ember-metal/core","ember-metal/utils","ember-metal/platform","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - var meta = __dependency2__.meta; - var typeOf = __dependency2__.typeOf; - var platform = __dependency3__.platform; + ### Use with `layout` + When used in a Handlebars template that is assigned to an `Ember.View` + instance's `layout` property Ember will render the layout template first, + inserting the view's own rendered output at the `{{yield}}` location. - var metaFor = meta, // utils.js - MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER, - o_defineProperty = platform.defineProperty; + An empty `` and the following application code: - function watchKey(obj, keyName, meta) { - // can't watch length on Array - it is special... - if (keyName === 'length' && typeOf(obj) === 'array') { return; } + ```javascript + AView = Ember.View.extend({ + classNames: ['a-view-with-layout'], + layout: Ember.Handlebars.compile('
    {{yield}}
    '), + template: Ember.Handlebars.compile('I am wrapped') + }); - var m = meta || metaFor(obj), watching = m.watching; + aView = AView.create(); + aView.appendTo('body'); + ``` - // activate watching first time - if (!watching[keyName]) { - watching[keyName] = 1; + Will result in the following HTML output: - if ('function' === typeof obj.willWatchProperty) { - obj.willWatchProperty(keyName); - } + ```html + +
    +
    + I am wrapped +
    +
    + + ``` - if (MANDATORY_SETTER && keyName in obj) { - m.values[keyName] = obj[keyName]; - o_defineProperty(obj, keyName, { - configurable: true, - enumerable: obj.propertyIsEnumerable(keyName), - set: Ember.MANDATORY_SETTER_FUNCTION, - get: Ember.DEFAULT_GETTER_FUNCTION(keyName) - }); - } - } else { - watching[keyName] = (watching[keyName] || 0) + 1; - } - }; + The `yield` helper cannot be used outside of a template assigned to an + `Ember.View`'s `layout` property and will throw an error if attempted. - function unwatchKey(obj, keyName, meta) { - var m = meta || metaFor(obj), watching = m.watching; + ```javascript + BView = Ember.View.extend({ + classNames: ['a-view-with-layout'], + template: Ember.Handlebars.compile('{{yield}}') + }); - if (watching[keyName] === 1) { - watching[keyName] = 0; + bView = BView.create(); + bView.appendTo('body'); - if ('function' === typeof obj.didUnwatchProperty) { - obj.didUnwatchProperty(keyName); - } + // throws + // Uncaught Error: assertion failed: + // You called yield in a template that was not a layout + ``` - if (MANDATORY_SETTER && keyName in obj) { - o_defineProperty(obj, keyName, { - configurable: true, - enumerable: obj.propertyIsEnumerable(keyName), - set: function(val) { - // redefine to set as enumerable - o_defineProperty(obj, keyName, { - configurable: true, - writable: true, - enumerable: true, - value: val - }); - delete m.values[keyName]; - }, - get: Ember.DEFAULT_GETTER_FUNCTION(keyName) - }); - } - } else if (watching[keyName] > 1) { - watching[keyName]--; - } - }; + ### Use with Ember.Component + When designing components `{{yield}}` is used to denote where, inside the component's + template, an optional block passed to the component should render: - __exports__.watchKey = watchKey; - __exports__.unwatchKey = unwatchKey; - }); -define("ember-metal/watch_path", - ["ember-metal/utils","ember-metal/chains","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var meta = __dependency1__.meta; - var typeOf = __dependency1__.typeOf; - var ChainNode = __dependency2__.ChainNode; + ```handlebars + + {{#labeled-textfield value=someProperty}} + First name: + {{/labeled-textfield}} + ``` - var metaFor = meta; + ```handlebars + + + ``` - // get the chains for the current object. If the current object has - // chains inherited from the proto they will be cloned and reconfigured for - // the current object. - function chainsFor(obj, meta) { - var m = meta || metaFor(obj), ret = m.chains; - if (!ret) { - ret = m.chains = new ChainNode(null, null, obj); - } else if (ret.value() !== obj) { - ret = m.chains = ret.copy(obj); - } - return ret; - } + Result: - function watchPath(obj, keyPath, meta) { - // can't watch length on Array - it is special... - if (keyPath === 'length' && typeOf(obj) === 'array') { return; } + ```html + + ``` - var m = meta || metaFor(obj), watching = m.watching; + @method yield + @for Ember.Handlebars.helpers + @param {Hash} options + @return {String} HTML string + */ + function yieldHelper(params, hash, options, env) { + var view = this; - if (!watching[keyPath]) { // activate watching first time - watching[keyPath] = 1; - chainsFor(obj, m).add(keyPath); - } else { - watching[keyPath] = (watching[keyPath] || 0) + 1; + // Yea gods + while (view && !get(view, 'layout')) { + if (view._contextView) { + view = view._contextView; + } else { + view = get(view, '_parentView'); + } } - }; - function unwatchPath(obj, keyPath, meta) { - var m = meta || metaFor(obj), watching = m.watching; + Ember.assert("You called yield in a template that was not a layout", !!view); - if (watching[keyPath] === 1) { - watching[keyPath] = 0; - chainsFor(obj, m).remove(keyPath); - } else if (watching[keyPath] > 1) { - watching[keyPath]--; - } - }; + return view._yield(null, env, options.morph, params); + } - __exports__.watchPath = watchPath; - __exports__.unwatchPath = unwatchPath; + __exports__.yieldHelper = yieldHelper; }); -define("ember-metal/watching", - ["ember-metal/utils","ember-metal/chains","ember-metal/watch_key","ember-metal/watch_path","exports"], +enifed("ember-htmlbars/hooks/attribute", + ["ember-views/attr_nodes/attr_node","ember-metal/error","ember-metal/streams/utils","ember-views/system/sanitize_attribute_value","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; /** - @module ember-metal + @module ember + @submodule ember-htmlbars */ - var meta = __dependency1__.meta; - var META_KEY = __dependency1__.META_KEY; - var GUID_KEY = __dependency1__.GUID_KEY; - var typeOf = __dependency1__.typeOf; - var generateGuid = __dependency1__.generateGuid; - var removeChainWatcher = __dependency2__.removeChainWatcher; - var flushPendingChains = __dependency2__.flushPendingChains; - var watchKey = __dependency3__.watchKey; - var unwatchKey = __dependency3__.unwatchKey; - var watchPath = __dependency4__.watchPath; - var unwatchPath = __dependency4__.unwatchPath; + var AttrNode = __dependency1__["default"]; + var EmberError = __dependency2__["default"]; + var isStream = __dependency3__.isStream; + var sanitizeAttributeValue = __dependency4__["default"]; - var metaFor = meta; // utils.js + var boundAttributesEnabled = false; - // returns true if the passed path is just a keyName - function isKeyName(path) { - return path.indexOf('.') === -1; + + __exports__["default"] = function attribute(env, morph, element, attrName, attrValue) { + if (boundAttributesEnabled) { + var attrNode = new AttrNode(attrName, attrValue); + attrNode._morph = morph; + env.data.view.appendChild(attrNode); + } else { + if (isStream(attrValue)) { + throw new EmberError('Bound attributes are not yet supported in Ember.js'); + } else { + var sanitizedValue = sanitizeAttributeValue(element, attrName, attrValue); + env.dom.setProperty(element, attrName, sanitizedValue); + } + } } - - /** - Starts watching a property on an object. Whenever the property changes, - invokes `Ember.propertyWillChange` and `Ember.propertyDidChange`. This is the - primitive used by observers and dependent keys; usually you will never call - this method directly but instead use higher level methods like - `Ember.addObserver()` - - @private - @method watch - @for Ember - @param obj - @param {String} keyName + }); +enifed("ember-htmlbars/hooks/block", + ["ember-views/views/simple_bound_view","ember-metal/streams/utils","ember-htmlbars/system/lookup-helper","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-htmlbars */ - function watch(obj, _keyPath, m) { - // can't watch length on Array - it is special... - if (_keyPath === 'length' && typeOf(obj) === 'array') { return; } - - if (isKeyName(_keyPath)) { - watchKey(obj, _keyPath, m); - } else { - watchPath(obj, _keyPath, m); - } - }; - function isWatching(obj, key) { - var meta = obj[META_KEY]; - return (meta && meta.watching[key]) > 0; - }; + var appendSimpleBoundView = __dependency1__.appendSimpleBoundView; + var isStream = __dependency2__.isStream; + var lookupHelper = __dependency3__["default"]; - watch.flushPending = flushPendingChains; + __exports__["default"] = function block(env, morph, view, path, params, hash, template, inverse) { + var helper = lookupHelper(path, view, env); - function unwatch(obj, _keyPath, m) { - // can't watch length on Array - it is special... - if (_keyPath === 'length' && typeOf(obj) === 'array') { return; } + var options = { + morph: morph, + template: template, + inverse: inverse, + isBlock: true + }; + var result = helper.helperFunction.call(view, params, hash, options, env); - if (isKeyName(_keyPath)) { - unwatchKey(obj, _keyPath, m); + if (isStream(result)) { + appendSimpleBoundView(view, morph, result); } else { - unwatchPath(obj, _keyPath, m); + morph.setContent(result); } - }; - + } + }); +enifed("ember-htmlbars/hooks/component", + ["ember-metal/core","ember-htmlbars/system/lookup-helper","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; /** - Call on an object when you first beget it from another object. This will - setup any chained watchers on the object instance as needed. This method is - safe to call multiple times. - - @private - @method rewatch - @for Ember - @param obj + @module ember + @submodule ember-htmlbars */ - function rewatch(obj) { - var m = obj[META_KEY], chains = m && m.chains; - // make sure the object has its own guid. - if (GUID_KEY in obj && !obj.hasOwnProperty(GUID_KEY)) { - generateGuid(obj); - } + var Ember = __dependency1__["default"]; + var lookupHelper = __dependency2__["default"]; - // make sure any chained watchers update. - if (chains && chains.value() !== obj) { - m.chains = chains.copy(obj); - } - }; + __exports__["default"] = function component(env, morph, view, tagName, attrs, template) { + var helper = lookupHelper(tagName, view, env); - var NODE_STACK = []; + Ember.assert('You specified `' + tagName + '` in your template, but a component for `' + tagName + '` could not be found.', !!helper); + return helper.helperFunction.call(view, [], attrs, {morph: morph, template: template}, env); + } + }); +enifed("ember-htmlbars/hooks/concat", + ["ember-metal/streams/utils","exports"], + function(__dependency1__, __exports__) { + "use strict"; /** - Tears down the meta on an object so that it can be garbage collected. - Multiple calls will have no effect. + @module ember + @submodule ember-htmlbars + */ - @method destroy - @for Ember - @param {Object} obj the object to destroy - @return {void} + var streamConcat = __dependency1__.concat; + + __exports__["default"] = function concat(env, parts) { + return streamConcat(parts, ''); + } + }); +enifed("ember-htmlbars/hooks/content", + ["ember-views/views/simple_bound_view","ember-metal/streams/utils","ember-htmlbars/system/lookup-helper","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-htmlbars */ - function destroy(obj) { - var meta = obj[META_KEY], node, nodes, key, nodeObject; - if (meta) { - obj[META_KEY] = null; - // remove chainWatchers to remove circular references that would prevent GC - node = meta.chains; - if (node) { - NODE_STACK.push(node); - // process tree - while (NODE_STACK.length > 0) { - node = NODE_STACK.pop(); - // push children - nodes = node._chains; - if (nodes) { - for (key in nodes) { - if (nodes.hasOwnProperty(key)) { - NODE_STACK.push(nodes[key]); - } - } - } - // remove chainWatcher in node object - if (node._watching) { - nodeObject = node._object; - if (nodeObject) { - removeChainWatcher(nodeObject, node._key, node); - } - } - } - } + + var appendSimpleBoundView = __dependency1__.appendSimpleBoundView; + var isStream = __dependency2__.isStream; + var lookupHelper = __dependency3__["default"]; + + __exports__["default"] = function content(env, morph, view, path) { + var helper = lookupHelper(path, view, env); + var result; + + if (helper) { + var options = { + morph: morph, + isInline: true + }; + result = helper.helperFunction.call(view, [], {}, options, env); + } else { + result = view.getStream(path); } - }; - __exports__.watch = watch; - __exports__.isWatching = isWatching; - __exports__.unwatch = unwatch; - __exports__.rewatch = rewatch; - __exports__.destroy = destroy; + if (isStream(result)) { + appendSimpleBoundView(view, morph, result); + } else { + morph.setContent(result); + } + } }); -})(); - -(function() { -/** - @class RSVP - @module RSVP - */ -define("rsvp/all", - ["./promise","exports"], +enifed("ember-htmlbars/hooks/element", + ["ember-htmlbars/system/lookup-helper","exports"], function(__dependency1__, __exports__) { "use strict"; - var Promise = __dependency1__["default"]; + /** + @module ember + @submodule ember-htmlbars + */ + + var lookupHelper = __dependency1__["default"]; + + __exports__["default"] = function element(env, domElement, view, path, params, hash) { //jshint ignore:line + var helper = lookupHelper(path, view, env); + if (helper) { + var options = { + element: domElement + }; + return helper.helperFunction.call(view, params, hash, options, env); + } else { + return view.getStream(path); + } + } + }); +enifed("ember-htmlbars/hooks/get", + ["exports"], + function(__exports__) { + "use strict"; /** - This is a convenient alias for `RSVP.Promise.all`. + @module ember + @submodule ember-htmlbars + */ - @method all - @for RSVP - @param {Array} array Array of promises. - @param {String} label An optional label. This is useful - for tooling. - @static + __exports__["default"] = function get(env, view, path) { + return view.getStream(path); + } + }); +enifed("ember-htmlbars/hooks/inline", + ["ember-views/views/simple_bound_view","ember-metal/streams/utils","ember-htmlbars/system/lookup-helper","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-htmlbars */ - __exports__["default"] = function all(array, label) { - return Promise.all(array, label); - }; + + var appendSimpleBoundView = __dependency1__.appendSimpleBoundView; + var isStream = __dependency2__.isStream; + var lookupHelper = __dependency3__["default"]; + + __exports__["default"] = function inline(env, morph, view, path, params, hash) { + var helper = lookupHelper(path, view, env); + + Ember.assert("A helper named '"+path+"' could not be found", helper); + + var result = helper.helperFunction.call(view, params, hash, {morph: morph}, env); + + if (isStream(result)) { + appendSimpleBoundView(view, morph, result); + } else { + morph.setContent(result); + } + } }); -define("rsvp/all_settled", - ["./promise","./utils","exports"], +enifed("ember-htmlbars/hooks/set", + ["ember-metal/core","ember-metal/error","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; - var Promise = __dependency1__["default"]; - var isArray = __dependency2__.isArray; - var isNonThenable = __dependency2__.isNonThenable; - /** - `RSVP.allSettled` is similar to `RSVP.all`, but instead of implementing - a fail-fast method, it waits until all the promises have returned and - shows you all the results. This is useful if you want to handle multiple - promises' failure states together as a set. + @module ember + @submodule ember-htmlbars + */ - Returns a promise that is fulfilled when all the given promises have been - settled. The return promise is fulfilled with an array of the states of - the promises passed into the `promises` array argument. + var Ember = __dependency1__["default"]; + var EmberError = __dependency2__["default"]; - Each state object will either indicate fulfillment or rejection, and - provide the corresponding value or reason. The states will take one of - the following formats: + __exports__["default"] = function set(env, view, name, value) { + + view._keywords[name] = value; + } + }); +enifed("ember-htmlbars/hooks/subexpr", + ["ember-htmlbars/system/lookup-helper","exports"], + function(__dependency1__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-htmlbars + */ - ```javascript - { state: 'fulfilled', value: value } - or - { state: 'rejected', reason: reason } - ``` + var lookupHelper = __dependency1__["default"]; - Example: + __exports__["default"] = function subexpr(env, view, path, params, hash) { + var helper = lookupHelper(path, view, env); - ```javascript - var promise1 = RSVP.Promise.resolve(1); - var promise2 = RSVP.Promise.reject(new Error('2')); - var promise3 = RSVP.Promise.reject(new Error('3')); - var promises = [ promise1, promise2, promise3 ]; + Ember.assert("A helper named '"+path+"' could not be found", helper); - RSVP.allSettled(promises).then(function(array){ - // array == [ - // { state: 'fulfilled', value: 1 }, - // { state: 'rejected', reason: Error }, - // { state: 'rejected', reason: Error } - // ] - // Note that for the second item, reason.message will be "2", and for the - // third item, reason.message will be "3". - }, function(error) { - // Not run. (This block would only be called if allSettled had failed, - // for instance if passed an incorrect argument type.) - }); - ``` + var options = { + isInline: true + }; + return helper.helperFunction.call(view, params, hash, options, env); + } + }); +enifed("ember-htmlbars/system/bootstrap", + ["ember-metal/core","ember-views/component_lookup","ember-views/system/jquery","ember-metal/error","ember-runtime/system/lazy_load","ember-template-compiler/system/compile","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { + "use strict"; + /*globals Handlebars */ - @method allSettled - @for RSVP - @param {Array} promises - @param {String} label - optional string that describes the promise. - Useful for tooling. - @return {Promise} promise that is fulfilled with an array of the settled - states of the constituent promises. - @static + /** + @module ember + @submodule ember-htmlbars */ - __exports__["default"] = function allSettled(entries, label) { - return new Promise(function(resolve, reject) { - if (!isArray(entries)) { - throw new TypeError('You must pass an array to allSettled.'); - } + var Ember = __dependency1__["default"]; + var ComponentLookup = __dependency2__["default"]; + var jQuery = __dependency3__["default"]; + var EmberError = __dependency4__["default"]; + var onLoad = __dependency5__.onLoad; + var htmlbarsCompile = __dependency6__["default"]; - var remaining = entries.length; - var entry; + /** + @module ember + @submodule ember-handlebars + */ - if (remaining === 0) { - resolve([]); - return; - } + /** + Find templates stored in the head tag as script tags and make them available + to `Ember.CoreView` in the global `Ember.TEMPLATES` object. This will be run + as as jQuery DOM-ready callback. - var results = new Array(remaining); + Script tags with `text/x-handlebars` will be compiled + with Ember's Handlebars and are suitable for use as a view's template. + Those with type `text/x-raw-handlebars` will be compiled with regular + Handlebars and are suitable for use in views' computed properties. - function fulfilledResolver(index) { - return function(value) { - resolveAll(index, fulfilled(value)); - }; - } + @private + @method bootstrap + @for Ember.Handlebars + @static + @param ctx + */ + function bootstrap(ctx) { + var selectors = 'script[type="text/x-handlebars"], script[type="text/x-raw-handlebars"]'; - function rejectedResolver(index) { - return function(reason) { - resolveAll(index, rejected(reason)); - }; - } + jQuery(selectors, ctx) + .each(function() { + // Get a reference to the script tag + var script = jQuery(this); - function resolveAll(index, value) { - results[index] = value; - if (--remaining === 0) { - resolve(results); - } + var compile = (script.attr('type') === 'text/x-raw-handlebars') ? + jQuery.proxy(Handlebars.compile, Handlebars) : + htmlbarsCompile; + // Get the name of the script, used by Ember.View's templateName property. + // First look for data-template-name attribute, then fall back to its + // id if no name is found. + var templateName = script.attr('data-template-name') || script.attr('id') || 'application'; + var template = compile(script.html()); + + // Check if template of same name already exists + if (Ember.TEMPLATES[templateName] !== undefined) { + throw new EmberError('Template named "' + templateName + '" already exists.'); } - for (var index = 0; index < entries.length; index++) { - entry = entries[index]; + // For templates which have a name, we save them and then remove them from the DOM + Ember.TEMPLATES[templateName] = template; - if (isNonThenable(entry)) { - resolveAll(index, fulfilled(entry)); - } else { - Promise.cast(entry).then(fulfilledResolver(index), rejectedResolver(index)); - } - } - }, label); - }; + // Remove script tag from DOM + script.remove(); + }); + } - function fulfilled(value) { - return { state: 'fulfilled', value: value }; + function _bootstrap() { + bootstrap( jQuery(document) ); } - function rejected(reason) { - return { state: 'rejected', reason: reason }; + function registerComponentLookup(container) { + container.register('component-lookup:main', ComponentLookup); } + + /* + We tie this to application.load to ensure that we've at least + attempted to bootstrap at the point that the application is loaded. + + We also tie this to document ready since we're guaranteed that all + the inline templates are present at this point. + + There's no harm to running this twice, since we remove the templates + from the DOM after processing. + */ + + onLoad('Ember.Application', function(Application) { + + + Application.initializer({ + name: 'domTemplates', + initialize: _bootstrap + }); + + Application.initializer({ + name: 'registerComponentLookup', + after: 'domTemplates', + initialize: registerComponentLookup + }); + + + }); + + __exports__["default"] = bootstrap; }); -define("rsvp/config", - ["./events","exports"], - function(__dependency1__, __exports__) { +enifed("ember-htmlbars/system/helper", + ["exports"], + function(__exports__) { "use strict"; - var EventTarget = __dependency1__["default"]; - - var config = { - instrument: false - }; + /** + @module ember + @submodule ember-htmlbars + */ - EventTarget.mixin(config); + /** + @class Helper + @namespace Ember.HTMLBars + */ + function Helper(helper, preprocessArguments) { + this.helperFunction = helper; - function configure(name, value) { - if (name === 'onerror') { - // handle for legacy users that expect the actual - // error to be passed to their function added via - // `RSVP.configure('onerror', someFunctionHere);` - config.on('error', value); - return; + if (preprocessArguments) { + this.preprocessArguments = preprocessArguments; } - if (arguments.length === 2) { - config[name] = value; - } else { - return config[name]; - } + this.isHTMLBars = true; } - __exports__.config = config; - __exports__.configure = configure; + Helper.prototype = { + preprocessArguments: function() { } + }; + + __exports__["default"] = Helper; }); -define("rsvp/defer", - ["./promise","exports"], - function(__dependency1__, __exports__) { +enifed("ember-htmlbars/system/lookup-helper", + ["ember-metal/core","ember-metal/cache","ember-htmlbars/system/make-view-helper","ember-htmlbars/compat/helper","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; - var Promise = __dependency1__["default"]; - /** - `RSVP.defer` returns an object similar to jQuery's `$.Deferred`. - `RSVP.defer` should be used when porting over code reliant on `$.Deferred`'s - interface. New code should use the `RSVP.Promise` constructor instead. + @module ember + @submodule ember-htmlbars + */ - The object returned from `RSVP.defer` is a plain object with three properties: + var Ember = __dependency1__["default"]; + var Cache = __dependency2__["default"]; + var makeViewHelper = __dependency3__["default"]; + var HandlebarsCompatibleHelper = __dependency4__["default"]; - * promise - an `RSVP.Promise`. - * reject - a function that causes the `promise` property on this object to - become rejected - * resolve - a function that causes the `promise` property on this object to - become fulfilled. + var ISNT_HELPER_CACHE = new Cache(1000, function(key) { + return key.indexOf('-') === -1; + }); + __exports__.ISNT_HELPER_CACHE = ISNT_HELPER_CACHE; + /** + Used to lookup/resolve handlebars helpers. The lookup order is: - Example: + * 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 - ```javascript - var deferred = RSVP.defer(); + @private + @method resolveHelper + @param {Container} container + @param {String} name the name of the helper to lookup + @return {Handlebars Helper} + */ + __exports__["default"] = function lookupHelper(name, view, env) { + var helper = env.helpers[name]; + if (helper) { + return helper; + } - deferred.resolve("Success!"); + var container = view.container; - deferred.promise.then(function(value){ - // value here is "Success!" - }); - ``` + if (!container || ISNT_HELPER_CACHE.get(name)) { + return; + } - @method defer - @for RSVP - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Object} - */ + var helperName = 'helper:' + name; + helper = container.lookup(helperName); + 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); - __exports__["default"] = function defer(label) { - var deferred = { }; + var Component = componentLookup.lookupFactory(name, container); + if (Component) { + helper = makeViewHelper(Component); + container.register(helperName, helper); + } + } - deferred.promise = new Promise(function(resolve, reject) { - deferred.resolve = resolve; - deferred.reject = reject; - }, label); + if (helper && !helper.isHTMLBars) { + helper = new HandlebarsCompatibleHelper(helper); + container.unregister(helperName); + container.register(helperName, helper); + } - return deferred; - }; + return helper; + } }); -define("rsvp/events", - ["exports"], - function(__exports__) { +enifed("ember-htmlbars/system/make-view-helper", + ["ember-metal/core","ember-htmlbars/system/helper","exports"], + function(__dependency1__, __dependency2__, __exports__) { "use strict"; - var indexOf = function(callbacks, callback) { - for (var i=0, l=callbacks.length; isomeString') + ``` - callbacks = allCallbacks[eventName]; + @method htmlSafe + @for Ember.String + @static + @return {Handlebars.SafeString} a string that will not be html escaped by Handlebars + */ + function htmlSafe(str) { + if (str === null || str === undefined) { + return ""; + } - index = indexOf(callbacks, callback); + if (typeof str !== 'string') { + str = ''+str; + } + return new SafeString(str); + } - if (index !== -1) { callbacks.splice(index, 1); } - }, + EmberStringUtils.htmlSafe = htmlSafe; + if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { /** - Use `trigger` to fire custom events. For example: + Mark a string as being safe for unescaped output with Handlebars. ```javascript - object.on('foo', function(){ - console.log('foo event happened!'); - }); - object.trigger('foo'); - // 'foo event happened!' logged to the console + '
    someString
    '.htmlSafe() ``` - You can also pass a value as a second argument to `trigger` that will be - passed as an argument to all event listeners for the event: - - ```javascript - object.on('foo', function(value){ - console.log(value.name); - }); - - object.trigger('foo', { name: 'bar' }); - // 'bar' logged to the console - ``` + See [Ember.String.htmlSafe](/api/classes/Ember.String.html#method_htmlSafe). - @method trigger - @param {String} eventName name of the event to be triggered - @param {Any} options optional value to be passed to any event handlers for - the given `eventName` - @private + @method htmlSafe + @for String + @return {Handlebars.SafeString} a string that will not be html escaped by Handlebars */ - trigger: function(eventName, options) { - var allCallbacks = callbacksFor(this), - callbacks, callbackTuple, callback, binding; - - if (callbacks = allCallbacks[eventName]) { - // Don't cache the callbacks.length since it may grow - for (var i=0; i=0;i--) { + child = children[i]; + index = total++; + views[index] = child; + queue[length++] = index; + view = child; + } + } + + index = queue[--length]; + view = views[index]; + + while (parentIndex === index) { + level--; + view._elementCreated = true; + this.didCreateElement(view); + if (willInsert) { + this.willInsertElement(view); + } + + if (level === 0) { + length--; + break; } + + parentIndex = parents[level]; + parent = parentIndex === -1 ? _parentView : views[parentIndex]; + this.insertElement(view, parent, element, -1); + index = queue[--length]; + view = views[index]; + element = elements[level]; + elements[level] = null; } } - }; - }); -define("rsvp/filter", - ["./all","./map","./utils","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var all = __dependency1__["default"]; - var map = __dependency2__["default"]; - var isFunction = __dependency3__.isFunction; - var isArray = __dependency3__.isArray; - /** - `RSVP.filter` is similar to JavaScript's native `filter` method, except that it - waits for all promises to become fulfilled before running the `filterFn` on - each item in given to `promises`. `RSVP.filter` returns a promise that will - become fulfilled with the result of running `filterFn` on the values the - promises become fulfilled with. + this.insertElement(view, _parentView, element, insertAt); - For example: + for (i=total-1; i>=0; i--) { + if (willInsert) { + views[i]._elementInserted = true; + this.didInsertElement(views[i]); + } + views[i] = null; + } - ```javascript + return element; + } - var promise1 = RSVP.resolve(1); - var promise2 = RSVP.resolve(2); - var promise3 = RSVP.resolve(3); + Renderer.prototype.uuid = function Renderer_uuid(view) { + if (view._uuid === undefined) { + view._uuid = ++this._uuid; + view._renderer = this; + } // else assert(view._renderer === this) + return view._uuid; + }; - var filterFn = function(item){ - return item > 1; + Renderer.prototype.scheduleInsert = + function Renderer_scheduleInsert(view, morph) { + if (view._morph || view._elementCreated) { + throw new Error("You cannot insert a View that has already been rendered"); + } + Ember.assert("You cannot insert a View without a morph", morph); + view._morph = morph; + var viewId = this.uuid(view); + this._inserts[viewId] = this.scheduleRender(this, function scheduledRenderTree() { + this._inserts[viewId] = null; + this.renderTree(view); + }); }; - RSVP.filter(promises, filterFn).then(function(result){ - // result is [ 2, 3 ] - }); - ``` - - If any of the `promises` given to `RSVP.filter` are rejected, the first promise - that is rejected will be given as an argument to the returned promise's - rejection handler. For example: - - ```javascript - var promise1 = RSVP.resolve(1); - var promise2 = RSVP.reject(new Error("2")); - var promise3 = RSVP.reject(new Error("3")); - var promises = [ promise1, promise2, promise3 ]; - - var filterFn = function(item){ - return item > 1; + Renderer.prototype.appendTo = + function Renderer_appendTo(view, target) { + var morph = this._dom.appendMorph(target); + this.scheduleInsert(view, morph); }; - RSVP.filter(promises, filterFn).then(function(array){ - // Code here never runs because there are rejected promises! - }, function(reason) { - // reason.message === "2" - }); - ``` + Renderer.prototype.replaceIn = + function Renderer_replaceIn(view, target) { + var morph = this._dom.createMorph(target, null, null); + this.scheduleInsert(view, morph); + }; - `RSVP.filter` will also wait for any promises returned from `filterFn`. - For instance, you may want to fetch a list of users then return a subset - of those users based on some asynchronous operation: + function Renderer_remove(_view, shouldDestroy, reset) { + var viewId = this.uuid(_view); - ```javascript + if (this._inserts[viewId]) { + this.cancelRender(this._inserts[viewId]); + this._inserts[viewId] = undefined; + } - var alice = { name: 'alice' }; - var bob = { name: 'bob' }; - var users = [ alice, bob ]; + if (!_view._elementCreated) { + return; + } - var promises = users.map(function(user){ - return RSVP.resolve(user); - }); + var removeQueue = []; + var destroyQueue = []; + var morph = _view._morph; + var idx, len, view, queue, childViews, i, l; - var filterFn = function(user){ - // Here, Alice has permissions to create a blog post, but Bob does not. - return getPrivilegesForUser(user).then(function(privs){ - return privs.can_create_blog_post === true; - }); - }; - RSVP.filter(promises, filterFn).then(function(users){ - // true, because the server told us only Alice can create a blog post. - users.length === 1; - // false, because Alice is the only user present in `users` - users[0] === bob; - }); - ``` + removeQueue.push(_view); - @method filter - @for RSVP - @param {Array} promises - @param {Function} filterFn - function to be called on each resolved value to - filter the final results. - @param {String} label optional string describing the promise. Useful for - tooling. - @return {Promise} - */ - function filter(promises, filterFn, label) { - return all(promises, label).then(function(values){ - if (!isArray(promises)) { - throw new TypeError('You must pass an array to filter.'); - } + for (idx=0; idx 2) { - resolve(slice.call(arguments, 1)); - } else { - resolve(value); - } - }; + AliasedProperty.prototype.setup = function(obj, keyName) { + Ember.assert("Setting alias '" + keyName + "' on self", this.altKey !== keyName); + var m = meta(obj); + if (m.watching[keyName]) { + addDependentKeys(this, obj, keyName, m); + } + }; + + AliasedProperty.prototype.teardown = function(obj, keyName) { + var m = meta(obj); + if (m.watching[keyName]) { + removeDependentKeys(this, obj, keyName, m); + } + }; + + AliasedProperty.prototype.readOnly = function() { + this.set = AliasedProperty_readOnlySet; + return this; + }; + + function AliasedProperty_readOnlySet(obj, keyName, value) { + throw new EmberError('Cannot set read-only property "' + keyName + '" on object: ' + inspect(obj)); + } + + AliasedProperty.prototype.oneWay = function() { + this.set = AliasedProperty_oneWaySet; + return this; + }; + + function AliasedProperty_oneWaySet(obj, keyName, value) { + defineProperty(obj, keyName, null); + return set(obj, keyName, value); } + // Backwards compatibility with Ember Data + AliasedProperty.prototype._meta = undefined; + AliasedProperty.prototype.meta = ComputedProperty.prototype.meta; + }); +enifed("ember-metal/array", + ["exports"], + function(__exports__) { + "use strict"; /** - `RSVP.denodeify` takes a "node-style" function and returns a function that - will return an `RSVP.Promise`. You can use `denodeify` in Node.js or the - browser when you'd prefer to use promises over using callbacks. For example, - `denodeify` transforms the following: + @module ember-metal + */ - ```javascript - var fs = require('fs'); + var ArrayPrototype = Array.prototype; - fs.readFile('myfile.txt', function(err, data){ - if (err) return handleError(err); - handleData(data); - }); - ``` + // Testing this is not ideal, but we want to use native functions + // if available, but not to use versions created by libraries like Prototype + var isNativeFunc = function(func) { + // This should probably work in all browsers likely to have ES5 array methods + return func && Function.prototype.toString.call(func).indexOf('[native code]') > -1; + }; - into: + var defineNativeShim = function(nativeFunc, shim) { + if (isNativeFunc(nativeFunc)) { + return nativeFunc; + } + return shim; + }; - ```javascript - var fs = require('fs'); + // From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/map + var map = defineNativeShim(ArrayPrototype.map, function(fun /*, thisp */) { + //"use strict"; - var readFile = RSVP.denodeify(fs.readFile); + if (this === void 0 || this === null || typeof fun !== "function") { + throw new TypeError(); + } - readFile('myfile.txt').then(handleData, handleError); - ``` + var t = Object(this); + var len = t.length >>> 0; + var res = new Array(len); + var thisp = arguments[1]; - Using `denodeify` makes it easier to compose asynchronous operations instead - of using callbacks. For example, instead of: + for (var i = 0; i < len; i++) { + if (i in t) { + res[i] = fun.call(thisp, t[i], i, t); + } + } - ```javascript - var fs = require('fs'); - var log = require('some-async-logger'); + return res; + }); - fs.readFile('myfile.txt', function(err, data){ - if (err) return handleError(err); - fs.writeFile('myfile2.txt', data, function(err){ - if (err) throw err; - log('success', function(err) { - if (err) throw err; - }); - }); - }); - ``` + // From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/foreach + var forEach = defineNativeShim(ArrayPrototype.forEach, function(fun /*, thisp */) { + //"use strict"; - You can chain the operations together using `then` from the returned promise: + if (this === void 0 || this === null || typeof fun !== "function") { + throw new TypeError(); + } - ```javascript - var fs = require('fs'); - var denodeify = RSVP.denodeify; - var readFile = denodeify(fs.readFile); - var writeFile = denodeify(fs.writeFile); - var log = denodeify(require('some-async-logger')); + var t = Object(this); + var len = t.length >>> 0; + var thisp = arguments[1]; - readFile('myfile.txt').then(function(data){ - return writeFile('myfile2.txt', data); - }).then(function(){ - return log('SUCCESS'); - }).then(function(){ - // success handler - }, function(reason){ - // rejection handler - }); - ``` + for (var i = 0; i < len; i++) { + if (i in t) { + fun.call(thisp, t[i], i, t); + } + } + }); - @method denodeify - @for RSVP - @param {Function} nodeFunc a "node-style" function that takes a callback as - its last argument. The callback expects an error to be passed as its first - argument (if an error occurred, otherwise null), and the value from the - operation as its second argument ("function(err, value){ }"). - @param {Any} binding optional argument for binding the "this" value when - calling the `nodeFunc` function. - @return {Function} a function that wraps `nodeFunc` to return an - `RSVP.Promise` - @static - */ - __exports__["default"] = function denodeify(nodeFunc, binding) { - return function() { - var nodeArgs = slice.call(arguments), resolve, reject; - var thisArg = this || binding; + var indexOf = defineNativeShim(ArrayPrototype.indexOf, function (obj, fromIndex) { + if (fromIndex === null || fromIndex === undefined) { + fromIndex = 0; + } + else if (fromIndex < 0) { + fromIndex = Math.max(0, this.length + fromIndex); + } - return new Promise(function(resolve, reject) { - Promise.all(nodeArgs).then(function(nodeArgs) { - try { - nodeArgs.push(makeNodeCallbackFor(resolve, reject)); - nodeFunc.apply(thisArg, nodeArgs); - } catch(e) { - reject(e); - } - }); - }); - }; - }; - }); -define("rsvp/promise", - ["./config","./events","./instrument","./utils","./promise/cast","./promise/all","./promise/race","./promise/resolve","./promise/reject","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __exports__) { - "use strict"; - var config = __dependency1__.config; - var EventTarget = __dependency2__["default"]; - var instrument = __dependency3__["default"]; - var objectOrFunction = __dependency4__.objectOrFunction; - var isFunction = __dependency4__.isFunction; - var now = __dependency4__.now; - var cast = __dependency5__["default"]; - var all = __dependency6__["default"]; - var race = __dependency7__["default"]; - var Resolve = __dependency8__["default"]; - var Reject = __dependency9__["default"]; + for (var i = fromIndex, j = this.length; i < j; i++) { + if (this[i] === obj) { + return i; + } + } + return -1; + }); - var guidKey = 'rsvp_' + now() + '-'; - var counter = 0; + var lastIndexOf = defineNativeShim(ArrayPrototype.lastIndexOf, function(obj, fromIndex) { + var len = this.length; + var idx; - function noop() {} + if (fromIndex === undefined) fromIndex = len-1; + else fromIndex = (fromIndex < 0) ? Math.ceil(fromIndex) : Math.floor(fromIndex); + if (fromIndex < 0) fromIndex += len; - __exports__["default"] = Promise; + for(idx = fromIndex;idx>=0;idx--) { + if (this[idx] === obj) return idx ; + } + return -1; + }); + + var filter = defineNativeShim(ArrayPrototype.filter, function (fn, context) { + var i, value; + var result = []; + var length = this.length; + + for (i = 0; i < length; i++) { + if (this.hasOwnProperty(i)) { + value = this[i]; + if (fn.call(context, value, i, this)) { + result.push(value); + } + } + } + return result; + }); + if (Ember.SHIM_ES5) { + ArrayPrototype.map = ArrayPrototype.map || map; + ArrayPrototype.forEach = ArrayPrototype.forEach || forEach; + ArrayPrototype.filter = ArrayPrototype.filter || filter; + ArrayPrototype.indexOf = ArrayPrototype.indexOf || indexOf; + ArrayPrototype.lastIndexOf = ArrayPrototype.lastIndexOf || lastIndexOf; + } /** - Promise objects represent the eventual result of an asynchronous operation. The - primary way of interacting with a promise is through its `then` method, which - registers callbacks to receive either a promise’s eventual value or the reason - why the promise cannot be fulfilled. + Array polyfills to support ES5 features in older browsers. - Terminology - ----------- + @namespace Ember + @property ArrayPolyfills + */ + __exports__.map = map; + __exports__.forEach = forEach; + __exports__.filter = filter; + __exports__.indexOf = indexOf; + __exports__.lastIndexOf = lastIndexOf; + }); +enifed("ember-metal/binding", + ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/observer","ember-metal/run_loop","ember-metal/path_cache","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // Ember.Logger, Ember.LOG_BINDINGS, assert + var get = __dependency2__.get; + var trySet = __dependency3__.trySet; + var guidFor = __dependency4__.guidFor; + var addObserver = __dependency5__.addObserver; + var removeObserver = __dependency5__.removeObserver; + var _suspendObserver = __dependency5__._suspendObserver; + var run = __dependency6__["default"]; + var isGlobalPath = __dependency7__.isGlobal; - - `promise` is an object or function with a `then` method whose behavior conforms to this specification. - - `thenable` is an object or function that defines a `then` method. - - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). - - `exception` is a value that is thrown using the throw statement. - - `reason` is a value that indicates why a promise was rejected. - - `settled` the final resting state of a promise, fulfilled or rejected. - A promise can be in one of three states: pending, fulfilled, or rejected. + // ES6TODO: where is Ember.lookup defined? + /** + @module ember-metal + */ - Promises that are fulfilled have a fulfillment value and are in the fulfilled - state. Promises that are rejected have a rejection reason and are in the - rejected state. A fulfillment value is never a thenable. Similarly, a - rejection reason is never a thenable. + // .......................................................... + // CONSTANTS + // - Promises can also be said to *resolve* a value. If this value is also a - promise, then the original promise's settled state will match the value's - settled state. So a promise that *resolves* a promise that rejects will - itself reject, and a promise that *resolves* a promise that fulfills will - itself fulfill. + /** + Debug parameter you can turn on. This will log all bindings that fire to + the console. This should be disabled in production code. Note that you + can also enable this from the console or temporarily. + @property LOG_BINDINGS + @for Ember + @type Boolean + @default false + */ + Ember.LOG_BINDINGS = false || !!Ember.ENV.LOG_BINDINGS; - Basic Usage: - ------------ + /** + Returns true if the provided path is global (e.g., `MyApp.fooController.bar`) + instead of local (`foo.bar.baz`). - ```js - var promise = new Promise(function(resolve, reject) { - // on success - resolve(value); + @method isGlobalPath + @for Ember + @private + @param {String} path + @return Boolean + */ - // on failure - reject(reason); - }); + function getWithGlobals(obj, path) { + return get(isGlobalPath(path) ? Ember.lookup : obj, path); + } - promise.then(function(value) { - // on fulfillment - }, function(reason) { - // on rejection - }); - ``` + // .......................................................... + // BINDING + // - Advanced Usage: - --------------- + function Binding(toPath, fromPath) { + this._direction = undefined; + this._from = fromPath; + this._to = toPath; + this._readyToSync = undefined; + this._oneWay = undefined; + } - Promises shine when abstracting away asynchronous interactions such as - `XMLHttpRequest`s. + /** + @class Binding + @namespace Ember + */ - ```js - function getJSON(url) { - return new Promise(function(resolve, reject){ - var xhr = new XMLHttpRequest(); + Binding.prototype = { + /** + This copies the Binding so it can be connected to another object. - xhr.open('GET', url); - xhr.onreadystatechange = handler; - xhr.responseType = 'json'; - xhr.setRequestHeader('Accept', 'application/json'); - xhr.send(); + @method copy + @return {Ember.Binding} `this` + */ + copy: function () { + var copy = new Binding(this._to, this._from); + if (this._oneWay) { copy._oneWay = true; } + return copy; + }, - function handler() { - if (this.readyState === this.DONE) { - if (this.status === 200) { - resolve(this.response); - } else { - reject(new Error("getJSON: `" + url + "` failed with status: [" + this.status + "]"); - } - } - }; - }); - } + // .......................................................... + // CONFIG + // - getJSON('/posts.json').then(function(json) { - // on fulfillment - }, function(reason) { - // on rejection - }); - ``` + /** + This will set `from` property path to the specified value. It will not + attempt to resolve this property path to an actual object until you + connect the binding. - Unlike callbacks, promises are great composable primitives. + The binding will search for the property path starting at the root object + you pass when you `connect()` the binding. It follows the same rules as + `get()` - see that method for more information. - ```js - Promise.all([ - getJSON('/posts'), - getJSON('/comments') - ]).then(function(values){ - values[0] // => postsJSON - values[1] // => commentsJSON + @method from + @param {String} path the property path to connect to + @return {Ember.Binding} `this` + */ + from: function(path) { + this._from = path; + return this; + }, - return values; - }); - ``` + /** + This will set the `to` property path to the specified value. It will not + attempt to resolve this property path to an actual object until you + connect the binding. - @class RSVP.Promise - @param {function} - @param {String} label optional string for labeling the promise. - Useful for tooling. - @constructor - */ - function Promise(resolver, label) { - if (!isFunction(resolver)) { - throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); - } + The binding will search for the property path starting at the root object + you pass when you `connect()` the binding. It follows the same rules as + `get()` - see that method for more information. - if (!(this instanceof Promise)) { - throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); - } + @method to + @param {String|Tuple} path A property path or tuple + @return {Ember.Binding} `this` + */ + to: function(path) { + this._to = path; + return this; + }, - this._id = counter++; - this._label = label; - this._subscribers = []; + /** + Configures the binding as one way. A one-way binding will relay changes + on the `from` side to the `to` side, but not the other way around. This + means that if you change the `to` side directly, the `from` side may have + a different value. - if (config.instrument) { - instrument('created', this); - } + @method oneWay + @return {Ember.Binding} `this` + */ + oneWay: function() { + this._oneWay = true; + return this; + }, - if (noop !== resolver) { - invokeResolver(resolver, this); - } - } + /** + @method toString + @return {String} string representation of binding + */ + toString: function() { + var oneWay = this._oneWay ? '[oneWay]' : ''; + return "Ember.Binding<" + guidFor(this) + ">(" + this._from + " -> " + this._to + ")" + oneWay; + }, - function invokeResolver(resolver, promise) { - function resolvePromise(value) { - resolve(promise, value); - } + // .......................................................... + // CONNECT AND SYNC + // - function rejectPromise(reason) { - reject(promise, reason); - } + /** + Attempts to connect this binding instance so that it can receive and relay + changes. This method will raise an exception if you have not set the + from/to properties yet. - try { - resolver(resolvePromise, rejectPromise); - } catch(e) { - rejectPromise(e); - } - } + @method connect + @param {Object} obj The root object for this binding. + @return {Ember.Binding} `this` + */ + connect: function(obj) { + Ember.assert('Must pass a valid object to Ember.Binding.connect()', !!obj); - Promise.cast = cast; - Promise.all = all; - Promise.race = race; - Promise.resolve = Resolve; - Promise.reject = Reject; + var fromPath = this._from; + var toPath = this._to; + trySet(obj, toPath, getWithGlobals(obj, fromPath)); - var PENDING = void 0; - var SEALED = 0; - var FULFILLED = 1; - var REJECTED = 2; + // add an observer on the object to be notified when the binding should be updated + addObserver(obj, fromPath, this, this.fromDidChange); - function subscribe(parent, child, onFulfillment, onRejection) { - var subscribers = parent._subscribers; - var length = subscribers.length; + // if the binding is a two-way binding, also set up an observer on the target + if (!this._oneWay) { + addObserver(obj, toPath, this, this.toDidChange); + } - subscribers[length] = child; - subscribers[length + FULFILLED] = onFulfillment; - subscribers[length + REJECTED] = onRejection; - } + this._readyToSync = true; - function publish(promise, settled) { - var child, callback, subscribers = promise._subscribers, detail = promise._detail; + return this; + }, - if (config.instrument) { - instrument(settled === FULFILLED ? 'fulfilled' : 'rejected', promise); - } + /** + Disconnects the binding instance. Changes will no longer be relayed. You + will not usually need to call this method. - for (var i = 0; i < subscribers.length; i += 3) { - child = subscribers[i]; - callback = subscribers[i + settled]; + @method disconnect + @param {Object} obj The root object you passed when connecting the binding. + @return {Ember.Binding} `this` + */ + disconnect: function(obj) { + Ember.assert('Must pass a valid object to Ember.Binding.disconnect()', !!obj); - invokeCallback(settled, child, callback, detail); - } + var twoWay = !this._oneWay; - promise._subscribers = null; - } + // remove an observer on the object so we're no longer notified of + // changes that should update bindings. + removeObserver(obj, this._from, this, this.fromDidChange); - Promise.prototype = { - constructor: Promise, + // if the binding is two-way, remove the observer from the target as well + if (twoWay) { + removeObserver(obj, this._to, this, this.toDidChange); + } - _id: undefined, - _guidKey: guidKey, - _label: undefined, + this._readyToSync = false; // disable scheduled syncs... + return this; + }, - _state: undefined, - _detail: undefined, - _subscribers: undefined, + // .......................................................... + // PRIVATE + // - _onerror: function (reason) { - config.trigger('error', reason); + /* called when the from side changes */ + fromDidChange: function(target) { + this._scheduleSync(target, 'fwd'); }, - /** - The primary way of interacting with a promise is through its `then` method, - which registers callbacks to receive either a promise's eventual value or the - reason why the promise cannot be fulfilled. + /* called when the to side changes */ + toDidChange: function(target) { + this._scheduleSync(target, 'back'); + }, - ```js - findUser().then(function(user){ - // user is available - }, function(reason){ - // user is unavailable, and you are given the reason why - }); - ``` + _scheduleSync: function(obj, dir) { + var existingDir = this._direction; - Chaining - -------- + // if we haven't scheduled the binding yet, schedule it + if (existingDir === undefined) { + run.schedule('sync', this, this._sync, obj); + this._direction = dir; + } - The return value of `then` is itself a promise. This second, "downstream" - promise is resolved with the return value of the first promise's fulfillment - or rejection handler, or rejected if the handler throws an exception. + // If both a 'back' and 'fwd' sync have been scheduled on the same object, + // default to a 'fwd' sync so that it remains deterministic. + if (existingDir === 'back' && dir === 'fwd') { + this._direction = 'fwd'; + } + }, - ```js - findUser().then(function (user) { - return user.name; - }, function (reason) { - return "default name"; - }).then(function (userName) { - // If `findUser` fulfilled, `userName` will be the user's name, otherwise it - // will be `"default name"` - }); + _sync: function(obj) { + var log = Ember.LOG_BINDINGS; - findUser().then(function (user) { - throw new Error("Found user, but still unhappy"); - }, function (reason) { - throw new Error("`findUser` rejected and we're unhappy"); - }).then(function (value) { - // never reached - }, function (reason) { - // if `findUser` fulfilled, `reason` will be "Found user, but still unhappy". - // If `findUser` rejected, `reason` will be "`findUser` rejected and we're unhappy". - }); - ``` - If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. + // don't synchronize destroyed objects or disconnected bindings + if (obj.isDestroyed || !this._readyToSync) { return; } - ```js - findUser().then(function (user) { - throw new PedagogicalException("Upstream error"); - }).then(function (value) { - // never reached - }).then(function (value) { - // never reached - }, function (reason) { - // The `PedgagocialException` is propagated all the way down to here - }); - ``` + // get the direction of the binding for the object we are + // synchronizing from + var direction = this._direction; - Assimilation - ------------ + var fromPath = this._from; + var toPath = this._to; - Sometimes the value you want to propagate to a downstream promise can only be - retrieved asynchronously. This can be achieved by returning a promise in the - fulfillment or rejection handler. The downstream promise will then be pending - until the returned promise is settled. This is called *assimilation*. + this._direction = undefined; - ```js - findUser().then(function (user) { - return findCommentsByAuthor(user); - }).then(function (comments) { - // The user's comments are now available - }); - ``` + // if we're synchronizing from the remote object... + if (direction === 'fwd') { + var fromValue = getWithGlobals(obj, this._from); + if (log) { + Ember.Logger.log(' ', this.toString(), '->', fromValue, obj); + } + if (this._oneWay) { + trySet(obj, toPath, fromValue); + } else { + _suspendObserver(obj, toPath, this, this.toDidChange, function () { + trySet(obj, toPath, fromValue); + }); + } + // if we're synchronizing *to* the remote object + } else if (direction === 'back') { + var toValue = get(obj, this._to); + if (log) { + Ember.Logger.log(' ', this.toString(), '<-', toValue, obj); + } + _suspendObserver(obj, fromPath, this, this.fromDidChange, function () { + trySet(isGlobalPath(fromPath) ? Ember.lookup : obj, fromPath, toValue); + }); + } + } - If the assimliated promise rejects, then the downstream promise will also reject. + }; - ```js - findUser().then(function (user) { - return findCommentsByAuthor(user); - }).then(function (comments) { - // If `findCommentsByAuthor` fulfills, we'll have the value here - }, function (reason) { - // If `findCommentsByAuthor` rejects, we'll have the reason here - }); - ``` + function mixinProperties(to, from) { + for (var key in from) { + if (from.hasOwnProperty(key)) { + to[key] = from[key]; + } + } + } - Simple Example - -------------- + mixinProperties(Binding, { - Synchronous Example + /* + See `Ember.Binding.from`. - ```javascript - var result; + @method from + @static + */ + from: function(from) { + var C = this; + return new C(undefined, from); + }, - try { - result = findResult(); - // success - } catch(reason) { - // failure + /* + See `Ember.Binding.to`. + + @method to + @static + */ + to: function(to) { + var C = this; + return new C(to, undefined); + }, + + /** + Creates a new Binding instance and makes it apply in a single direction. + A one-way binding will relay changes on the `from` side object (supplied + as the `from` argument) the `to` side, but not the other way around. + This means that if you change the "to" side directly, the "from" side may have + a different value. + + See `Binding.oneWay`. + + @method oneWay + @param {String} from from path. + @param {Boolean} [flag] (Optional) passing nothing here will make the + binding `oneWay`. You can instead pass `false` to disable `oneWay`, making the + binding two way again. + @return {Ember.Binding} `this` + */ + oneWay: function(from, flag) { + var C = this; + return new C(undefined, from).oneWay(flag); } - ``` - Errback Example + }); + /** + An `Ember.Binding` connects the properties of two objects so that whenever + the value of one property changes, the other property will be changed also. - ```js - findResult(function(result, err){ - if (err) { - // failure - } else { - // success - } - }); - ``` + ## Automatic Creation of Bindings with `/^*Binding/`-named Properties - Promise Example; + You do not usually create Binding objects directly but instead describe + bindings in your class or object definition using automatic binding + detection. - ```javascript - findResult().then(function(result){ - // success - }, function(reason){ - // failure - }); + Properties ending in a `Binding` suffix will be converted to `Ember.Binding` + instances. The value of this property should be a string representing a path + to another object or a custom binding instance created using Binding helpers + (see "One Way Bindings"): + + ``` + valueBinding: "MyApp.someController.title" ``` - Advanced Example - -------------- + This will create a binding from `MyApp.someController.title` to the `value` + property of your object instance automatically. Now the two values will be + kept in sync. - Synchronous Example + ## One Way Bindings - ```javascript - var author, books; + One especially useful binding customization you can use is the `oneWay()` + helper. This helper tells Ember that you are only interested in + receiving changes on the object you are binding from. For example, if you + are binding to a preference and you want to be notified if the preference + has changed, but your object will not be changing the preference itself, you + could do: - try { - author = findAuthor(); - books = findBooksByAuthor(author); - // success - } catch(reason) { - // failure - } + ``` + bigTitlesBinding: Ember.Binding.oneWay("MyApp.preferencesController.bigTitles") ``` - Errback Example + This way if the value of `MyApp.preferencesController.bigTitles` changes the + `bigTitles` property of your object will change also. However, if you + change the value of your `bigTitles` property, it will not update the + `preferencesController`. - ```js + One way bindings are almost twice as fast to setup and twice as fast to + execute because the binding only has to worry about changes to one side. - function foundBooks(books) { + You should consider using one way bindings anytime you have an object that + may be created frequently and you do not intend to change a property; only + to monitor it for changes (such as in the example above). - } + ## Adding Bindings Manually - function failure(reason) { + All of the examples above show you how to configure a custom binding, but the + result of these customizations will be a binding template, not a fully active + Binding instance. The binding will actually become active only when you + instantiate the object the binding belongs to. It is useful however, to + understand what actually happens when the binding is activated. - } + For a binding to function it must have at least a `from` property and a `to` + property. The `from` property path points to the object/key that you want to + bind from while the `to` path points to the object/key you want to bind to. - findAuthor(function(author, err){ - if (err) { - failure(err); - // failure - } else { - try { - findBoooksByAuthor(author, function(books, err) { - if (err) { - failure(err); - } else { - try { - foundBooks(books); - } catch(reason) { - failure(reason); - } - } - }); - } catch(error) { - failure(err); - } - // success - } - }); + When you define a custom binding, you are usually describing the property + you want to bind from (such as `MyApp.someController.value` in the examples + above). When your object is created, it will automatically assign the value + you want to bind `to` based on the name of your binding key. In the + examples above, during init, Ember objects will effectively call + something like this on your binding: + + ```javascript + binding = Ember.Binding.from("valueBinding").to("value"); ``` - Promise Example; + This creates a new binding instance based on the template you provide, and + sets the to path to the `value` property of the new object. Now that the + binding is fully configured with a `from` and a `to`, it simply needs to be + connected to become active. This is done through the `connect()` method: ```javascript - findAuthor(). - then(findBooksByAuthor). - then(function(books){ - // found books - }).catch(function(reason){ - // something went wrong - }); + binding.connect(this); ``` - @method then - @param {Function} onFulfilled - @param {Function} onRejected - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} - */ - then: function(onFulfillment, onRejection, label) { - var promise = this; - this._onerror = null; + Note that when you connect a binding you pass the object you want it to be + connected to. This object will be used as the root for both the from and + to side of the binding when inspecting relative paths. This allows the + binding to be automatically inherited by subclassed objects as well. - var thenPromise = new this.constructor(noop, label); + This also allows you to bind between objects using the paths you declare in + `from` and `to`: - if (this._state) { - var callbacks = arguments; - config.async(function invokePromiseCallback() { - invokeCallback(promise._state, thenPromise, callbacks[promise._state - 1], promise._detail); - }); - } else { - subscribe(this, thenPromise, onFulfillment, onRejection); - } + ```javascript + // Example 1 + binding = Ember.Binding.from("App.someObject.value").to("value"); + binding.connect(this); - if (config.instrument) { - instrument('chained', promise, thenPromise); - } + // Example 2 + binding = Ember.Binding.from("parentView.value").to("App.someObject.value"); + binding.connect(this); + ``` - return thenPromise; - }, + Now that the binding is connected, it will observe both the from and to side + and relay changes. - /** - `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same - as the catch block of a try/catch statement. + If you ever needed to do so (you almost never will, but it is useful to + understand this anyway), you could manually create an active binding by + using the `Ember.bind()` helper method. (This is the same method used by + to setup your bindings on objects): - ```js - function findAuthor(){ - throw new Error("couldn't find that author"); - } + ```javascript + Ember.bind(MyApp.anotherObject, "value", "MyApp.someController.value"); + ``` - // synchronous - try { - findAuthor(); - } catch(reason) { - // something went wrong - } + Both of these code fragments have the same effect as doing the most friendly + form of binding creation like so: - // async with promises - findAuthor().catch(function(reason){ - // something went wrong + ```javascript + MyApp.anotherObject = Ember.Object.create({ + valueBinding: "MyApp.someController.value", + + // OTHER CODE FOR THIS OBJECT... }); ``` - @method catch - @param {Function} onRejection - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} + Ember's built in binding creation method makes it easy to automatically + create bindings for you. You should always use the highest-level APIs + available, even if you understand how it works underneath. + + @class Binding + @namespace Ember + @since Ember 0.9 */ - 'catch': function(onRejection, label) { - return this.then(null, onRejection, label); - }, + // Ember.Binding = Binding; ES6TODO: where to put this? - /** - `finally` will be invoked regardless of the promise's fate just as native - try/catch/finally behaves - Synchronous example: + /** + Global helper method to create a new binding. Just pass the root object + along with a `to` and `from` path to create and connect the binding. - ```js - findAuthor() { - if (Math.random() > 0.5) { - throw new Error(); - } - return new Author(); - } + @method bind + @for Ember + @param {Object} obj The root object of the transform. + @param {String} to The path to the 'to' side of the binding. + Must be relative to obj. + @param {String} from The path to the 'from' side of the binding. + Must be relative to obj or a global path. + @return {Ember.Binding} binding instance + */ + function bind(obj, to, from) { + return new Binding(to, from).connect(obj); + } - try { - return findAuthor(); // succeed or fail - } catch(error) { - return findOtherAuther(); - } finally { - // always runs - // doesn't affect the return value - } - ``` + __exports__.bind = bind;/** + @method oneWay + @for Ember + @param {Object} obj The root object of the transform. + @param {String} to The path to the 'to' side of the binding. + Must be relative to obj. + @param {String} from The path to the 'from' side of the binding. + Must be relative to obj or a global path. + @return {Ember.Binding} binding instance + */ + function oneWay(obj, to, from) { + return new Binding(to, from).oneWay().connect(obj); + } - Asynchronous example: + __exports__.oneWay = oneWay;__exports__.Binding = Binding; + __exports__.isGlobalPath = isGlobalPath; + }); +enifed("ember-metal/cache", + ["ember-metal/dictionary","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var dictionary = __dependency1__["default"]; + __exports__["default"] = Cache; - ```js - findAuthor().catch(function(reason){ - return findOtherAuther(); - }).finally(function(){ - // author was either found, or not - }); - ``` + function Cache(limit, func) { + this.store = dictionary(null); + this.size = 0; + this.misses = 0; + this.hits = 0; + this.limit = limit; + this.func = func; + } - @method finally - @param {Function} callback - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} - */ - 'finally': function(callback, label) { - var constructor = this.constructor; + var UNDEFINED = function() { }; - return this.then(function(value) { - return constructor.cast(callback()).then(function(){ - return value; - }); - }, function(reason) { - return constructor.cast(callback()).then(function(){ - throw reason; - }); - }, label); - } - }; + Cache.prototype = { + set: function(key, value) { + if (this.limit > this.size) { + this.size ++; + if (value === undefined) { + this.store[key] = UNDEFINED; + } else { + this.store[key] = value; + } + } - function invokeCallback(settled, promise, callback, detail) { - var hasCallback = isFunction(callback), - value, error, succeeded, failed; + return value; + }, - if (hasCallback) { - try { - value = callback(detail); - succeeded = true; - } catch(e) { - failed = true; - error = e; + get: function(key) { + var value = this.store[key]; + + if (value === undefined) { + this.misses ++; + value = this.set(key, this.func(key)); + } else if (value === UNDEFINED) { + this.hits ++; + value = undefined; + } else { + this.hits ++; + // nothing to translate } - } else { - value = detail; - succeeded = true; - } - if (handleThenable(promise, value)) { - return; - } else if (hasCallback && succeeded) { - resolve(promise, value); - } else if (failed) { - reject(promise, error); - } else if (settled === FULFILLED) { - resolve(promise, value); - } else if (settled === REJECTED) { - reject(promise, value); + return value; + }, + + purge: function() { + this.store = dictionary(null); + this.size = 0; + this.hits = 0; + this.misses = 0; } + }; + }); +enifed("ember-metal/chains", + ["ember-metal/core","ember-metal/property_get","ember-metal/utils","ember-metal/array","ember-metal/watch_key","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // warn, assert, etc; + var get = __dependency2__.get; + var normalizeTuple = __dependency2__.normalizeTuple; + var metaFor = __dependency3__.meta; + var forEach = __dependency4__.forEach; + var watchKey = __dependency5__.watchKey; + var unwatchKey = __dependency5__.unwatchKey; + + var warn = Ember.warn; + var FIRST_KEY = /^([^\.]+)/; + + function firstKey(path) { + return path.match(FIRST_KEY)[0]; } - function handleThenable(promise, value) { - var then = null, - resolved; + var pendingQueue = []; - try { - if (promise === value) { - throw new TypeError("A promises callback cannot return that same promise."); - } + // attempts to add the pendingQueue chains again. If some of them end up + // back in the queue and reschedule is true, schedules a timeout to try + // again. + function flushPendingChains() { + if (pendingQueue.length === 0) { return; } // nothing to do - if (objectOrFunction(value)) { - then = value.then; + var queue = pendingQueue; + pendingQueue = []; - if (isFunction(then)) { - then.call(value, function(val) { - if (resolved) { return true; } - resolved = true; + forEach.call(queue, function(q) { + q[0].add(q[1]); + }); - if (value !== val) { - resolve(promise, val); - } else { - fulfill(promise, val); - } - }, function(val) { - if (resolved) { return true; } - resolved = true; + warn('Watching an undefined global, Ember expects watched globals to be' + + ' setup by the time the run loop is flushed, check for typos', pendingQueue.length === 0); + } - reject(promise, val); - }, 'derived from: ' + (promise._label || ' unknown promise')); + __exports__.flushPendingChains = flushPendingChains;function addChainWatcher(obj, keyName, node) { + if (!obj || ('object' !== typeof obj)) { return; } // nothing to do - return true; - } - } - } catch (error) { - if (resolved) { return true; } - reject(promise, error); - return true; - } + var m = metaFor(obj); + var nodes = m.chainWatchers; - return false; - } + if (!m.hasOwnProperty('chainWatchers')) { + nodes = m.chainWatchers = {}; + } - function resolve(promise, value) { - if (promise === value) { - fulfill(promise, value); - } else if (!handleThenable(promise, value)) { - fulfill(promise, value); + if (!nodes[keyName]) { + nodes[keyName] = []; } + nodes[keyName].push(node); + watchKey(obj, keyName, m); } - function fulfill(promise, value) { - if (promise._state !== PENDING) { return; } - promise._state = SEALED; - promise._detail = value; + function removeChainWatcher(obj, keyName, node) { + if (!obj || 'object' !== typeof obj) { return; } // nothing to do - config.async(publishFulfillment, promise); - } + var m = obj['__ember_meta__']; + if (m && !m.hasOwnProperty('chainWatchers')) { return; } // nothing to do - function reject(promise, reason) { - if (promise._state !== PENDING) { return; } - promise._state = SEALED; - promise._detail = reason; + var nodes = m && m.chainWatchers; - config.async(publishRejection, promise); + if (nodes && nodes[keyName]) { + nodes = nodes[keyName]; + for (var i = 0, l = nodes.length; i < l; i++) { + if (nodes[i] === node) { + nodes.splice(i, 1); + break; + } + } + } + unwatchKey(obj, keyName, m); } - function publishFulfillment(promise) { - publish(promise, promise._state = FULFILLED); - } + // A ChainNode watches a single key on an object. If you provide a starting + // value for the key then the node won't actually watch it. For a root node + // pass null for parent and key and object for value. + function ChainNode(parent, key, value) { + this._parent = parent; + this._key = key; - function publishRejection(promise) { - if (promise._onerror) { - promise._onerror(promise._detail); + // _watching is true when calling get(this._parent, this._key) will + // return the value of this node. + // + // It is false for the root of a chain (because we have no parent) + // and for global paths (because the parent node is the object with + // the observer on it) + this._watching = value===undefined; + + this._value = value; + this._paths = {}; + if (this._watching) { + this._object = parent.value(); + if (this._object) { + addChainWatcher(this._object, this._key, this); + } } - publish(promise, promise._state = REJECTED); + // Special-case: the EachProxy relies on immediate evaluation to + // establish its observers. + // + // TODO: Replace this with an efficient callback that the EachProxy + // can implement. + if (this._parent && this._parent._key === '@each') { + this.value(); + } } - }); -define("rsvp/promise/all", - ["../utils","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var isArray = __dependency1__.isArray; - var isNonThenable = __dependency1__.isNonThenable; - - /** - `RSVP.Promise.all` accepts an array of promises, and returns a new promise which - is fulfilled with an array of fulfillment values for the passed promises, or - rejected with the reason of the first passed promise to be rejected. It casts all - elements of the passed iterable to promises as it runs this algorithm. - Example: + var ChainNodePrototype = ChainNode.prototype; - ```javascript - var promise1 = RSVP.resolve(1); - var promise2 = RSVP.resolve(2); - var promise3 = RSVP.resolve(3); - var promises = [ promise1, promise2, promise3 ]; + function lazyGet(obj, key) { + if (!obj) return undefined; - RSVP.Promise.all(promises).then(function(array){ - // The array here would be [ 1, 2, 3 ]; - }); - ``` + var meta = obj['__ember_meta__']; + // check if object meant only to be a prototype + if (meta && meta.proto === obj) { + return undefined; + } - If any of the `promises` given to `RSVP.all` are rejected, the first promise - that is rejected will be given as an argument to the returned promises's - rejection handler. For example: + if (key === "@each") { + return get(obj, key); + } - Example: + // if a CP only return cached value + var desc = meta && meta.descs[key]; + if (desc && desc._cacheable) { + if (key in meta.cache) { + return meta.cache[key]; + } else { + return undefined; + } + } - ```javascript - var promise1 = RSVP.resolve(1); - var promise2 = RSVP.reject(new Error("2")); - var promise3 = RSVP.reject(new Error("3")); - var promises = [ promise1, promise2, promise3 ]; + return get(obj, key); + } - RSVP.Promise.all(promises).then(function(array){ - // Code here never runs because there are rejected promises! - }, function(error) { - // error.message === "2" - }); - ``` + ChainNodePrototype.value = function() { + if (this._value === undefined && this._watching) { + var obj = this._parent.value(); + this._value = lazyGet(obj, this._key); + } + return this._value; + }; - @method all - @for Ember.RSVP.Promise - @param {Array} entries array of promises - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} promise that is fulfilled when all `promises` have been - fulfilled, or rejected if any of them become rejected. - @static - */ - __exports__["default"] = function all(entries, label) { + ChainNodePrototype.destroy = function() { + if (this._watching) { + var obj = this._object; + if (obj) { + removeChainWatcher(obj, this._key, this); + } + this._watching = false; // so future calls do nothing + } + }; - /*jshint validthis:true */ - var Constructor = this; + // copies a top level object only + ChainNodePrototype.copy = function(obj) { + var ret = new ChainNode(null, null, obj); + var paths = this._paths; + var path; - return new Constructor(function(resolve, reject) { - if (!isArray(entries)) { - throw new TypeError('You must pass an array to all.'); + for (path in paths) { + // this check will also catch non-number vals. + if (paths[path] <= 0) { + continue; } + ret.add(path); + } + return ret; + }; - var remaining = entries.length; - var results = new Array(remaining); - var entry, pending = true; + // called on the root node of a chain to setup watchers on the specified + // path. + ChainNodePrototype.add = function(path) { + var obj, tuple, key, src, paths; - if (remaining === 0) { - resolve(results); - return; - } + paths = this._paths; + paths[path] = (paths[path] || 0) + 1; - function fulfillmentAt(index) { - return function(value) { - results[index] = value; - if (--remaining === 0) { - resolve(results); - } - }; - } + obj = this.value(); + tuple = normalizeTuple(obj, path); - function onRejection(reason) { - remaining = 0; - reject(reason); - } + // the path was a local path + if (tuple[0] && tuple[0] === obj) { + path = tuple[1]; + key = firstKey(path); + path = path.slice(key.length+1); - for (var index = 0; index < entries.length; index++) { - entry = entries[index]; - if (isNonThenable(entry)) { - results[index] = entry; - if (--remaining === 0) { - resolve(results); - } - } else { - Constructor.cast(entry).then(fulfillmentAt(index), onRejection); - } - } - }, label); + // global path, but object does not exist yet. + // put into a queue and try to connect later. + } else if (!tuple[0]) { + pendingQueue.push([this, path]); + tuple.length = 0; + return; + + // global path, and object already exists + } else { + src = tuple[0]; + key = path.slice(0, 0-(tuple[1].length+1)); + path = tuple[1]; + } + + tuple.length = 0; + this.chain(key, path, src); }; - }); -define("rsvp/promise/cast", - ["exports"], - function(__exports__) { - "use strict"; - /** - `RSVP.Promise.cast` coerces its argument to a promise, or returns the - argument if it is already a promise which shares a constructor with the caster. - Example: + // called on the root node of a chain to teardown watcher on the specified + // path + ChainNodePrototype.remove = function(path) { + var obj, tuple, key, src, paths; - ```javascript - var promise = RSVP.Promise.resolve(1); - var casted = RSVP.Promise.cast(promise); + paths = this._paths; + if (paths[path] > 0) { + paths[path]--; + } - console.log(promise === casted); // true - ``` + obj = this.value(); + tuple = normalizeTuple(obj, path); + if (tuple[0] === obj) { + path = tuple[1]; + key = firstKey(path); + path = path.slice(key.length+1); + } else { + src = tuple[0]; + key = path.slice(0, 0-(tuple[1].length+1)); + path = tuple[1]; + } - In the case of a promise whose constructor does not match, it is assimilated. - The resulting promise will fulfill or reject based on the outcome of the - promise being casted. + tuple.length = 0; + this.unchain(key, path); + }; - Example: + ChainNodePrototype.count = 0; - ```javascript - var thennable = $.getJSON('/api/foo'); - var casted = RSVP.Promise.cast(thennable); + ChainNodePrototype.chain = function(key, path, src) { + var chains = this._chains; + var node; + if (!chains) { + chains = this._chains = {}; + } - console.log(thennable === casted); // false - console.log(casted instanceof RSVP.Promise) // true + node = chains[key]; + if (!node) { + node = chains[key] = new ChainNode(this, key, src); + } + node.count++; // count chains... - casted.then(function(data) { - // data is the value getJSON fulfills with - }); - ``` + // chain rest of path if there is one + if (path) { + key = firstKey(path); + path = path.slice(key.length+1); + node.chain(key, path); // NOTE: no src means it will observe changes... + } + }; - In the case of a non-promise, a promise which will fulfill with that value is - returned. + ChainNodePrototype.unchain = function(key, path) { + var chains = this._chains; + var node = chains[key]; - Example: + // unchain rest of path first... + if (path && path.length > 1) { + var nextKey = firstKey(path); + var nextPath = path.slice(nextKey.length + 1); + node.unchain(nextKey, nextPath); + } - ```javascript - var value = 1; // could be a number, boolean, string, undefined... - var casted = RSVP.Promise.cast(value); + // delete node if needed. + node.count--; + if (node.count<=0) { + delete chains[node._key]; + node.destroy(); + } - console.log(value === casted); // false - console.log(casted instanceof RSVP.Promise) // true + }; - casted.then(function(val) { - val === value // => true - }); - ``` + ChainNodePrototype.willChange = function(events) { + var chains = this._chains; + if (chains) { + for(var key in chains) { + if (!chains.hasOwnProperty(key)) { + continue; + } + chains[key].willChange(events); + } + } - `RSVP.Promise.cast` is similar to `RSVP.Promise.resolve`, but `RSVP.Promise.cast` differs in the - following ways: + if (this._parent) { + this._parent.chainWillChange(this, this._key, 1, events); + } + }; - * `RSVP.Promise.cast` serves as a memory-efficient way of getting a promise, when you - have something that could either be a promise or a value. RSVP.resolve - will have the same effect but will create a new promise wrapper if the - argument is a promise. - * `RSVP.Promise.cast` is a way of casting incoming thenables or promise subclasses to - promises of the exact class specified, so that the resulting object's `then` is - ensured to have the behavior of the constructor you are calling cast on (i.e., RSVP.Promise). + ChainNodePrototype.chainWillChange = function(chain, path, depth, events) { + if (this._key) { + path = this._key + '.' + path; + } - @method cast - @param {Object} object to be casted - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} promise - @static - */ + if (this._parent) { + this._parent.chainWillChange(this, path, depth+1, events); + } else { + if (depth > 1) { + events.push(this.value(), path); + } + path = 'this.' + path; + if (this._paths[path] > 0) { + events.push(this.value(), path); + } + } + }; - __exports__["default"] = function cast(object, label) { - /*jshint validthis:true */ - var Constructor = this; + ChainNodePrototype.chainDidChange = function(chain, path, depth, events) { + if (this._key) { + path = this._key + '.' + path; + } - if (object && typeof object === 'object' && object.constructor === Constructor) { - return object; + if (this._parent) { + this._parent.chainDidChange(this, path, depth+1, events); + } else { + if (depth > 1) { + events.push(this.value(), path); + } + path = 'this.' + path; + if (this._paths[path] > 0) { + events.push(this.value(), path); + } } + }; - return new Constructor(function(resolve) { - resolve(object); - }, label); + ChainNodePrototype.didChange = function(events) { + // invalidate my own value first. + if (this._watching) { + var obj = this._parent.value(); + if (obj !== this._object) { + removeChainWatcher(this._object, this._key, this); + this._object = obj; + addChainWatcher(obj, this._key, this); + } + this._value = undefined; + + // Special-case: the EachProxy relies on immediate evaluation to + // establish its observers. + if (this._parent && this._parent._key === '@each') { + this.value(); + } + } + + // then notify chains... + var chains = this._chains; + if (chains) { + for(var key in chains) { + if (!chains.hasOwnProperty(key)) { continue; } + chains[key].didChange(events); + } + } + + // if no events are passed in then we only care about the above wiring update + if (events === null) { + return; + } + + // and finally tell parent about my path changing... + if (this._parent) { + this._parent.chainDidChange(this, this._key, 1, events); + } }; + + function finishChains(obj) { + // We only create meta if we really have to + var m = obj['__ember_meta__']; + var chains, chainWatchers, chainNodes; + + if (m) { + // finish any current chains node watchers that reference obj + chainWatchers = m.chainWatchers; + if (chainWatchers) { + for(var key in chainWatchers) { + if (!chainWatchers.hasOwnProperty(key)) { + continue; + } + + chainNodes = chainWatchers[key]; + if (chainNodes) { + for (var i=0,l=chainNodes.length;i true + client.set('lastName', 'Fuller'); + client.get('fullName'); // 'Betty Fuller' + ``` - container.unregister('model:user') - container.lookup('model:user') === undefined //=> true - ``` + _Note: This is the preferred way to define computed properties when writing third-party + libraries that depend on or use Ember, since there is no guarantee that the user + will have prototype extensions enabled._ - @method unregister - @param {String} fullName - */ - unregister: function(fullName) { - validateFullName(fullName); + You might use this method if you disabled + [Prototype Extensions](http://emberjs.com/guides/configuring-ember/disabling-prototype-extensions/). + The alternative syntax might look like this + (if prototype extensions are enabled, which is the default behavior): - var normalizedName = this.normalize(fullName); + ```js + fullName: function () { + return this.get('firstName') + ' ' + this.get('lastName'); + }.property('firstName', 'lastName') + ``` - this.registry.remove(normalizedName); - this.cache.remove(normalizedName); - this.factoryCache.remove(normalizedName); - this.resolveCache.remove(normalizedName); - this._options.remove(normalizedName); - }, + @method computed + @for Ember + @param {String} [dependentKeys*] Optional dependent keys that trigger this computed property. + @param {Function} func The computed property function. + @return {Ember.ComputedProperty} property descriptor instance + */ + function computed(func) { + var args; - /** - Given a fullName return the corresponding factory. + if (arguments.length > 1) { + args = a_slice.call(arguments); + func = args.pop(); + } - By default `resolve` will retrieve the factory from - its container's registry. + if (typeof func !== "function") { + throw new EmberError("Computed Property declared without a property function"); + } - ```javascript - var container = new Container(); - container.register('api:twitter', Twitter); + var cp = new ComputedProperty(func); - container.resolve('api:twitter') // => Twitter - ``` + if (args) { + cp.property.apply(cp, args); + } - Optionally the container can be provided with a custom resolver. - If provided, `resolve` will first provide the custom resolver - the opportunity to resolve the fullName, otherwise it will fallback - to the registry. + return cp; + } - ```javascript - var container = new Container(); - container.resolver = function(fullName) { - // lookup via the module system of choice - }; - - // the twitter factory is added to the module system - container.resolve('api:twitter') // => Twitter - ``` + /** + Returns the cached value for a property, if one exists. + This can be useful for peeking at the value of a computed + property that is generated lazily, without accidentally causing + it to be created. - @method resolve - @param {String} fullName - @return {Function} fullName's factory - */ - resolve: function(fullName) { - validateFullName(fullName); + @method cacheFor + @for Ember + @param {Object} obj the object whose property you want to check + @param {String} key the name of the property whose cached value you want + to return + @return {Object} the cached value + */ + function cacheFor(obj, key) { + var meta = obj['__ember_meta__']; + var cache = meta && meta.cache; + var ret = cache && cache[key]; - var normalizedName = this.normalize(fullName); - var cached = this.resolveCache.get(normalizedName); + if (ret === UNDEFINED) { + return undefined; + } + return ret; + } - if (cached) { return cached; } + cacheFor.set = function(cache, key, value) { + if (value === undefined) { + cache[key] = UNDEFINED; + } else { + cache[key] = value; + } + }; - var resolved = this.resolver(normalizedName) || this.registry.get(normalizedName); + cacheFor.get = function(cache, key) { + var ret = cache[key]; + if (ret === UNDEFINED) { + return undefined; + } + return ret; + }; - this.resolveCache.set(normalizedName, resolved); + cacheFor.remove = function(cache, key) { + cache[key] = undefined; + }; - return resolved; - }, + __exports__.ComputedProperty = ComputedProperty; + __exports__.computed = computed; + __exports__.cacheFor = cacheFor; + }); +enifed("ember-metal/computed_macros", + ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/computed","ember-metal/is_empty","ember-metal/is_none","ember-metal/alias"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__) { + "use strict"; + var Ember = __dependency1__["default"]; + var get = __dependency2__.get; + var set = __dependency3__.set; + var computed = __dependency4__.computed; + var isEmpty = __dependency5__["default"]; + var isNone = __dependency6__["default"]; + var alias = __dependency7__["default"]; - /** - A hook that can be used to describe how the resolver will - attempt to find the factory. + /** + @module ember-metal + */ - For example, the default Ember `.describe` returns the full - class name (including namespace) where Ember's resolver expects - to find the `fullName`. + var a_slice = [].slice; - @method describe - @param {String} fullName - @return {string} described fullName - */ - describe: function(fullName) { - return fullName; - }, + function getProperties(self, propertyNames) { + var ret = {}; + for(var i = 0; i < propertyNames.length; i++) { + ret[propertyNames[i]] = get(self, propertyNames[i]); + } + return ret; + } - /** - A hook to enable custom fullName normalization behaviour + function registerComputed(name, macro) { + computed[name] = function(dependentKey) { + var args = a_slice.call(arguments); + return computed(dependentKey, function() { + return macro.apply(this, args); + }); + }; + } - @method normalize - @param {String} fullName - @return {string} normalized fullName - */ - normalize: function(fullName) { - return fullName; - }, + function registerComputedWithProperties(name, macro) { + computed[name] = function() { + var properties = a_slice.call(arguments); - /** - @method makeToString + var computedFunc = computed(function() { + return macro.apply(this, [getProperties(this, properties)]); + }); - @param {any} factory - @param {string} fullName - @return {function} toString function - */ - makeToString: function(factory, fullName) { - return factory.toString(); - }, + return computedFunc.property.apply(computedFunc, properties); + }; + } - /** - Given a fullName return a corresponding instance. + /** + A computed property that returns true if the value of the dependent + property is null, an empty string, empty array, or empty function. - The default behaviour is for lookup to return a singleton instance. - The singleton is scoped to the container, allowing multiple containers - to all have their own locally scoped singletons. + Example - ```javascript - var container = new Container(); - container.register('api:twitter', Twitter); + ```javascript + var ToDoList = Ember.Object.extend({ + isDone: Ember.computed.empty('todos') + }); - var twitter = container.lookup('api:twitter'); + var todoList = ToDoList.create({ + todos: ['Unit Test', 'Documentation', 'Release'] + }); - twitter instanceof Twitter; // => true + todoList.get('isDone'); // false + todoList.get('todos').clear(); + todoList.get('isDone'); // true + ``` - // by default the container will return singletons - var twitter2 = container.lookup('api:twitter'); - twitter2 instanceof Twitter; // => true + @since 1.6.0 + @method computed.empty + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which negate + the original value for property + */ + computed.empty = function (dependentKey) { + return computed(dependentKey + '.length', function () { + return isEmpty(get(this, dependentKey)); + }); + }; - twitter === twitter2; //=> true - ``` + /** + A computed property that returns true if the value of the dependent + property is NOT null, an empty string, empty array, or empty function. - If singletons are not wanted an optional flag can be provided at lookup. + Example - ```javascript - var container = new Container(); - container.register('api:twitter', Twitter); + ```javascript + var Hamster = Ember.Object.extend({ + hasStuff: Ember.computed.notEmpty('backpack') + }); - var twitter = container.lookup('api:twitter', { singleton: false }); - var twitter2 = container.lookup('api:twitter', { singleton: false }); + var hamster = Hamster.create({ backpack: ['Food', 'Sleeping Bag', 'Tent'] }); - twitter === twitter2; //=> false - ``` + hamster.get('hasStuff'); // true + hamster.get('backpack').clear(); // [] + hamster.get('hasStuff'); // false + ``` - @method lookup - @param {String} fullName - @param {Object} options - @return {any} - */ - lookup: function(fullName, options) { - validateFullName(fullName); - return lookup(this, this.normalize(fullName), options); - }, + @method computed.notEmpty + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which returns true if + original value for property is not empty. + */ + computed.notEmpty = function(dependentKey) { + return computed(dependentKey + '.length', function () { + return !isEmpty(get(this, dependentKey)); + }); + }; - /** - Given a fullName return the corresponding factory. + /** + A computed property that returns true if the value of the dependent + property is null or undefined. This avoids errors from JSLint complaining + about use of ==, which can be technically confusing. - @method lookupFactory - @param {String} fullName - @return {any} - */ - lookupFactory: function(fullName) { - validateFullName(fullName); - return factoryFor(this, this.normalize(fullName)); - }, + Example - /** - Given a fullName check if the container is aware of its factory - or singleton instance. + ```javascript + var Hamster = Ember.Object.extend({ + isHungry: Ember.computed.none('food') + }); - @method has - @param {String} fullName - @return {Boolean} - */ - has: function(fullName) { - validateFullName(fullName); - return has(this, this.normalize(fullName)); - }, + var hamster = Hamster.create(); - /** - Allow registering options for all factories of a type. + hamster.get('isHungry'); // true + hamster.set('food', 'Banana'); + hamster.get('isHungry'); // false + hamster.set('food', null); + hamster.get('isHungry'); // true + ``` - ```javascript - var container = new Container(); + @method computed.none + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which + returns true if original value for property is null or undefined. + */ + registerComputed('none', function(dependentKey) { + return isNone(get(this, dependentKey)); + }); - // if all of type `connection` must not be singletons - container.optionsForType('connection', { singleton: false }); + /** + A computed property that returns the inverse boolean value + of the original value for the dependent property. - container.register('connection:twitter', TwitterConnection); - container.register('connection:facebook', FacebookConnection); + Example - var twitter = container.lookup('connection:twitter'); - var twitter2 = container.lookup('connection:twitter'); + ```javascript + var User = Ember.Object.extend({ + isAnonymous: Ember.computed.not('loggedIn') + }); - twitter === twitter2; // => false + var user = User.create({loggedIn: false}); - var facebook = container.lookup('connection:facebook'); - var facebook2 = container.lookup('connection:facebook'); + user.get('isAnonymous'); // true + user.set('loggedIn', true); + user.get('isAnonymous'); // false + ``` - facebook === facebook2; // => false - ``` + @method computed.not + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which returns + inverse of the original value for property + */ + registerComputed('not', function(dependentKey) { + return !get(this, dependentKey); + }); - @method optionsForType - @param {String} type - @param {Object} options - */ - optionsForType: function(type, options) { - if (this.parent) { illegalChildOperation('optionsForType'); } + /** + A computed property that converts the provided dependent property + into a boolean value. - this._typeOptions.set(type, options); - }, + ```javascript + var Hamster = Ember.Object.extend({ + hasBananas: Ember.computed.bool('numBananas') + }); - /** - @method options - @param {String} type - @param {Object} options - */ - options: function(type, options) { - this.optionsForType(type, options); - }, + var hamster = Hamster.create(); - /** - Used only via `injection`. + hamster.get('hasBananas'); // false + hamster.set('numBananas', 0); + hamster.get('hasBananas'); // false + hamster.set('numBananas', 1); + hamster.get('hasBananas'); // true + hamster.set('numBananas', null); + hamster.get('hasBananas'); // false + ``` - Provides a specialized form of injection, specifically enabling - all objects of one type to be injected with a reference to another - object. + @method computed.bool + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which converts + to boolean the original value for property + */ + registerComputed('bool', function(dependentKey) { + return !!get(this, dependentKey); + }); - For example, provided each object of type `controller` needed a `router`. - one would do the following: + /** + A computed property which matches the original value for the + dependent property against a given RegExp, returning `true` + if they values matches the RegExp and `false` if it does not. - ```javascript - var container = new Container(); + Example - container.register('router:main', Router); - container.register('controller:user', UserController); - container.register('controller:post', PostController); + ```javascript + var User = Ember.Object.extend({ + hasValidEmail: Ember.computed.match('email', /^.+@.+\..+$/) + }); - container.typeInjection('controller', 'router', 'router:main'); + var user = User.create({loggedIn: false}); - var user = container.lookup('controller:user'); - var post = container.lookup('controller:post'); + user.get('hasValidEmail'); // false + user.set('email', ''); + user.get('hasValidEmail'); // false + user.set('email', 'ember_hamster@example.com'); + user.get('hasValidEmail'); // true + ``` - user.router instanceof Router; //=> true - post.router instanceof Router; //=> true + @method computed.match + @for Ember + @param {String} dependentKey + @param {RegExp} regexp + @return {Ember.ComputedProperty} computed property which match + the original value for property against a given RegExp + */ + registerComputed('match', function(dependentKey, regexp) { + var value = get(this, dependentKey); + return typeof value === 'string' ? regexp.test(value) : false; + }); - // both controllers share the same router - user.router === post.router; //=> true - ``` + /** + A computed property that returns true if the provided dependent property + is equal to the given value. - @private - @method typeInjection - @param {String} type - @param {String} property - @param {String} fullName - */ - typeInjection: function(type, property, fullName) { - validateFullName(fullName); - if (this.parent) { illegalChildOperation('typeInjection'); } + Example - var fullNameType = fullName.split(':')[0]; - if(fullNameType === type) { - throw new Error('Cannot inject a `' + fullName + '` on other ' + type + '(s). Register the `' + fullName + '` as a different type and perform the typeInjection.'); - } - addTypeInjection(this.typeInjections, type, property, fullName); - }, + ```javascript + var Hamster = Ember.Object.extend({ + napTime: Ember.computed.equal('state', 'sleepy') + }); - /** - Defines injection rules. + var hamster = Hamster.create(); - These rules are used to inject dependencies onto objects when they - are instantiated. + hamster.get('napTime'); // false + hamster.set('state', 'sleepy'); + hamster.get('napTime'); // true + hamster.set('state', 'hungry'); + hamster.get('napTime'); // false + ``` - Two forms of injections are possible: + @method computed.equal + @for Ember + @param {String} dependentKey + @param {String|Number|Object} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is equal to the given value. + */ + registerComputed('equal', function(dependentKey, value) { + return get(this, dependentKey) === value; + }); - * Injecting one fullName on another fullName - * Injecting one fullName on a type + /** + A computed property that returns true if the provided dependent property + is greater than the provided value. - Example: + Example - ```javascript - var container = new Container(); + ```javascript + var Hamster = Ember.Object.extend({ + hasTooManyBananas: Ember.computed.gt('numBananas', 10) + }); - container.register('source:main', Source); - container.register('model:user', User); - container.register('model:post', Post); + var hamster = Hamster.create(); - // injecting one fullName on another fullName - // eg. each user model gets a post model - container.injection('model:user', 'post', 'model:post'); + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 3); + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 11); + hamster.get('hasTooManyBananas'); // true + ``` - // injecting one fullName on another type - container.injection('model', 'source', 'source:main'); + @method computed.gt + @for Ember + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is greater than given value. + */ + registerComputed('gt', function(dependentKey, value) { + return get(this, dependentKey) > value; + }); - var user = container.lookup('model:user'); - var post = container.lookup('model:post'); + /** + A computed property that returns true if the provided dependent property + is greater than or equal to the provided value. - user.source instanceof Source; //=> true - post.source instanceof Source; //=> true + Example - user.post instanceof Post; //=> true + ```javascript + var Hamster = Ember.Object.extend({ + hasTooManyBananas: Ember.computed.gte('numBananas', 10) + }); - // and both models share the same source - user.source === post.source; //=> true - ``` + var hamster = Hamster.create(); - @method injection - @param {String} factoryName - @param {String} property - @param {String} injectionName - */ - injection: function(fullName, property, injectionName) { - if (this.parent) { illegalChildOperation('injection'); } + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 3); + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 10); + hamster.get('hasTooManyBananas'); // true + ``` - validateFullName(injectionName); - var normalizedInjectionName = this.normalize(injectionName); + @method computed.gte + @for Ember + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is greater or equal then given value. + */ + registerComputed('gte', function(dependentKey, value) { + return get(this, dependentKey) >= value; + }); - if (fullName.indexOf(':') === -1) { - return this.typeInjection(fullName, property, normalizedInjectionName); - } + /** + A computed property that returns true if the provided dependent property + is less than the provided value. - validateFullName(fullName); - var normalizedName = this.normalize(fullName); + Example - addInjection(this.injections, normalizedName, property, normalizedInjectionName); - }, + ```javascript + var Hamster = Ember.Object.extend({ + needsMoreBananas: Ember.computed.lt('numBananas', 3) + }); + var hamster = Hamster.create(); - /** - Used only via `factoryInjection`. + hamster.get('needsMoreBananas'); // true + hamster.set('numBananas', 3); + hamster.get('needsMoreBananas'); // false + hamster.set('numBananas', 2); + hamster.get('needsMoreBananas'); // true + ``` - Provides a specialized form of injection, specifically enabling - all factory of one type to be injected with a reference to another - object. + @method computed.lt + @for Ember + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is less then given value. + */ + registerComputed('lt', function(dependentKey, value) { + return get(this, dependentKey) < value; + }); - For example, provided each factory of type `model` needed a `store`. - one would do the following: + /** + A computed property that returns true if the provided dependent property + is less than or equal to the provided value. - ```javascript - var container = new Container(); + Example - container.register('store:main', SomeStore); + ```javascript + var Hamster = Ember.Object.extend({ + needsMoreBananas: Ember.computed.lte('numBananas', 3) + }); - container.factoryTypeInjection('model', 'store', 'store:main'); + var hamster = Hamster.create(); - var store = container.lookup('store:main'); - var UserFactory = container.lookupFactory('model:user'); + hamster.get('needsMoreBananas'); // true + hamster.set('numBananas', 5); + hamster.get('needsMoreBananas'); // false + hamster.set('numBananas', 3); + hamster.get('needsMoreBananas'); // true + ``` - UserFactory.store instanceof SomeStore; //=> true - ``` + @method computed.lte + @for Ember + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is less or equal than given value. + */ + registerComputed('lte', function(dependentKey, value) { + return get(this, dependentKey) <= value; + }); - @private - @method factoryTypeInjection - @param {String} type - @param {String} property - @param {String} fullName - */ - factoryTypeInjection: function(type, property, fullName) { - if (this.parent) { illegalChildOperation('factoryTypeInjection'); } + /** + A computed property that performs a logical `and` on the + original values for the provided dependent properties. - addTypeInjection(this.factoryTypeInjections, type, property, this.normalize(fullName)); - }, + Example - /** - Defines factory injection rules. + ```javascript + var Hamster = Ember.Object.extend({ + readyForCamp: Ember.computed.and('hasTent', 'hasBackpack') + }); - Similar to regular injection rules, but are run against factories, via - `Container#lookupFactory`. + var hamster = Hamster.create(); - These rules are used to inject objects onto factories when they - are looked up. + hamster.get('readyForCamp'); // false + hamster.set('hasTent', true); + hamster.get('readyForCamp'); // false + hamster.set('hasBackpack', true); + hamster.get('readyForCamp'); // true + ``` - Two forms of injections are possible: + @method computed.and + @for Ember + @param {String} dependentKey* + @return {Ember.ComputedProperty} computed property which performs + a logical `and` on the values of all the original values for properties. + */ + registerComputedWithProperties('and', function(properties) { + for (var key in properties) { + if (properties.hasOwnProperty(key) && !properties[key]) { + return false; + } + } + return true; + }); - * Injecting one fullName on another fullName - * Injecting one fullName on a type + /** + A computed property which performs a logical `or` on the + original values for the provided dependent properties. - Example: + Example - ```javascript - var container = new Container(); + ```javascript + var Hamster = Ember.Object.extend({ + readyForRain: Ember.computed.or('hasJacket', 'hasUmbrella') + }); - container.register('store:main', Store); - container.register('store:secondary', OtherStore); - container.register('model:user', User); - container.register('model:post', Post); + var hamster = Hamster.create(); - // injecting one fullName on another type - container.factoryInjection('model', 'store', 'store:main'); + hamster.get('readyForRain'); // false + hamster.set('hasJacket', true); + hamster.get('readyForRain'); // true + ``` - // injecting one fullName on another fullName - container.factoryInjection('model:post', 'secondaryStore', 'store:secondary'); + @method computed.or + @for Ember + @param {String} dependentKey* + @return {Ember.ComputedProperty} computed property which performs + a logical `or` on the values of all the original values for properties. + */ + registerComputedWithProperties('or', function(properties) { + for (var key in properties) { + if (properties.hasOwnProperty(key) && properties[key]) { + return true; + } + } + return false; + }); - var UserFactory = container.lookupFactory('model:user'); - var PostFactory = container.lookupFactory('model:post'); - var store = container.lookup('store:main'); + /** + A computed property that returns the first truthy value + from a list of dependent properties. - UserFactory.store instanceof Store; //=> true - UserFactory.secondaryStore instanceof OtherStore; //=> false + Example - PostFactory.store instanceof Store; //=> true - PostFactory.secondaryStore instanceof OtherStore; //=> true + ```javascript + var Hamster = Ember.Object.extend({ + hasClothes: Ember.computed.any('hat', 'shirt') + }); - // and both models share the same source instance - UserFactory.store === PostFactory.store; //=> true - ``` + var hamster = Hamster.create(); - @method factoryInjection - @param {String} factoryName - @param {String} property - @param {String} injectionName - */ - factoryInjection: function(fullName, property, injectionName) { - if (this.parent) { illegalChildOperation('injection'); } + hamster.get('hasClothes'); // null + hamster.set('shirt', 'Hawaiian Shirt'); + hamster.get('hasClothes'); // 'Hawaiian Shirt' + ``` - var normalizedName = this.normalize(fullName); - var normalizedInjectionName = this.normalize(injectionName); + @method computed.any + @for Ember + @param {String} dependentKey* + @return {Ember.ComputedProperty} computed property which returns + the first truthy value of given list of properties. + */ + registerComputedWithProperties('any', function(properties) { + for (var key in properties) { + if (properties.hasOwnProperty(key) && properties[key]) { + return properties[key]; + } + } + return null; + }); - validateFullName(injectionName); + /** + A computed property that returns the array of values + for the provided dependent properties. - if (fullName.indexOf(':') === -1) { - return this.factoryTypeInjection(normalizedName, property, normalizedInjectionName); - } + Example - validateFullName(fullName); + ```javascript + var Hamster = Ember.Object.extend({ + clothes: Ember.computed.collect('hat', 'shirt') + }); - addInjection(this.factoryInjections, normalizedName, property, normalizedInjectionName); - }, + var hamster = Hamster.create(); - /** - A depth first traversal, destroying the container, its descendant containers and all - their managed objects. + hamster.get('clothes'); // [null, null] + hamster.set('hat', 'Camp Hat'); + hamster.set('shirt', 'Camp Shirt'); + hamster.get('clothes'); // ['Camp Hat', 'Camp Shirt'] + ``` - @method destroy - */ - destroy: function() { - for (var i=0, l=this.children.length; i' );` + teddy.get('firstName'); // 'Teddy' + ``` - return hash; - } + @method computed.readOnly + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which creates a + one way computed property to the original value for property. + @since 1.5.0 + */ + computed.readOnly = function(dependentKey) { + return alias(dependentKey).readOnly(); + }; + /** + A computed property that acts like a standard getter and setter, + but returns the value at the provided `defaultPath` if the + property itself has not been set to a value - function option(container, fullName, optionName) { - var options = container._options.get(fullName); + Example - if (options && options[optionName] !== undefined) { - return options[optionName]; - } + ```javascript + var Hamster = Ember.Object.extend({ + wishList: Ember.computed.defaultTo('favoriteFood') + }); - var type = fullName.split(":")[0]; - options = container._typeOptions.get(type); + var hamster = Hamster.create({ favoriteFood: 'Banana' }); - if (options) { - return options[optionName]; - } - } + hamster.get('wishList'); // 'Banana' + hamster.set('wishList', 'More Unit Tests'); + hamster.get('wishList'); // 'More Unit Tests' + hamster.get('favoriteFood'); // 'Banana' + ``` - function factoryFor(container, fullName) { - var name = fullName; - var factory = container.resolve(name); - var injectedFactory; - var cache = container.factoryCache; - var type = fullName.split(":")[0]; + @method computed.defaultTo + @for Ember + @param {String} defaultPath + @return {Ember.ComputedProperty} computed property which acts like + a standard getter and setter, but defaults to the value from `defaultPath`. + @deprecated Use `Ember.computed.oneWay` or custom CP with default instead. + */ + // ES6TODO: computed should have its own export path so you can do import {defaultTo} from computed + computed.defaultTo = function(defaultPath) { + return computed(function(key, newValue, cachedValue) { + Ember.deprecate('Usage of Ember.computed.defaultTo is deprecated, use `Ember.computed.oneWay` instead.'); - if (factory === undefined) { return; } + if (arguments.length === 1) { + return get(this, defaultPath); + } + return newValue != null ? newValue : get(this, defaultPath); + }); + }; - if (cache.has(fullName)) { - return cache.get(fullName); - } + /** + Creates a new property that is an alias for another property + on an object. Calls to `get` or `set` this property behave as + though they were called on the original property, but also + print a deprecation warning. - if (!factory || typeof factory.extend !== 'function' || (!Ember.MODEL_FACTORY_INJECTIONS && type === 'model')) { - // TODO: think about a 'safe' merge style extension - // for now just fallback to create time injection - return factory; - } else { + @method computed.deprecatingAlias + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which creates an + alias with a deprecation to the original value for property. + @since 1.7.0 + */ + computed.deprecatingAlias = function(dependentKey) { + return computed(dependentKey, function(key, value) { + Ember.deprecate('Usage of `' + key + '` is deprecated, use `' + dependentKey + '` instead.'); - var injections = injectionsFor(container, fullName); - var factoryInjections = factoryInjectionsFor(container, fullName); + if (arguments.length > 1) { + set(this, dependentKey, value); + return value; + } else { + return get(this, dependentKey); + } + }); + }; + }); +enifed("ember-metal/core", + ["exports"], + function(__exports__) { + "use strict"; + /*globals Ember:true,ENV,EmberENV,MetamorphENV:true */ - factoryInjections._toString = container.makeToString(factory, fullName); + /** + @module ember + @submodule ember-metal + */ - injectedFactory = factory.extend(injections); - injectedFactory.reopenClass(factoryInjections); + /** + All Ember methods and functions are defined inside of this namespace. You + generally should not add new properties to this namespace as it may be + overwritten by future versions of Ember. - cache.set(fullName, injectedFactory); + You can also use the shorthand `Em` instead of `Ember`. - return injectedFactory; - } + Ember-Runtime is a framework that provides core functions for Ember including + cross-platform functions, support for property observing and objects. Its + focus is on small size and performance. You can use this in place of or + along-side other cross-platform libraries such as jQuery. + + The core Runtime framework is based on the jQuery API with a number of + performance optimizations. + + @class Ember + @static + @version 1.10.0-beta.3 + */ + + if ('undefined' === typeof Ember) { + // Create core object. Make it act like an instance of Ember.Namespace so that + // objects assigned to it are given a sane string representation. + Ember = {}; } - function injectionsFor(container, fullName) { - var splitName = fullName.split(":"), - type = splitName[0], - injections = []; + // Default imports, exports and lookup to the global object; + Ember.imports = Ember.imports || this; + Ember.lookup = Ember.lookup || this; + var exports = Ember.exports = Ember.exports || this; - injections = injections.concat(container.typeInjections.get(type) || []); - injections = injections.concat(container.injections[fullName] || []); + // aliases needed to keep minifiers from removing the global context + exports.Em = exports.Ember = Ember; - injections = buildInjections(container, injections); - injections._debugContainerKey = fullName; - injections.container = container; + // Make sure these are set whether Ember was already defined or not - return injections; - } + Ember.isNamespace = true; - function factoryInjectionsFor(container, fullName) { - var splitName = fullName.split(":"), - type = splitName[0], - factoryInjections = []; + Ember.toString = function() { return "Ember"; }; - factoryInjections = factoryInjections.concat(container.factoryTypeInjections.get(type) || []); - factoryInjections = factoryInjections.concat(container.factoryInjections[fullName] || []); - factoryInjections = buildInjections(container, factoryInjections); - factoryInjections._debugContainerKey = fullName; + /** + @property VERSION + @type String + @default '1.10.0-beta.3' + @static + */ + Ember.VERSION = '1.10.0-beta.3'; - return factoryInjections; - } + /** + Standard environmental variables. You can define these in a global `EmberENV` + variable before loading Ember to control various configuration settings. - function instantiate(container, fullName) { - var factory = factoryFor(container, fullName); + For backwards compatibility with earlier versions of Ember the global `ENV` + variable will be used if `EmberENV` is not defined. - if (option(container, fullName, 'instantiate') === false) { - return factory; - } + @property ENV + @type Hash + */ - if (factory) { - if (typeof factory.extend === 'function') { - // assume the factory was extendable and is already injected - return factory.create(); - } else { - // assume the factory was extendable - // to create time injections - // TODO: support new'ing for instantiation and merge injections for pure JS Functions - return factory.create(injectionsFor(container, fullName)); - } - } + if (Ember.ENV) { + // do nothing if Ember.ENV is already setup + } else if ('undefined' !== typeof EmberENV) { + Ember.ENV = EmberENV; + } else if('undefined' !== typeof ENV) { + Ember.ENV = ENV; + } else { + Ember.ENV = {}; } - function eachDestroyable(container, callback) { - container.cache.eachLocal(function(key, value) { - if (option(container, key, 'instantiate') === false) { return; } - callback(value); - }); + Ember.config = Ember.config || {}; + + // We disable the RANGE API by default for performance reasons + if ('undefined' === typeof Ember.ENV.DISABLE_RANGE_API) { + Ember.ENV.DISABLE_RANGE_API = true; } - function resetCache(container) { - container.cache.eachLocal(function(key, value) { - if (option(container, key, 'instantiate') === false) { return; } - value.destroy(); - }); - container.cache.dict = {}; + if ("undefined" === typeof MetamorphENV) { + exports.MetamorphENV = {}; } - function addTypeInjection(rules, type, property, fullName) { - var injections = rules.get(type); + MetamorphENV.DISABLE_RANGE_API = Ember.ENV.DISABLE_RANGE_API; - if (!injections) { - injections = []; - rules.set(type, injections); - } + /** + Hash of enabled Canary features. Add to this before creating your application. - injections.push({ - property: property, - fullName: fullName - }); - } - - var VALID_FULL_NAME_REGEXP = /^[^:]+.+:[^:]+$/; - function validateFullName(fullName) { - if (!VALID_FULL_NAME_REGEXP.test(fullName)) { - throw new TypeError('Invalid Fullname, expected: `type:name` got: ' + fullName); - } - } - - function addInjection(rules, factoryName, property, injectionName) { - var injections = rules[factoryName] = rules[factoryName] || []; - injections.push({ property: property, fullName: injectionName }); - } - - __exports__["default"] = Container; - }); -define("container/inheriting_dict", - ["exports"], - function(__exports__) { - "use strict"; - // A safe and simple inheriting object. - function InheritingDict(parent) { - this.parent = parent; - this.dict = {}; - } - - InheritingDict.prototype = { + You can also define `ENV.FEATURES` if you need to enable features flagged at runtime. - /** - @property parent - @type InheritingDict - @default null - */ + @class FEATURES + @namespace Ember + @static + @since 1.1.0 + */ - parent: null, + Ember.FEATURES = Ember.ENV.FEATURES || {}; - /** - Object used to store the current nodes data. + /** + Test that a feature is enabled. Parsed by Ember's build tools to leave + experimental features out of beta/stable builds. - @property dict - @type Object - @default Object - */ - dict: null, + You can define the following configuration options: - /** - Retrieve the value given a key, if the value is present at the current - level use it, otherwise walk up the parent hierarchy and try again. If - no matching key is found, return undefined. + * `ENV.ENABLE_ALL_FEATURES` - force all features to be enabled. + * `ENV.ENABLE_OPTIONAL_FEATURES` - enable any features that have not been explicitly + enabled/disabled. - @method get - @param {String} key - @return {any} - */ - get: function(key) { - var dict = this.dict; + @method isEnabled + @param {String} feature + @return {Boolean} + @for Ember.FEATURES + @since 1.1.0 + */ - if (dict.hasOwnProperty(key)) { - return dict[key]; - } + Ember.FEATURES.isEnabled = function(feature) { + var featureValue = Ember.FEATURES[feature]; - if (this.parent) { - return this.parent.get(key); - } - }, + if (Ember.ENV.ENABLE_ALL_FEATURES) { + return true; + } else if (featureValue === true || featureValue === false || featureValue === undefined) { + return featureValue; + } else if (Ember.ENV.ENABLE_OPTIONAL_FEATURES) { + return true; + } else { + return false; + } + }; - /** - Set the given value for the given key, at the current level. + // .......................................................... + // BOOTSTRAP + // - @method set - @param {String} key - @param {Any} value - */ - set: function(key, value) { - this.dict[key] = value; - }, + /** + Determines whether Ember should enhance some built-in object prototypes to + provide a more friendly API. If enabled, a few methods will be added to + `Function`, `String`, and `Array`. `Object.prototype` will not be enhanced, + which is the one that causes most trouble for people. - /** - Delete the given key + In general we recommend leaving this option set to true since it rarely + conflicts with other code. If you need to turn it off however, you can + define an `ENV.EXTEND_PROTOTYPES` config to disable it. - @method remove - @param {String} key - */ - remove: function(key) { - delete this.dict[key]; - }, + @property EXTEND_PROTOTYPES + @type Boolean + @default true + @for Ember + */ + Ember.EXTEND_PROTOTYPES = Ember.ENV.EXTEND_PROTOTYPES; - /** - Check for the existence of given a key, if the key is present at the current - level return true, otherwise walk up the parent hierarchy and try again. If - no matching key is found, return false. + if (typeof Ember.EXTEND_PROTOTYPES === 'undefined') { + Ember.EXTEND_PROTOTYPES = true; + } - @method has - @param {String} key - @return {Boolean} - */ - has: function(key) { - var dict = this.dict; + /** + Determines whether Ember logs a full stack trace during deprecation warnings - if (dict.hasOwnProperty(key)) { - return true; - } + @property LOG_STACKTRACE_ON_DEPRECATION + @type Boolean + @default true + */ + Ember.LOG_STACKTRACE_ON_DEPRECATION = (Ember.ENV.LOG_STACKTRACE_ON_DEPRECATION !== false); - if (this.parent) { - return this.parent.has(key); - } + /** + Determines whether Ember should add ECMAScript 5 Array shims to older browsers. - return false; - }, + @property SHIM_ES5 + @type Boolean + @default Ember.EXTEND_PROTOTYPES + */ + Ember.SHIM_ES5 = (Ember.ENV.SHIM_ES5 === false) ? false : Ember.EXTEND_PROTOTYPES; - /** - Iterate and invoke a callback for each local key-value pair. + /** + Determines whether Ember logs info about version of used libraries - @method eachLocal - @param {Function} callback - @param {Object} binding - */ - eachLocal: function(callback, binding) { - var dict = this.dict; + @property LOG_VERSION + @type Boolean + @default true + */ + Ember.LOG_VERSION = (Ember.ENV.LOG_VERSION === false) ? false : true; - for (var prop in dict) { - if (dict.hasOwnProperty(prop)) { - callback.call(binding, prop, dict[prop]); - } - } - } - }; + /** + Empty function. Useful for some operations. Always returns `this`. - __exports__["default"] = InheritingDict; - }); -define("container", - ["container/container","exports"], - function(__dependency1__, __exports__) { - "use strict"; - /* - Public api for the container is still in flux. - The public api, specified on the application namespace should be considered the stable api. - // @module container + @method K @private + @return {Object} */ + function K() { return this; } + __exports__.K = K; + Ember.K = K; + //TODO: ES6 GLOBAL TODO - /* - Flag to enable/disable model factory injections (disabled by default) - If model factory injections are enabled, models should not be - accessed globally (only through `container.lookupFactory('model:modelName'))`); - */ - Ember.MODEL_FACTORY_INJECTIONS = false; + // Stub out the methods defined by the ember-debug package in case it's not loaded - if (Ember.ENV && typeof Ember.ENV.MODEL_FACTORY_INJECTIONS !== 'undefined') { - Ember.MODEL_FACTORY_INJECTIONS = !!Ember.ENV.MODEL_FACTORY_INJECTIONS; + if ('undefined' === typeof Ember.assert) { Ember.assert = K; } + if ('undefined' === typeof Ember.warn) { Ember.warn = K; } + if ('undefined' === typeof Ember.debug) { Ember.debug = K; } + if ('undefined' === typeof Ember.runInDebug) { Ember.runInDebug = K; } + if ('undefined' === typeof Ember.deprecate) { Ember.deprecate = K; } + if ('undefined' === typeof Ember.deprecateFunc) { + Ember.deprecateFunc = function(_, func) { return func; }; } - - var Container = __dependency1__["default"]; - - __exports__["default"] = Container; + __exports__["default"] = Ember; }); -})(); - -(function() { -define("ember-runtime/compare", - ["ember-metal/core","ember-metal/utils","ember-runtime/mixins/comparable","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // for Ember.ORDER_DEFINITION - var typeOf = __dependency2__.typeOf; - var Comparable = __dependency3__["default"]; +enifed("ember-metal/dependent_keys", + ["ember-metal/platform","ember-metal/watching","exports"], + function(__dependency1__, __dependency2__, __exports__) { + // Remove "use strict"; from transpiled module until + // https://bugs.webkit.org/show_bug.cgi?id=138038 is fixed + // + // REMOVE_USE_STRICT: true - // Used by Ember.compare - Ember.ORDER_DEFINITION = Ember.ENV.ORDER_DEFINITION || [ - 'undefined', - 'null', - 'boolean', - 'number', - 'string', - 'array', - 'object', - 'instance', - 'function', - 'class', - 'date' - ]; + var o_create = __dependency1__.create; + var watch = __dependency2__.watch; + var unwatch = __dependency2__.unwatch; /** - This will compare two javascript values of possibly different types. - It will tell you which one is greater than the other by returning: - - - -1 if the first is smaller than the second, - - 0 if both are equal, - - 1 if the first is greater than the second. + @module ember-metal + */ - The order is calculated based on `Ember.ORDER_DEFINITION`, if types are different. - In case they have the same type an appropriate comparison for this type is made. + // .......................................................... + // DEPENDENT KEYS + // - ```javascript - Ember.compare('hello', 'hello'); // 0 - Ember.compare('abc', 'dfg'); // -1 - Ember.compare(2, 1); // 1 - ``` + // data structure: + // meta.deps = { + // 'depKey': { + // 'keyName': count, + // } + // } - @method compare - @for Ember - @param {Object} v First value to compare - @param {Object} w Second value to compare - @return {Number} -1 if v < w, 0 if v = w and 1 if v > w. + /* + This function returns a map of unique dependencies for a + given object and key. */ - function compare(v, w) { - if (v === w) { return 0; } - - var type1 = typeOf(v); - var type2 = typeOf(w); + function keysForDep(depsMeta, depKey) { + var keys = depsMeta[depKey]; + if (!keys) { + // if there are no dependencies yet for a the given key + // create a new empty list of dependencies for the key + keys = depsMeta[depKey] = {}; + } else if (!depsMeta.hasOwnProperty(depKey)) { + // otherwise if the dependency list is inherited from + // a superclass, clone the hash + keys = depsMeta[depKey] = o_create(keys); + } + return keys; + } - if (Comparable) { - if (type1==='instance' && Comparable.detect(v.constructor)) { - return v.constructor.compare(v, w); - } + function metaForDeps(meta) { + return keysForDep(meta, 'deps'); + } - if (type2 === 'instance' && Comparable.detect(w.constructor)) { - return 1-w.constructor.compare(w, v); - } - } + function addDependentKeys(desc, obj, keyName, meta) { + // the descriptor has a list of dependent keys, so + // add all of its dependent keys. + var depKeys = desc._dependentKeys, depsMeta, idx, len, depKey, keys; + if (!depKeys) return; - // If we haven't yet generated a reverse-mapping of Ember.ORDER_DEFINITION, - // do so now. - var mapping = Ember.ORDER_DEFINITION_MAPPING; - if (!mapping) { - var order = Ember.ORDER_DEFINITION; - mapping = Ember.ORDER_DEFINITION_MAPPING = {}; - var idx, len; - for (idx = 0, len = order.length; idx < len; ++idx) { - mapping[order[idx]] = idx; - } + depsMeta = metaForDeps(meta); - // We no longer need Ember.ORDER_DEFINITION. - delete Ember.ORDER_DEFINITION; + for(idx = 0, len = depKeys.length; idx < len; idx++) { + depKey = depKeys[idx]; + // Lookup keys meta for depKey + keys = keysForDep(depsMeta, depKey); + // Increment the number of times depKey depends on keyName. + keys[keyName] = (keys[keyName] || 0) + 1; + // Watch the depKey + watch(obj, depKey, meta); } + } - var type1Index = mapping[type1]; - var type2Index = mapping[type2]; + __exports__.addDependentKeys = addDependentKeys;function removeDependentKeys(desc, obj, keyName, meta) { + // the descriptor has a list of dependent keys, so + // remove all of its dependent keys. + var depKeys = desc._dependentKeys, depsMeta, idx, len, depKey, keys; + if (!depKeys) return; - if (type1Index < type2Index) { return -1; } - if (type1Index > type2Index) { return 1; } + depsMeta = metaForDeps(meta); - // types are equal - so we have to check values now - switch (type1) { - case 'boolean': - case 'number': - if (v < w) { return -1; } - if (v > w) { return 1; } - return 0; + for(idx = 0, len = depKeys.length; idx < len; idx++) { + depKey = depKeys[idx]; + // Lookup keys meta for depKey + keys = keysForDep(depsMeta, depKey); + // Decrement the number of times depKey depends on keyName. + keys[keyName] = (keys[keyName] || 0) - 1; + // Unwatch the depKey + unwatch(obj, depKey, meta); + } + } - case 'string': - var comp = v.localeCompare(w); - if (comp < 0) { return -1; } - if (comp > 0) { return 1; } - return 0; + __exports__.removeDependentKeys = removeDependentKeys; + }); +enifed("ember-metal/deprecate_property", + ["ember-metal/core","ember-metal/platform","ember-metal/properties","ember-metal/property_get","ember-metal/property_set","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; + /** + @module ember-metal + */ - case 'array': - var vLen = v.length; - var wLen = w.length; - var l = Math.min(vLen, wLen); - var r = 0; - var i = 0; - while (r === 0 && i < l) { - r = compare(v[i],w[i]); - i++; - } - if (r !== 0) { return r; } + var Ember = __dependency1__["default"]; + var hasPropertyAccessors = __dependency2__.hasPropertyAccessors; + var defineProperty = __dependency3__.defineProperty; + var get = __dependency4__.get; + var set = __dependency5__.set; - // all elements are equal now - // shorter array should be ordered first - if (vLen < wLen) { return -1; } - if (vLen > wLen) { return 1; } - // arrays are equal now - return 0; - case 'instance': - if (Comparable && Comparable.detect(v)) { - return v.compare(v, w); - } - return 0; + /** + Used internally to allow changing properties in a backwards compatible way, and print a helpful + deprecation warning. - case 'date': - var vNum = v.getTime(); - var wNum = w.getTime(); - if (vNum < wNum) { return -1; } - if (vNum > wNum) { return 1; } - return 0; + @method deprecateProperty + @param {Object} object The object to add the deprecated property to. + @param {String} deprecatedKey The property to add (and print deprecation warnings upon accessing). + @param {String} newKey The property that will be aliased. + @private + @since 1.7.0 + */ - default: - return 0; + function deprecateProperty(object, deprecatedKey, newKey) { + function deprecate() { + Ember.deprecate('Usage of `' + deprecatedKey + '` is deprecated, use `' + newKey + '` instead.'); } - }; - __exports__["default"] = compare; + if (hasPropertyAccessors) { + defineProperty(object, deprecatedKey, { + configurable: true, + enumerable: false, + set: function(value) { deprecate(); set(this, newKey, value); }, + get: function() { deprecate(); return get(this, newKey); } + }); + } + } + + __exports__.deprecateProperty = deprecateProperty; }); -define("ember-runtime/computed/array_computed", - ["ember-metal/core","ember-runtime/computed/reduce_computed","ember-metal/enumerable_utils","ember-metal/platform","ember-metal/observer","ember-metal/error","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { +enifed("ember-metal/dictionary", + ["ember-metal/platform","exports"], + function(__dependency1__, __exports__) { "use strict"; - var Ember = __dependency1__["default"]; - var reduceComputed = __dependency2__.reduceComputed; - var ReduceComputedProperty = __dependency2__.ReduceComputedProperty; - var EnumerableUtils = __dependency3__["default"]; - var create = __dependency4__.create; - var addObserver = __dependency5__.addObserver; - var EmberError = __dependency6__["default"]; + var create = __dependency1__.create; - var a_slice = [].slice, - o_create = create, - forEach = EnumerableUtils.forEach; + // the delete is meant to hint at runtimes that this object should remain in + // dictionary mode. This is clearly a runtime specific hack, but currently it + // appears worthwile in some usecases. Please note, these deletes do increase + // the cost of creation dramatically over a plain Object.create. And as this + // only makes sense for long-lived dictionaries that aren't instantiated often. + __exports__["default"] = function makeDictionary(parent) { + var dict = create(parent); + dict['_dict'] = null; + delete dict['_dict']; + return dict; + } + }); +enifed("ember-metal/enumerable_utils", + ["ember-metal/array","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var _filter = __dependency1__.filter; + var a_forEach = __dependency1__.forEach; + var _indexOf = __dependency1__.indexOf; + var _map = __dependency1__.map; - function ArrayComputedProperty() { - var cp = this; + var splice = Array.prototype.splice; - ReduceComputedProperty.apply(this, arguments); + /** + * Defines some convenience methods for working with Enumerables. + * `Ember.EnumerableUtils` uses `Ember.ArrayPolyfills` when necessary. + * + * @class EnumerableUtils + * @namespace Ember + * @static + * */ - this.func = (function(reduceFunc) { - return function (propertyName) { - if (!cp._hasInstanceMeta(this, propertyName)) { - // When we recompute an array computed property, we need already - // retrieved arrays to be updated; we can't simply empty the cache and - // hope the array is re-retrieved. - forEach(cp._dependentKeys, function(dependentKey) { - addObserver(this, dependentKey, function() { - cp.recomputeOnce.call(this, propertyName); - }); - }, this); - } + /** + * Calls the map function on the passed object with a specified callback. This + * uses `Ember.ArrayPolyfill`'s-map method when necessary. + * + * @method map + * @param {Object} obj The object that should be mapped + * @param {Function} callback The callback to execute + * @param {Object} thisArg Value to use as this when executing *callback* + * + * @return {Array} An array of mapped values. + */ + function map(obj, callback, thisArg) { + return obj.map ? obj.map(callback, thisArg) : _map.call(obj, callback, thisArg); + } - return reduceFunc.apply(this, arguments); - }; - })(this.func); + __exports__.map = map;/** + * Calls the forEach function on the passed object with a specified callback. This + * uses `Ember.ArrayPolyfill`'s-forEach method when necessary. + * + * @method forEach + * @param {Object} obj The object to call forEach on + * @param {Function} callback The callback to execute + * @param {Object} thisArg Value to use as this when executing *callback* + * + */ + function forEach(obj, callback, thisArg) { + return obj.forEach ? obj.forEach(callback, thisArg) : a_forEach.call(obj, callback, thisArg); + } - return this; + __exports__.forEach = forEach;/** + * Calls the filter function on the passed object with a specified callback. This + * uses `Ember.ArrayPolyfill`'s-filter method when necessary. + * + * @method filter + * @param {Object} obj The object to call filter on + * @param {Function} callback The callback to execute + * @param {Object} thisArg Value to use as this when executing *callback* + * + * @return {Array} An array containing the filtered values + * @since 1.4.0 + */ + function filter(obj, callback, thisArg) { + return obj.filter ? obj.filter(callback, thisArg) : _filter.call(obj, callback, thisArg); } - ArrayComputedProperty.prototype = o_create(ReduceComputedProperty.prototype); - ArrayComputedProperty.prototype.initialValue = function () { - return Ember.A(); - }; - ArrayComputedProperty.prototype.resetValue = function (array) { - array.clear(); - return array; - }; + __exports__.filter = filter;/** + * Calls the indexOf function on the passed object with a specified callback. This + * uses `Ember.ArrayPolyfill`'s-indexOf method when necessary. + * + * @method indexOf + * @param {Object} obj The object to call indexOn on + * @param {Function} callback The callback to execute + * @param {Object} index The index to start searching from + * + */ + function indexOf(obj, element, index) { + return obj.indexOf ? obj.indexOf(element, index) : _indexOf.call(obj, element, index); + } - // This is a stopgap to keep the reference counts correct with lazy CPs. - ArrayComputedProperty.prototype.didChange = function (obj, keyName) { - return; - }; + __exports__.indexOf = indexOf;/** + * Returns an array of indexes of the first occurrences of the passed elements + * on the passed object. + * + * ```javascript + * var array = [1, 2, 3, 4, 5]; + * Ember.EnumerableUtils.indexesOf(array, [2, 5]); // [1, 4] + * + * var fubar = "Fubarr"; + * Ember.EnumerableUtils.indexesOf(fubar, ['b', 'r']); // [2, 4] + * ``` + * + * @method indexesOf + * @param {Object} obj The object to check for element indexes + * @param {Array} elements The elements to search for on *obj* + * + * @return {Array} An array of indexes. + * + */ + function indexesOf(obj, elements) { + return elements === undefined ? [] : map(elements, function(item) { + return indexOf(obj, item); + }); + } - /** - Creates a computed property which operates on dependent arrays and - is updated with "one at a time" semantics. When items are added or - removed from the dependent array(s) an array computed only operates - on the change instead of re-evaluating the entire array. This should - return an array, if you'd like to use "one at a time" semantics and - compute some value other then an array look at - `Ember.reduceComputed`. + __exports__.indexesOf = indexesOf;/** + * Adds an object to an array. If the array already includes the object this + * method has no effect. + * + * @method addObject + * @param {Array} array The array the passed item should be added to + * @param {Object} item The item to add to the passed array + * + * @return 'undefined' + */ + function addObject(array, item) { + var index = indexOf(array, item); + if (index === -1) { array.push(item); } + } - If there are more than one arguments the first arguments are - considered to be dependent property keys. The last argument is - required to be an options object. The options object can have the - following three properties. + __exports__.addObject = addObject;/** + * Removes an object from an array. If the array does not contain the passed + * object this method has no effect. + * + * @method removeObject + * @param {Array} array The array to remove the item from. + * @param {Object} item The item to remove from the passed array. + * + * @return 'undefined' + */ + function removeObject(array, item) { + var index = indexOf(array, item); + if (index !== -1) { array.splice(index, 1); } + } - `initialize` - An optional initialize function. Typically this will be used - to set up state on the instanceMeta object. + __exports__.removeObject = removeObject;function _replace(array, idx, amt, objects) { + var args = [].concat(objects); + var ret = []; + // https://code.google.com/p/chromium/issues/detail?id=56588 + var size = 60000; + var start = idx; + var ends = amt; + var count, chunk; - `removedItem` - A function that is called each time an element is - removed from the array. + while (args.length) { + count = ends > size ? size : ends; + if (count <= 0) { count = 0; } - `addedItem` - A function that is called each time an element is - added to the array. + chunk = args.splice(0, size); + chunk = [start, count].concat(chunk); + start += size; + ends -= count; - The `initialize` function has the following signature: + ret = ret.concat(splice.apply(array, chunk)); + } + return ret; + } - ```javascript - function(array, changeMeta, instanceMeta) - ``` + __exports__._replace = _replace;/** + * Replaces objects in an array with the passed objects. + * + * ```javascript + * var array = [1,2,3]; + * Ember.EnumerableUtils.replace(array, 1, 2, [4, 5]); // [1, 4, 5] + * + * var array = [1,2,3]; + * Ember.EnumerableUtils.replace(array, 1, 1, [4, 5]); // [1, 4, 5, 3] + * + * var array = [1,2,3]; + * Ember.EnumerableUtils.replace(array, 10, 1, [4, 5]); // [1, 2, 3, 4, 5] + * ``` + * + * @method replace + * @param {Array} array The array the objects should be inserted into. + * @param {Number} idx Starting index in the array to replace. If *idx* >= + * length, then append to the end of the array. + * @param {Number} amt Number of elements that should be removed from the array, + * starting at *idx* + * @param {Array} objects An array of zero or more objects that should be + * inserted into the array at *idx* + * + * @return {Array} The modified array. + */ + function replace(array, idx, amt, objects) { + if (array.replace) { + return array.replace(idx, amt, objects); + } else { + return _replace(array, idx, amt, objects); + } + } - `array` - The initial value of the arrayComputed, an empty array. + __exports__.replace = replace;/** + * Calculates the intersection of two arrays. This method returns a new array + * filled with the records that the two passed arrays share with each other. + * If there is no intersection, an empty array will be returned. + * + * ```javascript + * var array1 = [1, 2, 3, 4, 5]; + * var array2 = [1, 3, 5, 6, 7]; + * + * Ember.EnumerableUtils.intersection(array1, array2); // [1, 3, 5] + * + * var array1 = [1, 2, 3]; + * var array2 = [4, 5, 6]; + * + * Ember.EnumerableUtils.intersection(array1, array2); // [] + * ``` + * + * @method intersection + * @param {Array} array1 The first array + * @param {Array} array2 The second array + * + * @return {Array} The intersection of the two passed arrays. + */ + function intersection(array1, array2) { + var result = []; + forEach(array1, function(element) { + if (indexOf(array2, element) >= 0) { + result.push(element); + } + }); - `changeMeta` - An object which contains meta information about the - computed. It contains the following properties: + return result; + } - - `property` the computed property - - `propertyName` the name of the property on the object + __exports__.intersection = intersection;// TODO: this only exists to maintain the existing api, as we move forward it + // should only be part of the "global build" via some shim + __exports__["default"] = { + _replace: _replace, + addObject: addObject, + filter: filter, + forEach: forEach, + indexOf: indexOf, + indexesOf: indexesOf, + intersection: intersection, + map: map, + removeObject: removeObject, + replace: replace + }; + }); +enifed("ember-metal/error", + ["ember-metal/platform","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var create = __dependency1__.create; - `instanceMeta` - An object that can be used to store meta - information needed for calculating your computed. For example a - unique computed might use this to store the number of times a given - element is found in the dependent array. + var errorProps = [ + 'description', + 'fileName', + 'lineNumber', + 'message', + 'name', + 'number', + 'stack' + ]; + /** + A subclass of the JavaScript Error object for use in Ember. - The `removedItem` and `addedItem` functions both have the following signature: + @class Error + @namespace Ember + @extends Error + @constructor + */ + function EmberError() { + var tmp = Error.apply(this, arguments); - ```javascript - function(accumulatedValue, item, changeMeta, instanceMeta) - ``` + // Adds a `stack` property to the given error object that will yield the + // stack trace at the time captureStackTrace was called. + // When collecting the stack trace all frames above the topmost call + // to this function, including that call, will be left out of the + // stack trace. + // This is useful because we can hide Ember implementation details + // that are not very helpful for the user. + if (Error.captureStackTrace) { + Error.captureStackTrace(this, Ember.Error); + } + // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. + for (var idx = 0; idx < errorProps.length; idx++) { + this[errorProps[idx]] = tmp[errorProps[idx]]; + } + } - `accumulatedValue` - The value returned from the last time - `removedItem` or `addedItem` was called or an empty array. + EmberError.prototype = create(Error.prototype); - `item` - the element added or removed from the array + __exports__["default"] = EmberError; + }); +enifed("ember-metal/events", + ["ember-metal/core","ember-metal/utils","ember-metal/platform","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + // Remove "use strict"; from transpiled module until + // https://bugs.webkit.org/show_bug.cgi?id=138038 is fixed + // + // REMOVE_USE_STRICT: true - `changeMeta` - An object which contains meta information about the - change. It contains the following properties: + /** + @module ember-metal + */ + var Ember = __dependency1__["default"]; + var metaFor = __dependency2__.meta; + var tryFinally = __dependency2__.tryFinally; + var apply = __dependency2__.apply; + var applyStr = __dependency2__.applyStr; + var create = __dependency3__.create; - - `property` the computed property - - `propertyName` the name of the property on the object - - `index` the index of the added or removed item - - `item` the added or removed item: this is exactly the same as - the second arg - - `arrayChanged` the array that triggered the change. Can be - useful when depending on multiple arrays. + var a_slice = [].slice; - For property changes triggered on an item property change (when - depKey is something like `someArray.@each.someProperty`), - `changeMeta` will also contain the following property: + /* listener flags */ + var ONCE = 1; + var SUSPENDED = 2; - - `previousValues` an object whose keys are the properties that changed on - the item, and whose values are the item's previous values. - `previousValues` is important Ember coalesces item property changes via - Ember.run.once. This means that by the time removedItem gets called, item has - the new values, but you may need the previous value (eg for sorting & - filtering). - - `instanceMeta` - An object that can be used to store meta - information needed for calculating your computed. For example a - unique computed might use this to store the number of times a given - element is found in the dependent array. - - The `removedItem` and `addedItem` functions should return the accumulated - value. It is acceptable to not return anything (ie return undefined) - to invalidate the computation. This is generally not a good idea for - arrayComputed but it's used in eg max and min. + /* + The event system uses a series of nested hashes to store listeners on an + object. When a listener is registered, or when an event arrives, these + hashes are consulted to determine which target and action pair to invoke. - Example + The hashes are stored in the object's meta hash, and look like this: - ```javascript - Ember.computed.map = function(dependentKey, callback) { - var options = { - addedItem: function(array, item, changeMeta, instanceMeta) { - var mapped = callback(item); - array.insertAt(changeMeta.index, mapped); - return array; - }, - removedItem: function(array, item, changeMeta, instanceMeta) { - array.removeAt(changeMeta.index, 1); - return array; + // Object's meta hash + { + listeners: { // variable name: `listenerSet` + "foo:changed": [ // variable name: `actions` + target, method, flags + ] + } } - }; - - return Ember.arrayComputed(dependentKey, options); - }; - ``` - @method arrayComputed - @for Ember - @param {String} [dependentKeys*] - @param {Object} options - @return {Ember.ComputedProperty} */ - function arrayComputed (options) { - var args; - if (arguments.length > 1) { - args = a_slice.call(arguments, 0, -1); - options = a_slice.call(arguments, -1)[0]; + function indexOf(array, target, method) { + var index = -1; + // hashes are added to the end of the event array + // so it makes sense to start searching at the end + // of the array and search in reverse + for (var i = array.length - 3 ; i >=0; i -= 3) { + if (target === array[i] && method === array[i + 1]) { + index = i; break; + } } + return index; + } + + function actionsFor(obj, eventName) { + var meta = metaFor(obj, true); + var actions; + var listeners = meta.listeners; - if (typeof options !== "object") { - throw new EmberError("Array Computed Property declared without an options hash"); + if (!listeners) { + listeners = meta.listeners = create(null); + listeners.__source__ = obj; + } else if (listeners.__source__ !== obj) { + // setup inherited copy of the listeners object + listeners = meta.listeners = create(listeners); + listeners.__source__ = obj; } - var cp = new ArrayComputedProperty(options); + actions = listeners[eventName]; - if (args) { - cp.property.apply(cp, args); + // if there are actions, but the eventName doesn't exist in our listeners, then copy them from the prototype + if (actions && actions.__source__ !== obj) { + actions = listeners[eventName] = listeners[eventName].slice(); + actions.__source__ = obj; + } else if (!actions) { + actions = listeners[eventName] = []; + actions.__source__ = obj; } - return cp; - }; + return actions; + } - __exports__.arrayComputed = arrayComputed; - __exports__.ArrayComputedProperty = ArrayComputedProperty; - }); -define("ember-runtime/computed/reduce_computed", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/error","ember-metal/property_events","ember-metal/expand_properties","ember-metal/observer","ember-metal/computed","ember-metal/platform","ember-metal/enumerable_utils","ember-runtime/system/tracked_array","ember-runtime/mixins/array","ember-metal/run_loop","ember-runtime/system/set","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 - var e_get = __dependency2__.get; - var set = __dependency3__.set; - var guidFor = __dependency4__.guidFor; - var metaFor = __dependency4__.meta; - var EmberError = __dependency5__["default"]; - var propertyWillChange = __dependency6__.propertyWillChange; - var propertyDidChange = __dependency6__.propertyDidChange; - var expandProperties = __dependency7__["default"]; - var addObserver = __dependency8__.addObserver; - var observersFor = __dependency8__.observersFor; - var removeObserver = __dependency8__.removeObserver; - var addBeforeObserver = __dependency8__.addBeforeObserver; - var removeBeforeObserver = __dependency8__.removeBeforeObserver; - var ComputedProperty = __dependency9__.ComputedProperty; - var cacheFor = __dependency9__.cacheFor; - var create = __dependency10__.create; - var EnumerableUtils = __dependency11__["default"]; - var TrackedArray = __dependency12__["default"]; - var EmberArray = __dependency13__["default"]; - var run = __dependency14__["default"]; - var Set = __dependency15__["default"]; - var isArray = __dependency4__.isArray; + function accumulateListeners(obj, eventName, otherActions) { + var meta = obj['__ember_meta__']; + var actions = meta && meta.listeners && meta.listeners[eventName]; - var cacheSet = cacheFor.set, - cacheGet = cacheFor.get, - cacheRemove = cacheFor.remove, - a_slice = [].slice, - o_create = create, - forEach = EnumerableUtils.forEach, - // Here we explicitly don't allow `@each.foo`; it would require some special - // testing, but there's no particular reason why it should be disallowed. - eachPropertyPattern = /^(.*)\.@each\.(.*)/, - doubleEachPropertyPattern = /(.*\.@each){2,}/, - arrayBracketPattern = /\.\[\]$/; + if (!actions) { return; } - function get(obj, key) { - if (key === '@this') { - return obj; + var newActions = []; + + for (var i = actions.length - 3; i >= 0; i -= 3) { + var target = actions[i]; + var method = actions[i+1]; + var flags = actions[i+2]; + var actionIndex = indexOf(otherActions, target, method); + + if (actionIndex === -1) { + otherActions.push(target, method, flags); + newActions.push(target, method, flags); + } } - return e_get(obj, key); + return newActions; } - /* - Tracks changes to dependent arrays, as well as to properties of items in - dependent arrays. + __exports__.accumulateListeners = accumulateListeners;/** + Add an event listener - @class DependentArraysObserver + @method addListener + @for Ember + @param obj + @param {String} eventName + @param {Object|Function} target A target object or a function + @param {Function|String} method A function or the name of a function to be called on `target` + @param {Boolean} once A flag whether a function should only be called once */ - function DependentArraysObserver(callbacks, cp, instanceMeta, context, propertyName, sugarMeta) { - // user specified callbacks for `addedItem` and `removedItem` - this.callbacks = callbacks; + function addListener(obj, eventName, target, method, once) { + Ember.assert("You must pass at least an object and event name to Ember.addListener", !!obj && !!eventName); - // the computed property: remember these are shared across instances - this.cp = cp; + if (!method && 'function' === typeof target) { + method = target; + target = null; + } - // the ReduceComputedPropertyInstanceMeta this DependentArraysObserver is - // associated with - this.instanceMeta = instanceMeta; + var actions = actionsFor(obj, eventName); + var actionIndex = indexOf(actions, target, method); + var flags = 0; - // A map of array guids to dependentKeys, for the given context. We track - // this because we want to set up the computed property potentially before the - // dependent array even exists, but when the array observer fires, we lack - // enough context to know what to update: we can recover that context by - // getting the dependentKey. - this.dependentKeysByGuid = {}; + if (once) flags |= ONCE; - // a map of dependent array guids -> TrackedArray instances. We use - // this to lazily recompute indexes for item property observers. - this.trackedArraysByGuid = {}; + if (actionIndex !== -1) { return; } - // We suspend observers to ignore replacements from `reset` when totally - // recomputing. Unfortunately we cannot properly suspend the observers - // because we only have the key; instead we make the observers no-ops - this.suspended = false; + actions.push(target, method, flags); - // This is used to coalesce item changes from property observers within a - // single item. - this.changedItems = {}; - // This is used to coalesce item changes for multiple items that depend on - // some shared state. - this.changedItemCount = 0; + if ('function' === typeof obj.didAddListener) { + obj.didAddListener(eventName, target, method); + } } - function ItemPropertyObserverContext (dependentArray, index, trackedArray) { - Ember.assert("Internal error: trackedArray is null or undefined", trackedArray); + __exports__.addListener = addListener;/** + Remove an event listener - this.dependentArray = dependentArray; - this.index = index; - this.item = dependentArray.objectAt(index); - this.trackedArray = trackedArray; - this.beforeObserver = null; - this.observer = null; + Arguments should match those passed to `Ember.addListener`. - this.destroyed = false; - } + @method removeListener + @for Ember + @param obj + @param {String} eventName + @param {Object|Function} target A target object or a function + @param {Function|String} method A function or the name of a function to be called on `target` + */ + function removeListener(obj, eventName, target, method) { + Ember.assert("You must pass at least an object and event name to Ember.removeListener", !!obj && !!eventName); - DependentArraysObserver.prototype = { - setValue: function (newValue) { - this.instanceMeta.setValue(newValue, true); - }, - getValue: function () { - return this.instanceMeta.getValue(); - }, + if (!method && 'function' === typeof target) { + method = target; + target = null; + } - setupObservers: function (dependentArray, dependentKey) { - this.dependentKeysByGuid[guidFor(dependentArray)] = dependentKey; + function _removeListener(target, method) { + var actions = actionsFor(obj, eventName); + var actionIndex = indexOf(actions, target, method); - dependentArray.addArrayObserver(this, { - willChange: 'dependentArrayWillChange', - didChange: 'dependentArrayDidChange' - }); + // action doesn't exist, give up silently + if (actionIndex === -1) { return; } - if (this.cp._itemPropertyKeys[dependentKey]) { - this.setupPropertyObservers(dependentKey, this.cp._itemPropertyKeys[dependentKey]); + actions.splice(actionIndex, 3); + + if ('function' === typeof obj.didRemoveListener) { + obj.didRemoveListener(eventName, target, method); } - }, + } - teardownObservers: function (dependentArray, dependentKey) { - var itemPropertyKeys = this.cp._itemPropertyKeys[dependentKey] || []; + if (method) { + _removeListener(target, method); + } else { + var meta = obj['__ember_meta__']; + var actions = meta && meta.listeners && meta.listeners[eventName]; - delete this.dependentKeysByGuid[guidFor(dependentArray)]; + if (!actions) { return; } + for (var i = actions.length - 3; i >= 0; i -= 3) { + _removeListener(actions[i], actions[i+1]); + } + } + } - this.teardownPropertyObservers(dependentKey, itemPropertyKeys); + /** + Suspend listener during callback. - dependentArray.removeArrayObserver(this, { - willChange: 'dependentArrayWillChange', - didChange: 'dependentArrayDidChange' - }); - }, + This should only be used by the target of the event listener + when it is taking an action that would cause the event, e.g. + an object might suspend its property change listener while it is + setting that property. - suspendArrayObservers: function (callback, binding) { - var oldSuspended = this.suspended; - this.suspended = true; - callback.call(binding); - this.suspended = oldSuspended; - }, + @method suspendListener + @for Ember - setupPropertyObservers: function (dependentKey, itemPropertyKeys) { - var dependentArray = get(this.instanceMeta.context, dependentKey), - length = get(dependentArray, 'length'), - observerContexts = new Array(length); + @private + @param obj + @param {String} eventName + @param {Object|Function} target A target object or a function + @param {Function|String} method A function or the name of a function to be called on `target` + @param {Function} callback + */ + function suspendListener(obj, eventName, target, method, callback) { + if (!method && 'function' === typeof target) { + method = target; + target = null; + } - this.resetTransformations(dependentKey, observerContexts); + var actions = actionsFor(obj, eventName); + var actionIndex = indexOf(actions, target, method); - forEach(dependentArray, function (item, index) { - var observerContext = this.createPropertyObserverContext(dependentArray, index, this.trackedArraysByGuid[dependentKey]); - observerContexts[index] = observerContext; + if (actionIndex !== -1) { + actions[actionIndex+2] |= SUSPENDED; // mark the action as suspended + } - forEach(itemPropertyKeys, function (propertyKey) { - addBeforeObserver(item, propertyKey, this, observerContext.beforeObserver); - addObserver(item, propertyKey, this, observerContext.observer); - }, this); - }, this); - }, + function tryable() { return callback.call(target); } + function finalizer() { if (actionIndex !== -1) { actions[actionIndex+2] &= ~SUSPENDED; } } - teardownPropertyObservers: function (dependentKey, itemPropertyKeys) { - var dependentArrayObserver = this, - trackedArray = this.trackedArraysByGuid[dependentKey], - beforeObserver, - observer, - item; + return tryFinally(tryable, finalizer); + } - if (!trackedArray) { return; } + __exports__.suspendListener = suspendListener;/** + Suspends multiple listeners during a callback. - trackedArray.apply(function (observerContexts, offset, operation) { - if (operation === TrackedArray.DELETE) { return; } + @method suspendListeners + @for Ember - forEach(observerContexts, function (observerContext) { - observerContext.destroyed = true; - beforeObserver = observerContext.beforeObserver; - observer = observerContext.observer; - item = observerContext.item; + @private + @param obj + @param {Array} eventNames Array of event names + @param {Object|Function} target A target object or a function + @param {Function|String} method A function or the name of a function to be called on `target` + @param {Function} callback + */ + function suspendListeners(obj, eventNames, target, method, callback) { + if (!method && 'function' === typeof target) { + method = target; + target = null; + } - forEach(itemPropertyKeys, function (propertyKey) { - removeBeforeObserver(item, propertyKey, dependentArrayObserver, beforeObserver); - removeObserver(item, propertyKey, dependentArrayObserver, observer); - }); - }); - }); - }, + var suspendedActions = []; + var actionsList = []; + var eventName, actions, i, l; - createPropertyObserverContext: function (dependentArray, index, trackedArray) { - var observerContext = new ItemPropertyObserverContext(dependentArray, index, trackedArray); + for (i=0, l=eventNames.length; i= 0; i -= 3) { // looping in reverse for once listeners + var target = actions[i], method = actions[i+1], flags = actions[i+2]; + if (!method) { continue; } + if (flags & SUSPENDED) { continue; } + if (flags & ONCE) { removeListener(obj, eventName, target, method); } + if (!target) { target = obj; } + if ('string' === typeof method) { + if (params) { + applyStr(target, method, params); + } else { + target[method](); + } + } else { + if (params) { + apply(target, method, params); + } else { + method.call(target); } + } + } + return true; + } - forEach(observerContexts, function (context, index) { - context.index = index + offset; - }); - }); - }, + __exports__.sendEvent = sendEvent;/** + @private + @method hasListeners + @for Ember + @param obj + @param {String} eventName + */ + function hasListeners(obj, eventName) { + var meta = obj['__ember_meta__']; + var actions = meta && meta.listeners && meta.listeners[eventName]; - dependentArrayWillChange: function (dependentArray, index, removedCount, addedCount) { - if (this.suspended) { return; } + return !!(actions && actions.length); + } - var removedItem = this.callbacks.removedItem, - changeMeta, - guid = guidFor(dependentArray), - dependentKey = this.dependentKeysByGuid[guid], - itemPropertyKeys = this.cp._itemPropertyKeys[dependentKey] || [], - length = get(dependentArray, 'length'), - normalizedIndex = normalizeIndex(index, length, 0), - normalizedRemoveCount = normalizeRemoveCount(normalizedIndex, length, removedCount), - item, - itemIndex, - sliceIndex, - observerContexts; + __exports__.hasListeners = hasListeners;/** + @private + @method listenersFor + @for Ember + @param obj + @param {String} eventName + */ + function listenersFor(obj, eventName) { + var ret = []; + var meta = obj['__ember_meta__']; + var actions = meta && meta.listeners && meta.listeners[eventName]; - observerContexts = this.trackRemove(dependentKey, normalizedIndex, normalizedRemoveCount); + if (!actions) { return ret; } - function removeObservers(propertyKey) { - observerContexts[sliceIndex].destroyed = true; - removeBeforeObserver(item, propertyKey, this, observerContexts[sliceIndex].beforeObserver); - removeObserver(item, propertyKey, this, observerContexts[sliceIndex].observer); - } + for (var i = 0, l = actions.length; i < l; i += 3) { + var target = actions[i]; + var method = actions[i+1]; + ret.push([target, method]); + } - for (sliceIndex = normalizedRemoveCount - 1; sliceIndex >= 0; --sliceIndex) { - itemIndex = normalizedIndex + sliceIndex; - if (itemIndex >= length) { break; } + return ret; + } - item = dependentArray.objectAt(itemIndex); + __exports__.listenersFor = listenersFor;/** + Define a property as a function that should be executed when + a specified event or events are triggered. - forEach(itemPropertyKeys, removeObservers, this); - changeMeta = createChangeMeta(dependentArray, item, itemIndex, this.instanceMeta.propertyName, this.cp); - this.setValue( removedItem.call( - this.instanceMeta.context, this.getValue(), item, changeMeta, this.instanceMeta.sugarMeta)); - } - }, + ``` javascript + var Job = Ember.Object.extend({ + logCompleted: Ember.on('completed', function() { + console.log('Job completed!'); + }) + }); - dependentArrayDidChange: function (dependentArray, index, removedCount, addedCount) { - if (this.suspended) { return; } + var job = Job.create(); - var addedItem = this.callbacks.addedItem, - guid = guidFor(dependentArray), - dependentKey = this.dependentKeysByGuid[guid], - observerContexts = new Array(addedCount), - itemPropertyKeys = this.cp._itemPropertyKeys[dependentKey], - length = get(dependentArray, 'length'), - normalizedIndex = normalizeIndex(index, length, addedCount), - changeMeta, - observerContext; - - forEach(dependentArray.slice(normalizedIndex, normalizedIndex + addedCount), function (item, sliceIndex) { - if (itemPropertyKeys) { - observerContext = - observerContexts[sliceIndex] = - this.createPropertyObserverContext(dependentArray, normalizedIndex + sliceIndex, this.trackedArraysByGuid[dependentKey]); - forEach(itemPropertyKeys, function (propertyKey) { - addBeforeObserver(item, propertyKey, this, observerContext.beforeObserver); - addObserver(item, propertyKey, this, observerContext.observer); - }, this); - } + Ember.sendEvent(job, 'completed'); // Logs 'Job completed!' + ``` - changeMeta = createChangeMeta(dependentArray, item, normalizedIndex + sliceIndex, this.instanceMeta.propertyName, this.cp); - this.setValue( addedItem.call( - this.instanceMeta.context, this.getValue(), item, changeMeta, this.instanceMeta.sugarMeta)); - }, this); + @method on + @for Ember + @param {String} eventNames* + @param {Function} func + @return func + */ + function on(){ + var func = a_slice.call(arguments, -1)[0]; + var events = a_slice.call(arguments, 0, -1); + func.__ember_listens__ = events; + return func; + } - this.trackAdd(dependentKey, normalizedIndex, observerContexts); - }, + __exports__.on = on;__exports__.removeListener = removeListener; + }); +enifed("ember-metal/expand_properties", + ["ember-metal/core","ember-metal/error","ember-metal/enumerable_utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + var EmberError = __dependency2__["default"]; + var forEach = __dependency3__.forEach; - itemPropertyWillChange: function (obj, keyName, array, observerContext) { - var guid = guidFor(obj); + /** + @module ember-metal + */ - if (!this.changedItems[guid]) { - this.changedItems[guid] = { - array: array, - observerContext: observerContext, - obj: obj, - previousValues: {} - }; - } - ++this.changedItemCount; + var BRACE_EXPANSION = /^((?:[^\.]*\.)*)\{(.*)\}$/; + var SPLIT_REGEX = /\{|\}/; - this.changedItems[guid].previousValues[keyName] = get(obj, keyName); - }, + /** + Expands `pattern`, invoking `callback` for each expansion. - itemPropertyDidChange: function(obj, keyName, array, observerContext) { - if (--this.changedItemCount === 0) { - this.flushChanges(); - } - }, + The only pattern supported is brace-expansion, anything else will be passed + once to `callback` directly. - flushChanges: function() { - var changedItems = this.changedItems, key, c, changeMeta; + Example - for (key in changedItems) { - c = changedItems[key]; - if (c.observerContext.destroyed) { continue; } + ```js + function echo(arg){ console.log(arg); } - this.updateIndexes(c.observerContext.trackedArray, c.observerContext.dependentArray); + Ember.expandProperties('foo.bar', echo); //=> 'foo.bar' + Ember.expandProperties('{foo,bar}', echo); //=> 'foo', 'bar' + Ember.expandProperties('foo.{bar,baz}', echo); //=> 'foo.bar', 'foo.baz' + Ember.expandProperties('{foo,bar}.baz', echo); //=> '{foo,bar}.baz' + Ember.expandProperties('foo.{bar,baz}.@each', echo) //=> 'foo.bar.@each', 'foo.baz.@each' + Ember.expandProperties('{foo,bar}.{spam,eggs}', echo) //=> 'foo.spam', 'foo.eggs', 'bar.spam', 'bar.eggs' + Ember.expandProperties('{foo}.bar.{baz}') //=> 'foo.bar.baz' + ``` - changeMeta = createChangeMeta(c.array, c.obj, c.observerContext.index, this.instanceMeta.propertyName, this.cp, c.previousValues); - this.setValue( - this.callbacks.removedItem.call(this.instanceMeta.context, this.getValue(), c.obj, changeMeta, this.instanceMeta.sugarMeta)); - this.setValue( - this.callbacks.addedItem.call(this.instanceMeta.context, this.getValue(), c.obj, changeMeta, this.instanceMeta.sugarMeta)); - } - this.changedItems = {}; + @method + @private + @param {String} pattern The property pattern to expand. + @param {Function} callback The callback to invoke. It is invoked once per + expansion, and is passed the expansion. + */ + __exports__["default"] = function expandProperties(pattern, callback) { + if (pattern.indexOf(' ') > -1) { + throw new EmberError('Brace expanded properties cannot contain spaces, ' + + 'e.g. `user.{firstName, lastName}` should be `user.{firstName,lastName}`'); } - }; - function normalizeIndex(index, length, newItemsOffset) { - if (index < 0) { - return Math.max(0, length + index); - } else if (index < length) { - return index; - } else /* index > length */ { - return Math.min(length - newItemsOffset, index); - } - } + + return newExpandProperties(pattern, callback); + } - function normalizeRemoveCount(index, length, removedCount) { - return Math.min(removedCount, length - index); - } + function oldExpandProperties(pattern, callback) { + var match, prefix, list; - function createChangeMeta(dependentArray, item, index, propertyName, property, previousValues) { - var meta = { - arrayChanged: dependentArray, - index: index, - item: item, - propertyName: propertyName, - property: property - }; + if (match = BRACE_EXPANSION.exec(pattern)) { + prefix = match[1]; + list = match[2]; - if (previousValues) { - // previous values only available for item property changes - meta.previousValues = previousValues; + forEach(list.split(','), function (suffix) { + callback(prefix + suffix); + }); + } else { + callback(pattern); } - - return meta; } - function addItems (dependentArray, callbacks, cp, propertyName, meta) { - forEach(dependentArray, function (item, index) { - meta.setValue( callbacks.addedItem.call( - this, meta.getValue(), item, createChangeMeta(dependentArray, item, index, propertyName, cp), meta.sugarMeta)); - }, this); - } + function newExpandProperties(pattern, callback) { + if ('string' === Ember.typeOf(pattern)) { + var parts = pattern.split(SPLIT_REGEX); + var properties = [parts]; - function reset(cp, propertyName) { - var callbacks = cp._callbacks(), - meta; + forEach(parts, function(part, index) { + if (part.indexOf(',') >= 0) { + properties = duplicateAndReplace(properties, part.split(','), index); + } + }); - if (cp._hasInstanceMeta(this, propertyName)) { - meta = cp._instanceMeta(this, propertyName); - meta.setValue(cp.resetValue(meta.getValue())); + forEach(properties, function(property) { + callback(property.join('')); + }); } else { - meta = cp._instanceMeta(this, propertyName); - } - - if (cp.options.initialize) { - cp.options.initialize.call(this, meta.getValue(), { property: cp, propertyName: propertyName }, meta.sugarMeta); - } - } - - function partiallyRecomputeFor(obj, dependentKey) { - if (arrayBracketPattern.test(dependentKey)) { - return false; + callback(pattern); } - - var value = get(obj, dependentKey); - return EmberArray.detect(value); } - function ReduceComputedPropertyInstanceMeta(context, propertyName, initialValue) { - this.context = context; - this.propertyName = propertyName; - this.cache = metaFor(context).cache; + function duplicateAndReplace(properties, currentParts, index) { + var all = []; - this.dependentArrays = {}; - this.sugarMeta = {}; + forEach(properties, function(property) { + forEach(currentParts, function(part) { + var current = property.slice(0); + current[index] = part; + all.push(current); + }); + }); - this.initialValue = initialValue; + return all; } + }); +enifed("ember-metal/get_properties", + ["ember-metal/property_get","ember-metal/utils","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var get = __dependency1__.get; + var typeOf = __dependency2__.typeOf; - ReduceComputedPropertyInstanceMeta.prototype = { - getValue: function () { - var value = cacheGet(this.cache, this.propertyName); - if (value !== undefined) { - return value; - } else { - return this.initialValue; - } - }, + /** + To get multiple properties at once, call `Ember.getProperties` + with an object followed by a list of strings or an array: - setValue: function(newValue, triggerObservers) { - // This lets sugars force a recomputation, handy for very simple - // implementations of eg max. - if (newValue === cacheGet(this.cache, this.propertyName)) { - return; - } + ```javascript + Ember.getProperties(record, 'firstName', 'lastName', 'zipCode'); + // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } + ``` - if (triggerObservers) { - propertyWillChange(this.context, this.propertyName); - } + is equivalent to: - if (newValue === undefined) { - cacheRemove(this.cache, this.propertyName); - } else { - cacheSet(this.cache, this.propertyName, newValue); - } + ```javascript + Ember.getProperties(record, ['firstName', 'lastName', 'zipCode']); + // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } + ``` - if (triggerObservers) { - propertyDidChange(this.context, this.propertyName); - } + @method getProperties + @param obj + @param {String...|Array} list of keys to get + @return {Hash} + */ + __exports__["default"] = function getProperties(obj) { + var ret = {}; + var propertyNames = arguments; + var i = 1; + + if (arguments.length === 2 && typeOf(arguments[1]) === 'array') { + i = 0; + propertyNames = arguments[1]; } - }; + for(var len = propertyNames.length; i < len; i++) { + ret[propertyNames[i]] = get(obj, propertyNames[i]); + } + return ret; + } + }); +enifed("ember-metal/injected_property", + ["ember-metal/core","ember-metal/computed","ember-metal/alias","ember-metal/properties","ember-metal/platform","ember-metal/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // Ember.assert + var ComputedProperty = __dependency2__.ComputedProperty; + var AliasedProperty = __dependency3__.AliasedProperty; + var Descriptor = __dependency4__.Descriptor; + var create = __dependency5__.create; + var meta = __dependency6__.meta; /** - A computed property whose dependent keys are arrays and which is updated with - "one at a time" semantics. + Read-only property that returns the result of a container lookup. - @class ReduceComputedProperty + @class InjectedProperty @namespace Ember - @extends Ember.ComputedProperty + @extends Ember.Descriptor @constructor + @param {String} type The container type the property will lookup + @param {String} name (optional) The name the property will lookup, defaults + to the property's name */ - function ReduceComputedProperty(options) { - var cp = this; + function InjectedProperty(type, name) { + this.type = type; + this.name = name; - this.options = options; + this._super$Constructor(injectedPropertyGet); + AliasedPropertyPrototype.oneWay.call(this); + } - this._dependentKeys = null; - // A map of dependentKey -> [itemProperty, ...] that tracks what properties of - // items in the array we must track to update this property. - this._itemPropertyKeys = {}; - this._previousItemPropertyKeys = {}; + function injectedPropertyGet(keyName) { + var desc = meta(this).descs[keyName]; - this.readOnly(); - this.cacheable(); + Ember.assert("Attempting to lookup an injected property on an object " + + "without a container, ensure that the object was " + + "instantiated via a container.", this.container); - this.recomputeOnce = function(propertyName) { - // What we really want to do is coalesce by . - // We need a form of `scheduleOnce` that accepts an arbitrary token to - // coalesce by, in addition to the target and method. - run.once(this, recompute, propertyName); - }; - var recompute = function(propertyName) { - var dependentKeys = cp._dependentKeys, - meta = cp._instanceMeta(this, propertyName), - callbacks = cp._callbacks(); + return this.container.lookup(desc.type + ':' + (desc.name || keyName)); + } - reset.call(this, cp, propertyName); + InjectedProperty.prototype = create(Descriptor.prototype); - meta.dependentArraysObserver.suspendArrayObservers(function () { - forEach(cp._dependentKeys, function (dependentKey) { - Ember.assert( - "dependent array " + dependentKey + " must be an `Ember.Array`. " + - "If you are not extending arrays, you will need to wrap native arrays with `Ember.A`", - !(isArray(get(this, dependentKey)) && !EmberArray.detect(get(this, dependentKey)))); + var InjectedPropertyPrototype = InjectedProperty.prototype; + var ComputedPropertyPrototype = ComputedProperty.prototype; + var AliasedPropertyPrototype = AliasedProperty.prototype; - if (!partiallyRecomputeFor(this, dependentKey)) { return; } + InjectedPropertyPrototype._super$Constructor = ComputedProperty; - var dependentArray = get(this, dependentKey), - previousDependentArray = meta.dependentArrays[dependentKey]; + InjectedPropertyPrototype.get = ComputedPropertyPrototype.get; + InjectedPropertyPrototype.readOnly = ComputedPropertyPrototype.readOnly; - if (dependentArray === previousDependentArray) { - // The array may be the same, but our item property keys may have - // changed, so we set them up again. We can't easily tell if they've - // changed: the array may be the same object, but with different - // contents. - if (cp._previousItemPropertyKeys[dependentKey]) { - delete cp._previousItemPropertyKeys[dependentKey]; - meta.dependentArraysObserver.setupPropertyObservers(dependentKey, cp._itemPropertyKeys[dependentKey]); - } - } else { - meta.dependentArrays[dependentKey] = dependentArray; + InjectedPropertyPrototype.teardown = ComputedPropertyPrototype.teardown; - if (previousDependentArray) { - meta.dependentArraysObserver.teardownObservers(previousDependentArray, dependentKey); - } + __exports__["default"] = InjectedProperty; + }); +enifed("ember-metal/instrumentation", + ["ember-metal/core","ember-metal/utils","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + var tryCatchFinally = __dependency2__.tryCatchFinally; - if (dependentArray) { - meta.dependentArraysObserver.setupObservers(dependentArray, dependentKey); - } - } - }, this); - }, this); + /** + The purpose of the Ember Instrumentation module is + to provide efficient, general-purpose instrumentation + for Ember. - forEach(cp._dependentKeys, function(dependentKey) { - if (!partiallyRecomputeFor(this, dependentKey)) { return; } + Subscribe to a listener by using `Ember.subscribe`: - var dependentArray = get(this, dependentKey); - if (dependentArray) { - addItems.call(this, dependentArray, callbacks, cp, propertyName, meta); - } - }, this); - }; + ```javascript + Ember.subscribe("render", { + before: function(name, timestamp, payload) { + }, - this.func = function (propertyName) { - Ember.assert("Computed reduce values require at least one dependent key", cp._dependentKeys); + after: function(name, timestamp, payload) { - recompute.call(this, propertyName); + } + }); + ``` - return cp._instanceMeta(this, propertyName).getValue(); - }; - } + If you return a value from the `before` callback, that same + value will be passed as a fourth parameter to the `after` + callback. - ReduceComputedProperty.prototype = o_create(ComputedProperty.prototype); + Instrument a block of code by using `Ember.instrument`: - function defaultCallback(computedValue) { - return computedValue; - } + ```javascript + Ember.instrument("render.handlebars", payload, function() { + // rendering logic + }, binding); + ``` - ReduceComputedProperty.prototype._callbacks = function () { - if (!this.callbacks) { - var options = this.options; - this.callbacks = { - removedItem: options.removedItem || defaultCallback, - addedItem: options.addedItem || defaultCallback - }; - } - return this.callbacks; - }; + Event names passed to `Ember.instrument` are namespaced + by periods, from more general to more specific. Subscribers + can listen for events by whatever level of granularity they + are interested in. - ReduceComputedProperty.prototype._hasInstanceMeta = function (context, propertyName) { - return !!metaFor(context).cacheMeta[propertyName]; - }; + In the above example, the event is `render.handlebars`, + and the subscriber listened for all events beginning with + `render`. It would receive callbacks for events named + `render`, `render.handlebars`, `render.container`, or + even `render.handlebars.layout`. - ReduceComputedProperty.prototype._instanceMeta = function (context, propertyName) { - var cacheMeta = metaFor(context).cacheMeta, - meta = cacheMeta[propertyName]; + @class Instrumentation + @namespace Ember + @static + */ + var subscribers = []; + __exports__.subscribers = subscribers;var cache = {}; - if (!meta) { - meta = cacheMeta[propertyName] = new ReduceComputedPropertyInstanceMeta(context, propertyName, this.initialValue()); - meta.dependentArraysObserver = new DependentArraysObserver(this._callbacks(), this, meta, context, propertyName, meta.sugarMeta); + var populateListeners = function(name) { + var listeners = []; + var subscriber; + + for (var i=0, l=subscribers.length; i 1) { - args = a_slice.call(arguments, 0, -1); - options = a_slice.call(arguments, -1)[0]; - } + registerCoreLibrary: function(name, version) { + this.register(name, version, true); + }, + + deRegister: function(name) { + var lib = this._getLibraryByName(name); + var index; + + if (lib) { + index = indexOf(this._registry, lib); + this._registry.splice(index, 1); + } + }, - if (typeof options !== "object") { - throw new EmberError("Reduce Computed Property declared without an options hash"); + each: function(callback) { + Ember.deprecate('Using Ember.libraries.each() is deprecated. Access to a list of registered libraries is currently a private API. If you are not knowingly accessing this method, your out-of-date Ember Inspector may be doing so.'); + forEach(this._registry, function(lib) { + callback(lib.name, lib.version); + }); } + }; - if (!('initialValue' in options)) { - throw new EmberError("Reduce Computed Property declared without an initial value"); + __exports__["default"] = Libraries; + }); +enifed("ember-metal/logger", + ["ember-metal/core","ember-metal/error","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // Ember.imports + var EmberError = __dependency2__["default"]; + + function K() { return this; } + + function consoleMethod(name) { + var consoleObj, logToConsole; + if (Ember.imports.console) { + consoleObj = Ember.imports.console; + } else if (typeof console !== 'undefined') { + consoleObj = console; } - var cp = new ReduceComputedProperty(options); + var method = typeof consoleObj === 'object' ? consoleObj[name] : null; - if (args) { - cp.property.apply(cp, args); + if (method) { + // Older IE doesn't support bind, but Chrome needs it + if (typeof method.bind === 'function') { + logToConsole = method.bind(consoleObj); + logToConsole.displayName = 'console.' + name; + return logToConsole; + } else if (typeof method.apply === 'function') { + logToConsole = function() { + method.apply(consoleObj, arguments); + }; + logToConsole.displayName = 'console.' + name; + return logToConsole; + } else { + return function() { + var message = Array.prototype.join.call(arguments, ', '); + method(message); + }; + } } + } - return cp; + function assertPolyfill(test, message) { + if (!test) { + try { + // attempt to preserve the stack + throw new EmberError("assertion failed: " + message); + } catch(error) { + setTimeout(function() { + throw error; + }, 0); + } + } } - __exports__.reduceComputed = reduceComputed; - __exports__.ReduceComputedProperty = ReduceComputedProperty; - }); -define("ember-runtime/computed/reduce_computed_macros", - ["ember-metal/core","ember-metal/merge","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/error","ember-metal/enumerable_utils","ember-metal/run_loop","ember-metal/observer","ember-runtime/computed/array_computed","ember-runtime/computed/reduce_computed","ember-runtime/system/object_proxy","ember-runtime/system/subarray","ember-runtime/keys","ember-runtime/compare","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 + Inside Ember-Metal, simply uses the methods from `imports.console`. + Override this to provide more robust logging functionality. + + @class Logger + @namespace Ember */ + __exports__["default"] = { + /** + Logs the arguments to the console. + You can pass as many arguments as you want and they will be joined together with a space. - var Ember = __dependency1__["default"]; - // Ember.assert - var merge = __dependency2__["default"]; - var get = __dependency3__.get; - var set = __dependency4__.set; - var isArray = __dependency5__.isArray; - var guidFor = __dependency5__.guidFor; - var EmberError = __dependency6__["default"]; - var EnumerableUtils = __dependency7__["default"]; - var run = __dependency8__["default"]; - var addObserver = __dependency9__.addObserver; - var arrayComputed = __dependency10__.arrayComputed; - var reduceComputed = __dependency11__.reduceComputed; - var ObjectProxy = __dependency12__["default"]; - var SubArray = __dependency13__["default"]; - var keys = __dependency14__["default"]; - var compare = __dependency15__["default"]; + ```javascript + var foo = 1; + Ember.Logger.log('log value of foo:', foo); + // "log value of foo: 1" will be printed to the console + ``` - var a_slice = [].slice, - forEach = EnumerableUtils.forEach, - SearchProxy; + @method log + @for Ember.Logger + @param {*} arguments + */ + log: consoleMethod('log') || K, - /** - A computed property that returns the sum of the value - in the dependent array. + /** + Prints the arguments to the console with a warning icon. + You can pass as many arguments as you want and they will be joined together with a space. - @method computed.sum - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computes the sum of all values in the dependentKey's array - @since 1.4.0 - */ + ```javascript + Ember.Logger.warn('Something happened!'); + // "Something happened!" will be printed to the console with a warning icon. + ``` - function sum(dependentKey){ - return reduceComputed(dependentKey, { - initialValue: 0, + @method warn + @for Ember.Logger + @param {*} arguments + */ + warn: consoleMethod('warn') || K, - addedItem: function(accumulatedValue, item, changeMeta, instanceMeta){ - return accumulatedValue + item; - }, + /** + Prints the arguments to the console with an error icon, red text and a stack trace. + You can pass as many arguments as you want and they will be joined together with a space. - removedItem: function(accumulatedValue, item, changeMeta, instanceMeta){ - return accumulatedValue - item; - } - }); - }; + ```javascript + Ember.Logger.error('Danger! Danger!'); + // "Danger! Danger!" will be printed to the console in red text. + ``` - /** - A computed property that calculates the maximum value in the - dependent array. This will return `-Infinity` when the dependent - array is empty. + @method error + @for Ember.Logger + @param {*} arguments + */ + error: consoleMethod('error') || K, - ```javascript - var Person = Ember.Object.extend({ - childAges: Ember.computed.mapBy('children', 'age'), - maxChildAge: Ember.computed.max('childAges') - }); + /** + Logs the arguments to the console. + You can pass as many arguments as you want and they will be joined together with a space. - var lordByron = Person.create({ children: [] }); + ```javascript + var foo = 1; + Ember.Logger.info('log value of foo:', foo); + // "log value of foo: 1" will be printed to the console + ``` - lordByron.get('maxChildAge'); // -Infinity - lordByron.get('children').pushObject({ - name: 'Augusta Ada Byron', age: 7 - }); - lordByron.get('maxChildAge'); // 7 - lordByron.get('children').pushObjects([{ - name: 'Allegra Byron', - age: 5 - }, { - name: 'Elizabeth Medora Leigh', - age: 8 - }]); - lordByron.get('maxChildAge'); // 8 - ``` + @method info + @for Ember.Logger + @param {*} arguments + */ + info: consoleMethod('info') || K, - @method computed.max - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computes the largest value in the dependentKey's array - */ - function max (dependentKey) { - return reduceComputed(dependentKey, { - initialValue: -Infinity, + /** + Logs the arguments to the console in blue text. + You can pass as many arguments as you want and they will be joined together with a space. - addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { - return Math.max(accumulatedValue, item); - }, + ```javascript + var foo = 1; + Ember.Logger.debug('log value of foo:', foo); + // "log value of foo: 1" will be printed to the console + ``` - removedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { - if (item < accumulatedValue) { - return accumulatedValue; - } - } - }); - }; + @method debug + @for Ember.Logger + @param {*} arguments + */ + debug: consoleMethod('debug') || consoleMethod('info') || K, + + /** + If the value passed into `Ember.Logger.assert` is not truthy it will throw an error with a stack trace. + + ```javascript + Ember.Logger.assert(true); // undefined + Ember.Logger.assert(true === false); // Throws an Assertion failed error. + ``` + @method assert + @for Ember.Logger + @param {Boolean} bool Value to test + */ + assert: consoleMethod('assert') || assertPolyfill + }; + }); +enifed("ember-metal/map", + ["ember-metal/utils","ember-metal/array","ember-metal/platform","ember-metal/deprecate_property","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; /** - A computed property that calculates the minimum value in the - dependent array. This will return `Infinity` when the dependent - array is empty. + @module ember-metal + */ - ```javascript - var Person = Ember.Object.extend({ - childAges: Ember.computed.mapBy('children', 'age'), - minChildAge: Ember.computed.min('childAges') - }); + /* + JavaScript (before ES6) does not have a Map implementation. Objects, + which are often used as dictionaries, may only have Strings as keys. - var lordByron = Person.create({ children: [] }); + Because Ember has a way to get a unique identifier for every object + via `Ember.guidFor`, we can implement a performant Map with arbitrary + keys. Because it is commonly used in low-level bookkeeping, Map is + implemented as a pure JavaScript object for performance. - lordByron.get('minChildAge'); // Infinity - lordByron.get('children').pushObject({ - name: 'Augusta Ada Byron', age: 7 - }); - lordByron.get('minChildAge'); // 7 - lordByron.get('children').pushObjects([{ - name: 'Allegra Byron', - age: 5 - }, { - name: 'Elizabeth Medora Leigh', - age: 8 - }]); - lordByron.get('minChildAge'); // 5 - ``` + This implementation follows the current iteration of the ES6 proposal for + maps (http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets), + with one exception: as we do not have the luxury of in-VM iteration, we implement a + forEach method for iteration. - @method computed.min - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computes the smallest value in the dependentKey's array + Map is mocked out to look like an Ember object, so you can do + `Ember.Map.create()` for symmetry with other Ember classes. */ - function min(dependentKey) { - return reduceComputed(dependentKey, { - initialValue: Infinity, - addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { - return Math.min(accumulatedValue, item); - }, + var guidFor = __dependency1__.guidFor; + var indexOf = __dependency2__.indexOf; + var create = __dependency3__.create; + var deprecateProperty = __dependency4__.deprecateProperty; - removedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { - if (item > accumulatedValue) { - return accumulatedValue; - } - } - }); - }; + function missingFunction(fn) { + throw new TypeError('' + Object.prototype.toString.call(fn) + " is not a function"); + } - /** - Returns an array mapped via the callback + function missingNew(name) { + throw new TypeError("Constructor " + name + "requires 'new'"); + } - The callback method you provide should have the following signature. - `item` is the current item in the iteration. + function copyNull(obj) { + var output = create(null); - ```javascript - function(item); - ``` + for (var prop in obj) { + // hasOwnPropery is not needed because obj is Object.create(null); + output[prop] = obj[prop]; + } - Example + return output; + } - ```javascript - var Hamster = Ember.Object.extend({ - excitingChores: Ember.computed.map('chores', function(chore) { - return chore.toUpperCase() + '!'; - }) - }); + function copyMap(original, newObject) { + var keys = original.keys.copy(); + var values = copyNull(original.values); - var hamster = Hamster.create({ - chores: ['clean', 'write more unit tests'] - }); + newObject.keys = keys; + newObject.values = values; + newObject.size = original.size; - hamster.get('excitingChores'); // ['CLEAN!', 'WRITE MORE UNIT TESTS!'] - ``` + return newObject; + } - @method computed.map - @for Ember - @param {String} dependentKey - @param {Function} callback - @return {Ember.ComputedProperty} an array mapped via the callback + /** + This class is used internally by Ember and Ember Data. + Please do not use it at this time. We plan to clean it up + and add many tests soon. + + @class OrderedSet + @namespace Ember + @constructor + @private */ - function map(dependentKey, callback) { - var options = { - addedItem: function(array, item, changeMeta, instanceMeta) { - var mapped = callback.call(this, item); - array.insertAt(changeMeta.index, mapped); - return array; - }, - removedItem: function(array, item, changeMeta, instanceMeta) { - array.removeAt(changeMeta.index, 1); - return array; - } - }; + function OrderedSet() { - return arrayComputed(dependentKey, options); - }; + if (this instanceof OrderedSet) { + this.clear(); + this._silenceRemoveDeprecation = false; + } else { + missingNew("OrderedSet"); + } + } /** - Returns an array mapped to the specified key. + @method create + @static + @return {Ember.OrderedSet} + */ + OrderedSet.create = function() { + var Constructor = this; - ```javascript - var Person = Ember.Object.extend({ - childAges: Ember.computed.mapBy('children', 'age') - }); + return new Constructor(); + }; - var lordByron = Person.create({ children: [] }); + OrderedSet.prototype = { + constructor: OrderedSet, + /** + @method clear + */ + clear: function() { + this.presenceSet = create(null); + this.list = []; + this.size = 0; + }, - lordByron.get('childAges'); // [] - lordByron.get('children').pushObject({ name: 'Augusta Ada Byron', age: 7 }); - lordByron.get('childAges'); // [7] - lordByron.get('children').pushObjects([{ - name: 'Allegra Byron', - age: 5 - }, { - name: 'Elizabeth Medora Leigh', - age: 8 - }]); - lordByron.get('childAges'); // [7, 5, 8] - ``` + /** + @method add + @param obj + @param guid (optional, and for internal use) + @return {Ember.OrderedSet} + */ + add: function(obj, _guid) { + var guid = _guid || guidFor(obj); + var presenceSet = this.presenceSet; + var list = this.list; - @method computed.mapBy - @for Ember - @param {String} dependentKey - @param {String} propertyKey - @return {Ember.ComputedProperty} an array mapped to the specified key - */ - function mapBy (dependentKey, propertyKey) { - var callback = function(item) { return get(item, propertyKey); }; - return map(dependentKey + '.@each.' + propertyKey, callback); - }; + if (presenceSet[guid] === true) { + return; + } - /** - @method computed.mapProperty - @for Ember - @deprecated Use `Ember.computed.mapBy` instead - @param dependentKey - @param propertyKey - */ - var mapProperty = mapBy; + presenceSet[guid] = true; + this.size = list.push(obj); - /** - Filters the array by the callback. + return this; + }, - The callback method you provide should have the following signature. - `item` is the current item in the iteration. + /** + @deprecated - ```javascript - function(item); - ``` + @method remove + @param obj + @param _guid (optional and for internal use only) + @return {Boolean} + */ + remove: function(obj, _guid) { + Ember.deprecate('Calling `OrderedSet.prototype.remove` has been deprecated, please use `OrderedSet.prototype.delete` instead.', this._silenceRemoveDeprecation); - ```javascript - var Hamster = Ember.Object.extend({ - remainingChores: Ember.computed.filter('chores', function(chore) { - return !chore.done; - }) - }); + return this["delete"](obj, _guid); + }, - var hamster = Hamster.create({ - chores: [ - { name: 'cook', done: true }, - { name: 'clean', done: true }, - { name: 'write more unit tests', done: false } - ] - }); + /** + @method delete + @param obj + @param _guid (optional and for internal use only) + @return {Boolean} + */ + "delete": function(obj, _guid) { + var guid = _guid || guidFor(obj); + var presenceSet = this.presenceSet; + var list = this.list; - hamster.get('remainingChores'); // [{name: 'write more unit tests', done: false}] - ``` + if (presenceSet[guid] === true) { + delete presenceSet[guid]; + var index = indexOf.call(list, obj); + if (index > -1) { + list.splice(index, 1); + } + this.size = list.length; + return true; + } else { + return false; + } + }, - @method computed.filter - @for Ember - @param {String} dependentKey - @param {Function} callback - @return {Ember.ComputedProperty} the filtered array - */ - function filter(dependentKey, callback) { - var options = { - initialize: function (array, changeMeta, instanceMeta) { - instanceMeta.filteredArrayIndexes = new SubArray(); - }, + /** + @method isEmpty + @return {Boolean} + */ + isEmpty: function() { + return this.size === 0; + }, - addedItem: function(array, item, changeMeta, instanceMeta) { - var match = !!callback.call(this, item), - filterIndex = instanceMeta.filteredArrayIndexes.addItem(changeMeta.index, match); + /** + @method has + @param obj + @return {Boolean} + */ + has: function(obj) { + if (this.size === 0) { return false; } - if (match) { - array.insertAt(filterIndex, item); - } + var guid = guidFor(obj); + var presenceSet = this.presenceSet; - return array; - }, + return presenceSet[guid] === true; + }, - removedItem: function(array, item, changeMeta, instanceMeta) { - var filterIndex = instanceMeta.filteredArrayIndexes.removeItem(changeMeta.index); + /** + @method forEach + @param {Function} fn + @param self + */ + forEach: function(fn /*, thisArg*/) { + if (typeof fn !== 'function') { + missingFunction(fn); + } - if (filterIndex > -1) { - array.removeAt(filterIndex); - } + if (this.size === 0) { return; } - return array; + var list = this.list; + var length = arguments.length; + var i; + + if (length === 2) { + for (i = 0; i < list.length; i++) { + fn.call(arguments[1], list[i]); + } + } else { + for (i = 0; i < list.length; i++) { + fn(list[i]); + } } - }; + }, - return arrayComputed(dependentKey, options); + /** + @method toArray + @return {Array} + */ + toArray: function() { + return this.list.slice(); + }, + + /** + @method copy + @return {Ember.OrderedSet} + */ + copy: function() { + var Constructor = this.constructor; + var set = new Constructor(); + + set._silenceRemoveDeprecation = this._silenceRemoveDeprecation; + set.presenceSet = copyNull(this.presenceSet); + set.list = this.toArray(); + set.size = this.size; + + return set; + } }; + deprecateProperty(OrderedSet.prototype, 'length', 'size'); + /** - Filters the array by the property and value + A Map stores values indexed by keys. Unlike JavaScript's + default Objects, the keys of a Map can be any JavaScript + object. - ```javascript - var Hamster = Ember.Object.extend({ - remainingChores: Ember.computed.filterBy('chores', 'done', false) - }); + Internally, a Map has two data structures: - var hamster = Hamster.create({ - chores: [ - { name: 'cook', done: true }, - { name: 'clean', done: true }, - { name: 'write more unit tests', done: false } - ] - }); + 1. `keys`: an OrderedSet of all of the existing keys + 2. `values`: a JavaScript Object indexed by the `Ember.guidFor(key)` - hamster.get('remainingChores'); // [{ name: 'write more unit tests', done: false }] - ``` + When a key/value pair is added for the first time, we + add the key to the `keys` OrderedSet, and create or + replace an entry in `values`. When an entry is deleted, + we delete its entry in `keys` and `values`. - @method computed.filterBy - @for Ember - @param {String} dependentKey - @param {String} propertyKey - @param {*} value - @return {Ember.ComputedProperty} the filtered array + @class Map + @namespace Ember + @private + @constructor */ - function filterBy (dependentKey, propertyKey, value) { - var callback; - - if (arguments.length === 2) { - callback = function(item) { - return get(item, propertyKey); - }; + function Map() { + if (this instanceof this.constructor) { + this.keys = OrderedSet.create(); + this.keys._silenceRemoveDeprecation = true; + this.values = create(null); + this.size = 0; } else { - callback = function(item) { - return get(item, propertyKey) === value; - }; + missingNew("OrderedSet"); } + } - return filter(dependentKey + '.@each.' + propertyKey, callback); - }; + Ember.Map = Map; /** - @method computed.filterProperty - @for Ember - @param dependentKey - @param propertyKey - @param value - @deprecated Use `Ember.computed.filterBy` instead + @method create + @static */ - var filterProperty = filterBy; - - /** - A computed property which returns a new array with all the unique - elements from one or more dependent arrays. - - Example + Map.create = function() { + var Constructor = this; + return new Constructor(); + }; - ```javascript - var Hamster = Ember.Object.extend({ - uniqueFruits: Ember.computed.uniq('fruits') - }); + Map.prototype = { + constructor: Map, - var hamster = Hamster.create({ - fruits: [ - 'banana', - 'grape', - 'kale', - 'banana' - ] - }); + /** + This property will change as the number of objects in the map changes. - hamster.get('uniqueFruits'); // ['banana', 'grape', 'kale'] - ``` + @property size + @type number + @default 0 + */ + size: 0, - @method computed.uniq - @for Ember - @param {String} propertyKey* - @return {Ember.ComputedProperty} computes a new array with all the - unique elements from the dependent array - */ - function uniq() { - var args = a_slice.call(arguments); - args.push({ - initialize: function(array, changeMeta, instanceMeta) { - instanceMeta.itemCounts = {}; - }, + /** + Retrieve the value associated with a given key. - addedItem: function(array, item, changeMeta, instanceMeta) { - var guid = guidFor(item); + @method get + @param {*} key + @return {*} the value associated with the key, or `undefined` + */ + get: function(key) { + if (this.size === 0) { return; } - if (!instanceMeta.itemCounts[guid]) { - instanceMeta.itemCounts[guid] = 1; - } else { - ++instanceMeta.itemCounts[guid]; - } - array.addObject(item); - return array; - }, - removedItem: function(array, item, _, instanceMeta) { - var guid = guidFor(item), - itemCounts = instanceMeta.itemCounts; + var values = this.values; + var guid = guidFor(key); - if (--itemCounts[guid] === 0) { - array.removeObject(item); - } - return array; - } - }); - return arrayComputed.apply(null, args); - }; + return values[guid]; + }, - /** - Alias for [Ember.computed.uniq](/api/#method_computed_uniq). + /** + Adds a value to the map. If a value for the given key has already been + provided, the new value will replace the old value. - @method computed.union - @for Ember - @param {String} propertyKey* - @return {Ember.ComputedProperty} computes a new array with all the - unique elements from the dependent array - */ - var union = uniq; + @method set + @param {*} key + @param {*} value + @return {Ember.Map} + */ + set: function(key, value) { + var keys = this.keys; + var values = this.values; + var guid = guidFor(key); - /** - A computed property which returns a new array with all the duplicated - elements from two or more dependent arrays. + // ensure we don't store -0 + var k = key === -0 ? 0 : key; - Example + keys.add(k, guid); - ```javascript - var obj = Ember.Object.createWithMixins({ - adaFriends: ['Charles Babbage', 'John Hobhouse', 'William King', 'Mary Somerville'], - charlesFriends: ['William King', 'Mary Somerville', 'Ada Lovelace', 'George Peacock'], - friendsInCommon: Ember.computed.intersect('adaFriends', 'charlesFriends') - }); + values[guid] = value; - obj.get('friendsInCommon'); // ['William King', 'Mary Somerville'] - ``` + this.size = keys.size; - @method computed.intersect - @for Ember - @param {String} propertyKey* - @return {Ember.ComputedProperty} computes a new array with all the - duplicated elements from the dependent arrays - */ - function intersect() { - var getDependentKeyGuids = function (changeMeta) { - return EnumerableUtils.map(changeMeta.property._dependentKeys, function (dependentKey) { - return guidFor(dependentKey); - }); - }; + return this; + }, - var args = a_slice.call(arguments); - args.push({ - initialize: function (array, changeMeta, instanceMeta) { - instanceMeta.itemCounts = {}; - }, + /** + @deprecated see delete + Removes a value from the map for an associated key. - addedItem: function(array, item, changeMeta, instanceMeta) { - var itemGuid = guidFor(item), - dependentGuids = getDependentKeyGuids(changeMeta), - dependentGuid = guidFor(changeMeta.arrayChanged), - numberOfDependentArrays = changeMeta.property._dependentKeys.length, - itemCounts = instanceMeta.itemCounts; + @method remove + @param {*} key + @return {Boolean} true if an item was removed, false otherwise + */ + remove: function(key) { + Ember.deprecate('Calling `Map.prototype.remove` has been deprecated, please use `Map.prototype.delete` instead.'); - if (!itemCounts[itemGuid]) { itemCounts[itemGuid] = {}; } - if (itemCounts[itemGuid][dependentGuid] === undefined) { itemCounts[itemGuid][dependentGuid] = 0; } + return this["delete"](key); + }, - if (++itemCounts[itemGuid][dependentGuid] === 1 && - numberOfDependentArrays === keys(itemCounts[itemGuid]).length) { + /** + Removes a value from the map for an associated key. - array.addObject(item); - } - return array; - }, - removedItem: function(array, item, changeMeta, instanceMeta) { - var itemGuid = guidFor(item), - dependentGuids = getDependentKeyGuids(changeMeta), - dependentGuid = guidFor(changeMeta.arrayChanged), - numberOfDependentArrays = changeMeta.property._dependentKeys.length, - numberOfArraysItemAppearsIn, - itemCounts = instanceMeta.itemCounts; - - if (itemCounts[itemGuid][dependentGuid] === undefined) { itemCounts[itemGuid][dependentGuid] = 0; } - if (--itemCounts[itemGuid][dependentGuid] === 0) { - delete itemCounts[itemGuid][dependentGuid]; - numberOfArraysItemAppearsIn = keys(itemCounts[itemGuid]).length; + @method delete + @param {*} key + @return {Boolean} true if an item was removed, false otherwise + */ + "delete": function(key) { + if (this.size === 0) { return false; } + // don't use ES6 "delete" because it will be annoying + // to use in browsers that are not ES6 friendly; + var keys = this.keys; + var values = this.values; + var guid = guidFor(key); - if (numberOfArraysItemAppearsIn === 0) { - delete itemCounts[itemGuid]; - } - array.removeObject(item); - } - return array; + if (keys["delete"](key, guid)) { + delete values[guid]; + this.size = keys.size; + return true; + } else { + return false; } - }); - return arrayComputed.apply(null, args); - }; - - /** - A computed property which returns a new array with all the - properties from the first dependent array that are not in the second - dependent array. + }, - Example + /** + Check whether a key is present. - ```javascript - var Hamster = Ember.Object.extend({ - likes: ['banana', 'grape', 'kale'], - wants: Ember.computed.setDiff('likes', 'fruits') - }); + @method has + @param {*} key + @return {Boolean} true if the item was present, false otherwise + */ + has: function(key) { + return this.keys.has(key); + }, - var hamster = Hamster.create({ - fruits: [ - 'grape', - 'kale', - ] - }); + /** + Iterate over all the keys and values. Calls the function once + for each key, passing in value, key, and the map being iterated over, + in that order. - hamster.get('wants'); // ['banana'] - ``` + The keys are guaranteed to be iterated over in insertion order. - @method computed.setDiff - @for Ember - @param {String} setAProperty - @param {String} setBProperty - @return {Ember.ComputedProperty} computes a new array with all the - items from the first dependent array that are not in the second - dependent array - */ - function setDiff(setAProperty, setBProperty) { - if (arguments.length !== 2) { - throw new EmberError("setDiff requires exactly two dependent arrays."); - } - return arrayComputed(setAProperty, setBProperty, { - addedItem: function (array, item, changeMeta, instanceMeta) { - var setA = get(this, setAProperty), - setB = get(this, setBProperty); + @method forEach + @param {Function} callback + @param {*} self if passed, the `this` value inside the + callback. By default, `this` is the map. + */ + forEach: function(callback /*, thisArg*/) { + if (typeof callback !== 'function') { + missingFunction(callback); + } - if (changeMeta.arrayChanged === setA) { - if (!setB.contains(item)) { - array.addObject(item); - } - } else { - array.removeObject(item); - } - return array; - }, + if (this.size === 0) { return; } - removedItem: function (array, item, changeMeta, instanceMeta) { - var setA = get(this, setAProperty), - setB = get(this, setBProperty); + var length = arguments.length; + var map = this; + var cb, thisArg; - if (changeMeta.arrayChanged === setB) { - if (setA.contains(item)) { - array.addObject(item); - } - } else { - array.removeObject(item); - } - return array; + if (length === 2) { + thisArg = arguments[1]; + cb = function(key) { + callback.call(thisArg, map.get(key), key, map); + }; + } else { + cb = function(key) { + callback(map.get(key), key, map); + }; } - }); - }; - function binarySearch(array, item, low, high) { - var mid, midItem, res, guidMid, guidItem; + this.keys.forEach(cb); + }, - if (arguments.length < 4) { high = get(array, 'length'); } - if (arguments.length < 3) { low = 0; } + /** + @method clear + */ + clear: function() { + this.keys.clear(); + this.values = create(null); + this.size = 0; + }, - if (low === high) { - return low; + /** + @method copy + @return {Ember.Map} + */ + copy: function() { + return copyMap(this, new Map()); } + }; - mid = low + Math.floor((high - low) / 2); - midItem = array.objectAt(mid); - - guidMid = _guidFor(midItem); - guidItem = _guidFor(item); + deprecateProperty(Map.prototype, 'length', 'size'); - if (guidMid === guidItem) { - return mid; - } + /** + @class MapWithDefault + @namespace Ember + @extends Ember.Map + @private + @constructor + @param [options] + @param {*} [options.defaultValue] + */ + function MapWithDefault(options) { + this._super$constructor(); + this.defaultValue = options.defaultValue; + } - res = this.order(midItem, item); - if (res === 0) { - res = guidMid < guidItem ? -1 : 1; + /** + @method create + @static + @param [options] + @param {*} [options.defaultValue] + @return {Ember.MapWithDefault|Ember.Map} If options are passed, returns + `Ember.MapWithDefault` otherwise returns `Ember.Map` + */ + MapWithDefault.create = function(options) { + if (options) { + return new MapWithDefault(options); + } else { + return new Map(); } + }; + MapWithDefault.prototype = create(Map.prototype); + MapWithDefault.prototype.constructor = MapWithDefault; + MapWithDefault.prototype._super$constructor = Map; + MapWithDefault.prototype._super$get = Map.prototype.get; - if (res < 0) { - return this.binarySearch(array, item, mid+1, high); - } else if (res > 0) { - return this.binarySearch(array, item, low, mid); - } + /** + Retrieve the value associated with a given key. - return mid; + @method get + @param {*} key + @return {*} the value associated with the key, or the default value + */ + MapWithDefault.prototype.get = function(key) { + var hasValue = this.has(key); - function _guidFor(item) { - if (SearchProxy.detectInstance(item)) { - return guidFor(get(item, 'content')); - } - return guidFor(item); + if (hasValue) { + return this._super$get(key); + } else { + var defaultValue = this.defaultValue(key); + this.set(key, defaultValue); + return defaultValue; } - } - - - var SearchProxy = ObjectProxy.extend(); + }; /** - A computed property which returns a new array with all the - properties from the first dependent array sorted based on a property - or sort function. + @method copy + @return {Ember.MapWithDefault} + */ + MapWithDefault.prototype.copy = function() { + var Constructor = this.constructor; + return copyMap(this, new Constructor({ + defaultValue: this.defaultValue + })); + }; - The callback method you provide should have the following signature: + __exports__["default"] = Map; + + __exports__.OrderedSet = OrderedSet; + __exports__.Map = Map; + __exports__.MapWithDefault = MapWithDefault; + }); +enifed("ember-metal/merge", + ["ember-metal/keys","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var keys = __dependency1__["default"]; + + /** + Merge the contents of two objects together into the first object. ```javascript - function(itemA, itemB); + Ember.merge({first: 'Tom'}, {last: 'Dale'}); // {first: 'Tom', last: 'Dale'} + var a = {first: 'Yehuda'}, b = {last: 'Katz'}; + Ember.merge(a, b); // a == {first: 'Yehuda', last: 'Katz'}, b == {last: 'Katz'} ``` - - `itemA` the first item to compare. - - `itemB` the second item to compare. + @method merge + @for Ember + @param {Object} original The object to merge into + @param {Object} updates The object to copy properties from + @return {Object} + */ + __exports__["default"] = function merge(original, updates) { + if (!updates || typeof updates !== 'object') { + return original; + } - This function should return negative number (e.g. `-1`) when `itemA` should come before - `itemB`. It should return positive number (e.g. `1`) when `itemA` should come after - `itemB`. If the `itemA` and `itemB` are equal this function should return `0`. + var props = keys(updates); + var prop; + var length = props.length; - Therefore, if this function is comparing some numeric values, simple `itemA - itemB` or - `itemA.get( 'foo' ) - itemB.get( 'foo' )` can be used instead of series of `if`. + for (var i = 0; i < length; i++) { + prop = props[i]; + original[prop] = updates[prop]; + } - Example + return original; + } + }); +enifed("ember-metal/mixin", + ["ember-metal/core","ember-metal/merge","ember-metal/array","ember-metal/platform","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/expand_properties","ember-metal/properties","ember-metal/computed","ember-metal/binding","ember-metal/observer","ember-metal/events","ember-metal/streams/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __exports__) { + // Remove "use strict"; from transpiled module until + // https://bugs.webkit.org/show_bug.cgi?id=138038 is fixed + // + // REMOVE_USE_STRICT: true - ```javascript - var ToDoList = Ember.Object.extend({ - // using standard ascending sort - todosSorting: ['name'], - sortedTodos: Ember.computed.sort('todos', 'todosSorting'), + /** + @module ember + @submodule ember-metal + */ - // using descending sort - todosSortingDesc: ['name:desc'], - sortedTodosDesc: Ember.computed.sort('todos', 'todosSortingDesc'), + var Ember = __dependency1__["default"]; + // warn, assert, wrap, et; + var merge = __dependency2__["default"]; + var a_indexOf = __dependency3__.indexOf; + var a_forEach = __dependency3__.forEach; + var o_create = __dependency4__.create; + var get = __dependency5__.get; + var set = __dependency6__.set; + var trySet = __dependency6__.trySet; + var guidFor = __dependency7__.guidFor; + var metaFor = __dependency7__.meta; + var wrap = __dependency7__.wrap; + var makeArray = __dependency7__.makeArray; + var isArray = __dependency7__.isArray; + var expandProperties = __dependency8__["default"]; + var Descriptor = __dependency9__.Descriptor; + var defineProperty = __dependency9__.defineProperty; + var ComputedProperty = __dependency10__.ComputedProperty; + var Binding = __dependency11__.Binding; + var addObserver = __dependency12__.addObserver; + var removeObserver = __dependency12__.removeObserver; + var addBeforeObserver = __dependency12__.addBeforeObserver; + var removeBeforeObserver = __dependency12__.removeBeforeObserver; + var _suspendObserver = __dependency12__._suspendObserver; + var addListener = __dependency13__.addListener; + var removeListener = __dependency13__.removeListener; + var isStream = __dependency14__.isStream; + + var REQUIRED; + var a_slice = [].slice; - // using a custom sort function - priorityTodos: Ember.computed.sort('todos', function(a, b){ - if (a.priority > b.priority) { - return 1; - } else if (a.priority < b.priority) { - return -1; - } + function superFunction(){ + var func = this.__nextSuper; + var ret; - return 0; - }), - }); + if (func) { + var length = arguments.length; + this.__nextSuper = null; + if (length === 0) { + ret = func.call(this); + } else if (length === 1) { + ret = func.call(this, arguments[0]); + } else if (length === 2) { + ret = func.call(this, arguments[0], arguments[1]); + } else { + ret = func.apply(this, arguments); + } + this.__nextSuper = func; + return ret; + } + } - var todoList = ToDoList.create({todos: [ - { name: 'Unit Test', priority: 2 }, - { name: 'Documentation', priority: 3 }, - { name: 'Release', priority: 1 } - ]}); + // ensure we prime superFunction to mitigate + // v8 bug potentially incorrectly deopts this function: https://code.google.com/p/v8/issues/detail?id=3709 + var primer = { + __nextSuper: function(a,b,c,d ) { } + }; - todoList.get('sortedTodos'); // [{ name:'Documentation', priority:3 }, { name:'Release', priority:1 }, { name:'Unit Test', priority:2 }] - todoList.get('sortedTodosDesc'); // [{ name:'Unit Test', priority:2 }, { name:'Release', priority:1 }, { name:'Documentation', priority:3 }] - todoList.get('priorityTodos'); // [{ name:'Release', priority:1 }, { name:'Unit Test', priority:2 }, { name:'Documentation', priority:3 }] - ``` + superFunction.call(primer); + superFunction.call(primer, 1); + superFunction.call(primer, 1, 2); + superFunction.call(primer, 1, 2, 3); - @method computed.sort - @for Ember - @param {String} dependentKey - @param {String or Function} sortDefinition a dependent key to an - array of sort properties (add `:desc` to the arrays sort properties to sort descending) or a function to use when sorting - @return {Ember.ComputedProperty} computes a new sorted array based - on the sort property array or callback function - */ - function sort(itemsKey, sortDefinition) { - Ember.assert("Ember.computed.sort requires two arguments: an array key to sort and either a sort properties key or sort function", arguments.length === 2); + function mixinsMeta(obj) { + var m = metaFor(obj, true); + var ret = m.mixins; + if (!ret) { + ret = m.mixins = {}; + } else if (!m.hasOwnProperty('mixins')) { + ret = m.mixins = o_create(ret); + } + return ret; + } - var initFn, sortPropertiesKey; + function isMethod(obj) { + return 'function' === typeof obj && + obj.isMethod !== false && + obj !== Boolean && + obj !== Object && + obj !== Number && + obj !== Array && + obj !== Date && + obj !== String; + } - if (typeof sortDefinition === 'function') { - initFn = function (array, changeMeta, instanceMeta) { - instanceMeta.order = sortDefinition; - instanceMeta.binarySearch = binarySearch; - }; - } else { - sortPropertiesKey = sortDefinition; - initFn = function (array, changeMeta, instanceMeta) { - function setupSortProperties() { - var sortPropertyDefinitions = get(this, sortPropertiesKey), - sortProperty, - sortProperties = instanceMeta.sortProperties = [], - sortPropertyAscending = instanceMeta.sortPropertyAscending = {}, - idx, - asc; + var CONTINUE = {}; - Ember.assert("Cannot sort: '" + sortPropertiesKey + "' is not an array.", isArray(sortPropertyDefinitions)); + function mixinProperties(mixinsMeta, mixin) { + var guid; - changeMeta.property.clearItemPropertyKeys(itemsKey); + if (mixin instanceof Mixin) { + guid = guidFor(mixin); + if (mixinsMeta[guid]) { return CONTINUE; } + mixinsMeta[guid] = mixin; + return mixin.properties; + } else { + return mixin; // apply anonymous mixin properties + } + } - forEach(sortPropertyDefinitions, function (sortPropertyDefinition) { - if ((idx = sortPropertyDefinition.indexOf(':')) !== -1) { - sortProperty = sortPropertyDefinition.substring(0, idx); - asc = sortPropertyDefinition.substring(idx+1).toLowerCase() !== 'desc'; - } else { - sortProperty = sortPropertyDefinition; - asc = true; - } + function concatenatedMixinProperties(concatProp, props, values, base) { + var concats; - sortProperties.push(sortProperty); - sortPropertyAscending[sortProperty] = asc; - changeMeta.property.itemPropertyKey(itemsKey, sortProperty); - }); + // reset before adding each new mixin to pickup concats from previous + concats = values[concatProp] || base[concatProp]; + if (props[concatProp]) { + concats = concats ? concats.concat(props[concatProp]) : props[concatProp]; + } - sortPropertyDefinitions.addObserver('@each', this, updateSortPropertiesOnce); - } + return concats; + } - function updateSortPropertiesOnce() { - run.once(this, updateSortProperties, changeMeta.propertyName); - } + function giveDescriptorSuper(meta, key, property, values, descs) { + var superProperty; - function updateSortProperties(propertyName) { - setupSortProperties.call(this); - changeMeta.property.recomputeOnce.call(this, propertyName); - } + // Computed properties override methods, and do not call super to them + if (values[key] === undefined) { + // Find the original descriptor in a parent mixin + superProperty = descs[key]; + } - addObserver(this, sortPropertiesKey, updateSortPropertiesOnce); + // If we didn't find the original descriptor in a parent mixin, find + // it on the original object. + superProperty = superProperty || meta.descs[key]; - setupSortProperties.call(this); + if (superProperty === undefined || !(superProperty instanceof ComputedProperty)) { + return property; + } + // Since multiple mixins may inherit from the same parent, we need + // to clone the computed property so that other mixins do not receive + // the wrapped version. + property = o_create(property); + property.func = wrap(property.func, superProperty.func); - instanceMeta.order = function (itemA, itemB) { - var isProxy = itemB instanceof SearchProxy, - sortProperty, result, asc; + return property; + } - for (var i = 0; i < this.sortProperties.length; ++i) { - sortProperty = this.sortProperties[i]; - result = compare(get(itemA, sortProperty), isProxy ? itemB[sortProperty] : get(itemB, sortProperty)); + var sourceAvailable = (function() { + return this; + }).toString().indexOf('return this;') > -1; - if (result !== 0) { - asc = this.sortPropertyAscending[sortProperty]; - return asc ? result : (-1 * result); - } - } + function giveMethodSuper(obj, key, method, values, descs) { + var superMethod; - return 0; - }; + // Methods overwrite computed properties, and do not call super to them. + if (descs[key] === undefined) { + // Find the original method in a parent mixin + superMethod = values[key]; + } - instanceMeta.binarySearch = binarySearch; - }; + // If we didn't find the original value in a parent mixin, find it in + // the original object + superMethod = superMethod || obj[key]; + + // Only wrap the new method if the original method was a function + if (superMethod === undefined || 'function' !== typeof superMethod) { + return method; } - return arrayComputed(itemsKey, { - initialize: initFn, + var hasSuper; + if (sourceAvailable) { + hasSuper = method.__hasSuper; - addedItem: function (array, item, changeMeta, instanceMeta) { - var index = instanceMeta.binarySearch(array, item); - array.insertAt(index, item); - return array; - }, + if (hasSuper === undefined) { + hasSuper = method.toString().indexOf('_super') > -1; + method.__hasSuper = hasSuper; + } + } - removedItem: function (array, item, changeMeta, instanceMeta) { - var proxyProperties, index, searchItem; + if (sourceAvailable === false || hasSuper) { + return wrap(method, superMethod); + } else { + return method; + } + } - if (changeMeta.previousValues) { - proxyProperties = merge({ content: item }, changeMeta.previousValues); + function applyConcatenatedProperties(obj, key, value, values) { + var baseValue = values[key] || obj[key]; - searchItem = SearchProxy.create(proxyProperties); + if (baseValue) { + if ('function' === typeof baseValue.concat) { + if (value === null || value === undefined) { + return baseValue; } else { - searchItem = item; + return baseValue.concat(value); } - - index = instanceMeta.binarySearch(array, searchItem); - array.removeAt(index); - return array; + } else { + return makeArray(baseValue).concat(value); } - }); - }; + } else { + return makeArray(value); + } + } + function applyMergedProperties(obj, key, value, values) { + var baseValue = values[key] || obj[key]; - __exports__.sum = sum; - __exports__.min = min; - __exports__.max = max; - __exports__.map = map; - __exports__.sort = sort; - __exports__.setDiff = setDiff; - __exports__.mapBy = mapBy; - __exports__.mapProperty = mapProperty; - __exports__.filter = filter; - __exports__.filterBy = filterBy; - __exports__.filterProperty = filterProperty; - __exports__.uniq = uniq; - __exports__.union = union; - __exports__.intersect = intersect; - }); -define("ember-runtime/controllers/array_controller", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/enumerable_utils","ember-runtime/system/array_proxy","ember-runtime/mixins/sortable","ember-runtime/controllers/controller","ember-metal/computed","ember-metal/error","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-runtime - */ + Ember.assert("You passed in `" + JSON.stringify(value) + "` as the value for `" + key + + "` but `" + key + "` cannot be an Array", !isArray(value)); - var Ember = __dependency1__["default"]; - var get = __dependency2__.get; - var set = __dependency3__.set; - var EnumerableUtils = __dependency4__["default"]; - var ArrayProxy = __dependency5__["default"]; - var SortableMixin = __dependency6__["default"]; - var ControllerMixin = __dependency7__.ControllerMixin; - var computed = __dependency8__.computed; - var EmberError = __dependency9__["default"]; + if (!baseValue) { return value; } - var forEach = EnumerableUtils.forEach, - replace = EnumerableUtils.replace; + var newBase = merge({}, baseValue); + var hasFunction = false; - /** - `Ember.ArrayController` provides a way for you to publish a collection of - objects so that you can easily bind to the collection from a Handlebars - `#each` helper, an `Ember.CollectionView`, or other controllers. + for (var prop in value) { + if (!value.hasOwnProperty(prop)) { continue; } - The advantage of using an `ArrayController` is that you only have to set up - your view bindings once; to change what's displayed, simply swap out the - `content` property on the controller. + var propValue = value[prop]; + if (isMethod(propValue)) { + // TODO: support for Computed Properties, etc? + hasFunction = true; + newBase[prop] = giveMethodSuper(obj, prop, propValue, baseValue, {}); + } else { + newBase[prop] = propValue; + } + } - For example, imagine you wanted to display a list of items fetched via an XHR - request. Create an `Ember.ArrayController` and set its `content` property: + if (hasFunction) { + newBase._super = superFunction; + } - ```javascript - MyApp.listController = Ember.ArrayController.create(); + return newBase; + } - $.get('people.json', function(data) { - MyApp.listController.set('content', data); - }); - ``` + function addNormalizedProperty(base, key, value, meta, descs, values, concats, mergings) { + if (value instanceof Descriptor) { + if (value === REQUIRED && descs[key]) { return CONTINUE; } - Then, create a view that binds to your new controller: + // Wrap descriptor function to implement + // __nextSuper() if needed + if (value.func) { + value = giveDescriptorSuper(meta, key, value, values, descs); + } - ```handlebars - {{#each MyApp.listController}} - {{firstName}} {{lastName}} - {{/each}} - ``` + descs[key] = value; + values[key] = undefined; + } else { + if ((concats && a_indexOf.call(concats, key) >= 0) || + key === 'concatenatedProperties' || + key === 'mergedProperties') { + value = applyConcatenatedProperties(base, key, value, values); + } else if ((mergings && a_indexOf.call(mergings, key) >= 0)) { + value = applyMergedProperties(base, key, value, values); + } else if (isMethod(value)) { + value = giveMethodSuper(base, key, value, values, descs); + } - Although you are binding to the controller, the behavior of this controller - is to pass through any methods or properties to the underlying array. This - capability comes from `Ember.ArrayProxy`, which this class inherits from. + descs[key] = undefined; + values[key] = value; + } + } - Sometimes you want to display computed properties within the body of an - `#each` helper that depend on the underlying items in `content`, but are not - present on those items. To do this, set `itemController` to the name of a - controller (probably an `ObjectController`) that will wrap each individual item. + function mergeMixins(mixins, m, descs, values, base, keys) { + var mixin, props, key, concats, mergings, meta; - For example: + function removeKeys(keyName) { + delete descs[keyName]; + delete values[keyName]; + } - ```handlebars - {{#each post in controller}} -
  • {{post.title}} ({{post.titleLength}} characters)
  • - {{/each}} - ``` + for(var i=0, l=mixins.length; i= 0 && idx < length) { - var controllerClass = this.lookupItemController(object); - if (controllerClass) { - return this.controllerAt(idx, object, controllerClass); + binding.connect(obj); + obj[key] = binding; } } + // mark as applied + m.bindings = {}; + } + } - // When `controllerClass` is falsy, we have not opted in to using item - // controllers, so return the object directly. + function finishPartial(obj, m) { + connectBindings(obj, m || metaFor(obj)); + return obj; + } - // When the index is out of range, we want to return the "out of range" - // value, whatever that might be. Rather than make assumptions - // (e.g. guessing `null` or `undefined`) we defer this to `arrangedContent`. - return object; - }, + function followAlias(obj, desc, m, descs, values) { + var altKey = desc.methodName; + var value; + if (descs[altKey] || values[altKey]) { + value = values[altKey]; + desc = descs[altKey]; + } else if (m.descs[altKey]) { + desc = m.descs[altKey]; + value = undefined; + } else { + desc = undefined; + value = obj[altKey]; + } - arrangedContentDidChange: function() { - this._super(); - this._resetSubControllers(); - }, + return { desc: desc, value: value }; + } - arrayContentDidChange: function(idx, removedCnt, addedCnt) { - var subControllers = get(this, '_subControllers'), - subControllersToRemove = subControllers.slice(idx, idx+removedCnt); + function updateObserversAndListeners(obj, key, observerOrListener, pathsKey, updateMethod) { + var paths = observerOrListener[pathsKey]; - forEach(subControllersToRemove, function(subController) { - if (subController) { subController.destroy(); } - }); + if (paths) { + for (var i=0, l=paths.length; i i; i++) { - controller = subControllers[i]; - if (controller) { - controller.destroy(); - } - } + if (desc === undefined && value === undefined) { continue; } - subControllers.length = 0; - } - }, + replaceObserversAndListeners(obj, key, value); + detectBinding(obj, key, value, m); + defineProperty(obj, key, desc, value, m); + } - willDestroy: function() { - this._resetSubControllers(); - this._super(); + if (!partial) { // don't apply to prototype + finishPartial(obj, m); } - }); - __exports__["default"] = ArrayController; - }); -define("ember-runtime/controllers/controller", - ["ember-metal/core","ember-metal/property_get","ember-runtime/system/object","ember-metal/mixin","ember-metal/computed","ember-runtime/mixins/action_handler","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // Ember.assert, Ember.deprecate - var get = __dependency2__.get; - var EmberObject = __dependency3__["default"]; - var Mixin = __dependency4__.Mixin; - var computed = __dependency5__.computed; - var ActionHandler = __dependency6__["default"]; + return obj; + } /** - @module ember - @submodule ember-runtime + @method mixin + @for Ember + @param obj + @param mixins* + @return obj */ + function mixin(obj) { + var args = a_slice.call(arguments, 1); + applyMixin(obj, args, false); + return obj; + } - /** - `Ember.ControllerMixin` provides a standard interface for all classes that - compose Ember's controller layer: `Ember.Controller`, - `Ember.ArrayController`, and `Ember.ObjectController`. + __exports__.mixin = mixin;/** + The `Ember.Mixin` class allows you to create mixins, whose properties can be + added to other classes. For instance, - @class ControllerMixin - @namespace Ember - @uses Ember.ActionHandler - */ - var ControllerMixin = Mixin.create(ActionHandler, { - /* ducktype as a controller */ - isController: true, + ```javascript + App.Editable = Ember.Mixin.create({ + edit: function() { + console.log('starting to edit'); + this.set('isEditing', true); + }, + isEditing: false + }); - /** - The object to which actions from the view should be sent. + // Mix mixins into classes by passing them as the first arguments to + // .extend. + App.CommentView = Ember.View.extend(App.Editable, { + template: Ember.Handlebars.compile('{{#if view.isEditing}}...{{else}}...{{/if}}') + }); - For example, when a Handlebars template uses the `{{action}}` helper, - it will attempt to send the action to the view's controller's `target`. + commentView = App.CommentView.create(); + commentView.edit(); // outputs 'starting to edit' + ``` - By default, the value of the target property is set to the router, and - is injected when a controller is instantiated. This injection is defined - in Ember.Application#buildContainer, and is applied as part of the - applications initialization process. It can also be set after a controller - has been instantiated, for instance when using the render helper in a - template, or when a controller is used as an `itemController`. In most - cases the `target` property will automatically be set to the logical - consumer of actions for the controller. + Note that Mixins are created with `Ember.Mixin.create`, not + `Ember.Mixin.extend`. - @property target - @default null - */ - target: null, + Note that mixins extend a constructor's prototype so arrays and object literals + defined as properties will be shared amongst objects that implement the mixin. + If you want to define a property in a mixin that is not shared, you can define + it either as a computed property or have it be created on initialization of the object. - container: null, + ```javascript + //filters array will be shared amongst any object implementing mixin + App.Filterable = Ember.Mixin.create({ + filters: Ember.A() + }); - parentController: null, + //filters will be a separate array for every object implementing the mixin + App.Filterable = Ember.Mixin.create({ + filters: Ember.computed(function(){return Ember.A();}) + }); - store: null, + //filters will be created as a separate array during the object's initialization + App.Filterable = Ember.Mixin.create({ + init: function() { + this._super(); + this.set("filters", Ember.A()); + } + }); + ``` - model: computed.alias('content'), + @class Mixin + @namespace Ember + */ + __exports__["default"] = Mixin; + function Mixin(args, properties) { + this.properties = properties; - deprecatedSendHandles: function(actionName) { - return !!this[actionName]; - }, + var length = args && args.length; - 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 controllers are deprecated in favor of action handlers on an `actions` object ( action: `' + actionName + '` on ' + this + ')', false); - this[actionName].apply(this, args); - return; + if (length > 0) { + var m = new Array(length); + + for (var i = 0; i < length; i++) { + var x = args[i]; + if (x instanceof Mixin) { + m[i] = x; + } else { + m[i] = new Mixin(undefined, x); + } + } + + this.mixins = m; + } else { + this.mixins = undefined; } - }); + this.ownerConstructor = undefined; + } + + Mixin._apply = applyMixin; + + Mixin.applyPartial = function(obj) { + var args = a_slice.call(arguments, 1); + return applyMixin(obj, args, true); + }; + + Mixin.finishPartial = finishPartial; + + // ES6TODO: this relies on a global state? + Ember.anyUnprocessedMixins = false; /** - @class Controller - @namespace Ember - @extends Ember.Object - @uses Ember.ControllerMixin + @method create + @static + @param arguments* */ - var Controller = EmberObject.extend(ControllerMixin); + Mixin.create = function() { + // ES6TODO: this relies on a global state? + Ember.anyUnprocessedMixins = true; + var M = this; + var length = arguments.length; + var args = new Array(length); + for (var i = 0; i < length; i++) { + args[i] = arguments[i]; + } + return new M(args, undefined); + }; - __exports__.Controller = Controller; - __exports__.ControllerMixin = ControllerMixin; - }); -define("ember-runtime/controllers/object_controller", - ["ember-runtime/controllers/controller","ember-runtime/system/object_proxy","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var ControllerMixin = __dependency1__.ControllerMixin; - var ObjectProxy = __dependency2__["default"]; + var MixinPrototype = Mixin.prototype; /** - @module ember - @submodule ember-runtime + @method reopen + @param arguments* */ + MixinPrototype.reopen = function() { + var mixin; + + if (this.properties) { + mixin = new Mixin(undefined, this.properties); + this.properties = undefined; + this.mixins = [mixin]; + } else if (!this.mixins) { + this.mixins = []; + } + + var len = arguments.length; + var mixins = this.mixins; + var idx; + + for(idx=0; idx < len; idx++) { + mixin = arguments[idx]; + Ember.assert('Expected hash or Mixin instance, got ' + Object.prototype.toString.call(mixin), + typeof mixin === 'object' && mixin !== null && + Object.prototype.toString.call(mixin) !== '[object Array]'); + + if (mixin instanceof Mixin) { + mixins.push(mixin); + } else { + mixins.push(new Mixin(undefined, mixin)); + } + } + + return this; + }; /** - `Ember.ObjectController` is part of Ember's Controller layer. It is intended - to wrap a single object, proxying unhandled attempts to `get` and `set` to the underlying - content object, and to forward unhandled action attempts to its `target`. + @method apply + @param obj + @return applied object + */ + MixinPrototype.apply = function(obj) { + return applyMixin(obj, [this], false); + }; - `Ember.ObjectController` derives this functionality from its superclass - `Ember.ObjectProxy` and the `Ember.ControllerMixin` mixin. + MixinPrototype.applyPartial = function(obj) { + return applyMixin(obj, [this], true); + }; - @class ObjectController - @namespace Ember - @extends Ember.ObjectProxy - @uses Ember.ControllerMixin - **/ - var ObjectController = ObjectProxy.extend(ControllerMixin); - __exports__["default"] = ObjectController; - }); -define("ember-runtime/copy", - ["ember-metal/enumerable_utils","ember-metal/utils","ember-runtime/system/object","ember-runtime/mixins/copyable","ember-metal/platform","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { - "use strict"; - var EnumerableUtils = __dependency1__["default"]; - var typeOf = __dependency2__.typeOf; - var EmberObject = __dependency3__["default"]; - var Copyable = __dependency4__["default"]; - var create = __dependency5__.create; + function _detect(curMixin, targetMixin, seen) { + var guid = guidFor(curMixin); - var indexOf = EnumerableUtils.indexOf; + if (seen[guid]) { return false; } + seen[guid] = true; - function _copy(obj, deep, seen, copies) { - var ret, loc, key; + if (curMixin === targetMixin) { return true; } + var mixins = curMixin.mixins; + var loc = mixins ? mixins.length : 0; + while (--loc >= 0) { + if (_detect(mixins[loc], targetMixin, seen)) { return true; } + } + return false; + } - // primitive data types are immutable, just return them. - if ('object' !== typeof obj || obj===null) return obj; + /** + @method detect + @param obj + @return {Boolean} + */ + MixinPrototype.detect = function(obj) { + if (!obj) { return false; } + if (obj instanceof Mixin) { return _detect(obj, this, {}); } + var m = obj['__ember_meta__']; + var mixins = m && m.mixins; + if (mixins) { + return !!mixins[guidFor(this)]; + } + return false; + }; - // avoid cyclical loops - if (deep && (loc=indexOf(seen, obj))>=0) return copies[loc]; + MixinPrototype.without = function() { + var ret = new Mixin([this]); + ret._without = a_slice.call(arguments); + return ret; + }; - Ember.assert('Cannot clone an Ember.Object that does not implement Ember.Copyable', !(obj instanceof EmberObject) || (Copyable && Copyable.detect(obj))); + function _keys(ret, mixin, seen) { + if (seen[guidFor(mixin)]) { return; } + seen[guidFor(mixin)] = true; - // IMPORTANT: this specific test will detect a native array only. Any other - // object will need to implement Copyable. - if (typeOf(obj) === 'array') { - ret = obj.slice(); - if (deep) { - loc = ret.length; - while(--loc>=0) ret[loc] = _copy(ret[loc], deep, seen, copies); + if (mixin.properties) { + var props = mixin.properties; + for (var key in props) { + if (props.hasOwnProperty(key)) { ret[key] = true; } } - } else if (Copyable && Copyable.detect(obj)) { - ret = obj.copy(deep, seen, copies); - } else if (obj instanceof Date) { - ret = new Date(obj.getTime()); - } else { - ret = {}; - for(key in obj) { - if (!obj.hasOwnProperty(key)) continue; - - // Prevents browsers that don't respect non-enumerability from - // copying internal Ember properties - if (key.substring(0,2) === '__') continue; + } else if (mixin.mixins) { + a_forEach.call(mixin.mixins, function(x) { _keys(ret, x, seen); }); + } + } - ret[key] = deep ? _copy(obj[key], deep, seen, copies) : obj[key]; + MixinPrototype.keys = function() { + var keys = {}; + var seen = {}; + var ret = []; + _keys(keys, this, seen); + for(var key in keys) { + if (keys.hasOwnProperty(key)) { + ret.push(key); } } + return ret; + }; - if (deep) { - seen.push(obj); - copies.push(ret); + // returns the mixins currently applied to the specified object + // TODO: Make Ember.mixin + Mixin.mixins = function(obj) { + var m = obj['__ember_meta__']; + var mixins = m && m.mixins; + var ret = []; + + if (!mixins) { return ret; } + + for (var key in mixins) { + var mixin = mixins[key]; + + // skip primitive mixins since these are always anonymous + if (!mixin.properties) { ret.push(mixin); } } return ret; - } + }; - /** - Creates a clone of the passed object. This function can take just about - any type of object and create a clone of it, including primitive values - (which are not actually cloned because they are immutable). + REQUIRED = new Descriptor(); + REQUIRED.toString = function() { return '(Required Property)'; }; - If the passed object implements the `clone()` method, then this function - will simply call that method and return the result. + /** + Denotes a required property for a mixin - @method copy + @method required @for Ember - @param {Object} obj The object to clone - @param {Boolean} deep If true, a deep copy of the object is made - @return {Object} The cloned object */ - function copy(obj, deep) { - // fast paths - if ('object' !== typeof obj || obj===null) return obj; // can't copy primitives - if (Copyable && Copyable.detect(obj)) return obj.copy(deep); - return _copy(obj, deep, deep ? [] : null, deep ? [] : null); - }; + function required() { + return REQUIRED; + } - __exports__["default"] = copy; - }); -define("ember-runtime/core", - ["exports"], - function(__exports__) { - "use strict"; - /** - @module ember - @submodule ember-runtime - */ + __exports__.required = required;function Alias(methodName) { + this.methodName = methodName; + } + + Alias.prototype = new Descriptor(); /** - Compares two objects, returning true if they are logically equal. This is - a deeper comparison than a simple triple equal. For sets it will compare the - internal objects. For any other object that implements `isEqual()` it will - respect that method. + Makes a method available via an additional name. ```javascript - Ember.isEqual('hello', 'hello'); // true - Ember.isEqual(1, 2); // false - Ember.isEqual([4, 2], [4, 2]); // false + App.Person = Ember.Object.extend({ + name: function() { + return 'Tomhuda Katzdale'; + }, + moniker: Ember.aliasMethod('name') + }); + + var goodGuy = App.Person.create(); + + goodGuy.name(); // 'Tomhuda Katzdale' + goodGuy.moniker(); // 'Tomhuda Katzdale' ``` - @method isEqual + @method aliasMethod @for Ember - @param {Object} a first object to compare - @param {Object} b second object to compare - @return {Boolean} - */ - function isEqual(a, b) { - if (a && 'function'===typeof a.isEqual) return a.isEqual(b); - if (a instanceof Date && b instanceof Date) { - return a.getTime() === b.getTime(); - } - return a === b; - }; - - __exports__.isEqual = isEqual; - }); -define("ember-runtime/ext/function", - ["ember-metal/core","ember-metal/expand_properties","ember-metal/computed"], - function(__dependency1__, __dependency2__, __dependency3__) { - "use strict"; - /** - @module ember - @submodule ember-runtime + @param {String} methodName name of the method to alias + @return {Ember.Descriptor} */ + function aliasMethod(methodName) { + return new Alias(methodName); + } - var Ember = __dependency1__["default"]; - // Ember.EXTEND_PROTOTYPES, Ember.assert - var expandProperties = __dependency2__["default"]; - var computed = __dependency3__.computed; + __exports__.aliasMethod = aliasMethod;// .......................................................... + // OBSERVER HELPER + // - var a_slice = Array.prototype.slice; - var FunctionPrototype = Function.prototype; + /** + Specify a method that observes property changes. - if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) { + ```javascript + Ember.Object.extend({ + valueObserver: Ember.observer('value', function() { + // Executes whenever the "value" property changes + }) + }); + ``` - /** - The `property` extension of Javascript's Function prototype is available - when `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Function` is - `true`, which is the default. + In the future this method may become asynchronous. If you want to ensure + synchronous behavior, use `immediateObserver`. - Computed properties allow you to treat a function like a property: + Also available as `Function.prototype.observes` if prototype extensions are + enabled. - ```javascript - MyApp.President = Ember.Object.extend({ - firstName: '', - lastName: '', + @method observer + @for Ember + @param {String} propertyNames* + @param {Function} func + @return func + */ + function observer() { + var func = a_slice.call(arguments, -1)[0]; + var paths; - fullName: function() { - return this.get('firstName') + ' ' + this.get('lastName'); + var addWatchedProperty = function (path) { paths.push(path); }; + var _paths = a_slice.call(arguments, 0, -1); - // Call this flag to mark the function as a property - }.property() - }); + if (typeof func !== "function") { + // revert to old, soft-deprecated argument ordering - var president = MyApp.President.create({ - firstName: "Barack", - lastName: "Obama" - }); + func = arguments[0]; + _paths = a_slice.call(arguments, 1); + } - president.get('fullName'); // "Barack Obama" - ``` + paths = []; - Treating a function like a property is useful because they can work with - bindings, just like any other property. + for (var i=0; i<_paths.length; ++i) { + expandProperties(_paths[i], addWatchedProperty); + } - Many computed properties have dependencies on other properties. For - example, in the above example, the `fullName` property depends on - `firstName` and `lastName` to determine its value. You can tell Ember - about these dependencies like this: + if (typeof func !== "function") { + throw new Ember.Error("Ember.observer called without a function"); + } - ```javascript - MyApp.President = Ember.Object.extend({ - firstName: '', - lastName: '', + func.__ember_observes__ = paths; + return func; + } - fullName: function() { - return this.get('firstName') + ' ' + this.get('lastName'); + __exports__.observer = observer;/** + Specify a method that observes property changes. - // Tell Ember.js that this computed property depends on firstName - // and lastName - }.property('firstName', 'lastName') - }); - ``` + ```javascript + Ember.Object.extend({ + valueObserver: Ember.immediateObserver('value', function() { + // Executes whenever the "value" property changes + }) + }); + ``` - Make sure you list these dependencies so Ember knows when to update - bindings that connect to a computed property. Changing a dependency - will not immediately trigger an update of the computed property, but - will instead clear the cache so that it is updated when the next `get` - is called on the property. + In the future, `Ember.observer` may become asynchronous. In this event, + `Ember.immediateObserver` will maintain the synchronous behavior. - See [Ember.ComputedProperty](/api/classes/Ember.ComputedProperty.html), [Ember.computed](/api/#method_computed). + Also available as `Function.prototype.observesImmediately` if prototype extensions are + enabled. - @method property - @for Function - */ - FunctionPrototype.property = function() { - var ret = computed(this); - // ComputedProperty.prototype.property expands properties; no need for us to - // do so here. - return ret.property.apply(ret, arguments); - }; - - /** - The `observes` extension of Javascript's Function prototype is available - when `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Function` is - true, which is the default. + @method immediateObserver + @for Ember + @param {String} propertyNames* + @param {Function} func + @return func + */ + function immediateObserver() { + for (var i=0, l=arguments.length; i this.changingFrom ? 'green' : 'red'; + // logic + } + }), - /** - The `observesImmediately` extension of Javascript's Function prototype is - available when `Ember.EXTEND_PROTOTYPES` or - `Ember.EXTEND_PROTOTYPES.Function` is true, which is the default. + friendsDidChange: Ember.observer('friends.@each.name', function(obj, keyName) { + // some logic + // obj.get(keyName) returns friends array + }) + }); + ``` - You can observe property changes simply by adding the `observesImmediately` - call to the end of your method declarations in classes that you write. - For example: + Also available as `Function.prototype.observesBefore` if prototype extensions are + enabled. - ```javascript - Ember.Object.extend({ - valueObserver: function() { - // Executes immediately after the "value" property changes - }.observesImmediately('value') - }); - ``` + @method beforeObserver + @for Ember + @param {String} propertyNames* + @param {Function} func + @return func + */ + function beforeObserver() { + var func = a_slice.call(arguments, -1)[0]; + var paths; - In the future, `observes` may become asynchronous. In this event, - `observesImmediately` will maintain the synchronous behavior. + var addWatchedProperty = function(path) { paths.push(path); }; - See `Ember.immediateObserver`. + var _paths = a_slice.call(arguments, 0, -1); - @method observesImmediately - @for Function - */ - FunctionPrototype.observesImmediately = function() { - for (var i=0, l=arguments.length; i= 0) return; - if (typeof obj.hasOwnProperty === 'function' && !obj.hasOwnProperty(key)) return; - - array.push(key); - }; - keys = function keys(obj) { - var ret = [], key; - for (key in obj) { - pushPropertyName(obj, ret, key); - } - - // IE8 doesn't enumerate property that named the same as prototype properties. - for (var i = 0, l = prototypeProperties.length; i < l; i++) { - key = prototypeProperties[i]; - - pushPropertyName(obj, ret, key); - } - - return ret; - }; - } + var hasPropertyAccessors = hasES5CompliantDefineProperty; + var canDefineNonEnumerableProperties = hasES5CompliantDefineProperty; - __exports__["default"] = keys; - }); -define("ember-runtime", - ["ember-metal","ember-runtime/core","ember-runtime/keys","ember-runtime/compare","ember-runtime/copy","ember-runtime/system/namespace","ember-runtime/system/object","ember-runtime/system/tracked_array","ember-runtime/system/subarray","ember-runtime/system/container","ember-runtime/system/application","ember-runtime/system/array_proxy","ember-runtime/system/object_proxy","ember-runtime/system/core_object","ember-runtime/system/each_proxy","ember-runtime/system/native_array","ember-runtime/system/set","ember-runtime/system/string","ember-runtime/system/deferred","ember-runtime/system/lazy_load","ember-runtime/mixins/array","ember-runtime/mixins/comparable","ember-runtime/mixins/copyable","ember-runtime/mixins/enumerable","ember-runtime/mixins/freezable","ember-runtime/mixins/observable","ember-runtime/mixins/action_handler","ember-runtime/mixins/deferred","ember-runtime/mixins/mutable_enumerable","ember-runtime/mixins/mutable_array","ember-runtime/mixins/target_action_support","ember-runtime/mixins/evented","ember-runtime/mixins/promise_proxy","ember-runtime/mixins/sortable","ember-runtime/computed/array_computed","ember-runtime/computed/reduce_computed","ember-runtime/computed/reduce_computed_macros","ember-runtime/controllers/array_controller","ember-runtime/controllers/object_controller","ember-runtime/controllers/controller","ember-runtime/ext/rsvp","ember-runtime/ext/string","ember-runtime/ext/function","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__, __dependency22__, __dependency23__, __dependency24__, __dependency25__, __dependency26__, __dependency27__, __dependency28__, __dependency29__, __dependency30__, __dependency31__, __dependency32__, __dependency33__, __dependency34__, __dependency35__, __dependency36__, __dependency37__, __dependency38__, __dependency39__, __dependency40__, __dependency41__, __dependency42__, __dependency43__, __exports__) { - "use strict"; /** - Ember Runtime + Platform specific methods and feature detectors needed by the framework. - @module ember - @submodule ember-runtime - @requires ember-metal + @class platform + @namespace Ember + @static */ + __exports__.create = create; + __exports__.defineProperty = defineProperty; + __exports__.defineProperties = defineProperties; + __exports__.hasPropertyAccessors = hasPropertyAccessors; + __exports__.canDefineNonEnumerableProperties = canDefineNonEnumerableProperties; + }); +enifed("ember-metal/platform/create", + ["exports"], + function(__exports__) { + // Remove "use strict"; from transpiled module until + // https://bugs.webkit.org/show_bug.cgi?id=138038 is fixed + // + // REMOVE_USE_STRICT: true - // BEGIN EXPORTS - Ember.compare = __dependency4__["default"]; - Ember.copy = __dependency5__["default"]; - Ember.isEqual = __dependency2__.isEqual; - Ember.keys = __dependency3__["default"]; - - Ember.Array = __dependency21__["default"]; + /** + @class platform + @namespace Ember + @static + */ - Ember.Comparable = __dependency22__["default"]; - Ember.Copyable = __dependency23__["default"]; + /** + Identical to `Object.create()`. Implements if not available natively. - Ember.SortableMixin = __dependency34__["default"]; + @method create + @for Ember + */ + var create; + // ES5 15.2.3.5 + // http://es5.github.com/#x15.2.3.5 + if (!(Object.create && !Object.create(null).hasOwnProperty)) { + /* jshint scripturl:true, proto:true */ + // Contributed by Brandon Benvie, October, 2012 + var createEmpty; + var supportsProto = !({'__proto__':null} instanceof Object); + // the following produces false positives + // in Opera Mini => not a reliable check + // 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; + + function Empty() {} + Empty.prototype = empty; + // short-circuit future calls + createEmpty = function () { + return new Empty(); + }; + return new Empty(); + }; + } - Ember.Freezable = __dependency25__.Freezable; - Ember.FROZEN_ERROR = __dependency25__.FROZEN_ERROR; + create = Object.create = function create(prototype, properties) { - Ember.DeferredMixin = __dependency28__["default"]; + var object; + function Type() {} // An empty constructor. - Ember.MutableEnumerable = __dependency29__["default"]; - Ember.MutableArray = __dependency30__["default"]; + if (prototype === null) { + object = createEmpty(); + } 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 + } - Ember.TargetActionSupport = __dependency31__["default"]; - Ember.Evented = __dependency32__["default"]; + Type.prototype = prototype; - Ember.PromiseProxyMixin = __dependency33__["default"]; + object = new Type(); + } - Ember.Observable = __dependency26__["default"]; + if (properties !== undefined) { + Object.defineProperties(object, properties); + } - Ember.arrayComputed = __dependency35__.arrayComputed; - Ember.ArrayComputedProperty = __dependency35__.ArrayComputedProperty; - Ember.reduceComputed = __dependency36__.reduceComputed; - Ember.ReduceComputedProperty = __dependency36__.ReduceComputedProperty; + return object; + }; + } else { + create = Object.create; + } - // ES6TODO: this seems a less than ideal way/place to add properties to Ember.computed - var EmComputed = Ember.computed; + __exports__["default"] = create; + }); +enifed("ember-metal/platform/define_properties", + ["ember-metal/platform/define_property","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var defineProperty = __dependency1__.defineProperty; - EmComputed.sum = __dependency37__.sum; - EmComputed.min = __dependency37__.min; - EmComputed.max = __dependency37__.max; - EmComputed.map = __dependency37__.map; - EmComputed.sort = __dependency37__.sort; - EmComputed.setDiff = __dependency37__.setDiff; - EmComputed.mapBy = __dependency37__.mapBy; - EmComputed.mapProperty = __dependency37__.mapProperty; - EmComputed.filter = __dependency37__.filter; - EmComputed.filterBy = __dependency37__.filterBy; - EmComputed.filterProperty = __dependency37__.filterProperty; - EmComputed.uniq = __dependency37__.uniq; - EmComputed.union = __dependency37__.union; - EmComputed.intersect = __dependency37__.intersect; - - Ember.String = __dependency18__["default"]; - Ember.Object = __dependency7__["default"]; - Ember.TrackedArray = __dependency8__["default"]; - Ember.SubArray = __dependency9__["default"]; - Ember.Container = __dependency10__["default"]; - Ember.Namespace = __dependency6__["default"]; - Ember.Application = __dependency11__["default"]; - Ember.Enumerable = __dependency24__["default"]; - Ember.ArrayProxy = __dependency12__["default"]; - Ember.ObjectProxy = __dependency13__["default"]; - Ember.ActionHandler = __dependency27__["default"]; - Ember.CoreObject = __dependency14__["default"]; - Ember.EachArray = __dependency15__.EachArray; - Ember.EachProxy = __dependency15__.EachProxy; - Ember.NativeArray = __dependency16__["default"]; - // ES6TODO: Currently we must rely on the global from ember-metal/core to avoid circular deps - // Ember.A = A; - Ember.Set = __dependency17__["default"]; - Ember.Deferred = __dependency19__["default"]; - Ember.onLoad = __dependency20__.onLoad; - Ember.runLoadHooks = __dependency20__.runLoadHooks; + var defineProperties = Object.defineProperties; - Ember.ArrayController = __dependency38__["default"]; - Ember.ObjectController = __dependency39__["default"]; - Ember.Controller = __dependency40__.Controller; - Ember.ControllerMixin = __dependency40__.ControllerMixin; + // ES5 15.2.3.7 + // http://es5.github.com/#x15.2.3.7 + if (!defineProperties) { + defineProperties = function defineProperties(object, properties) { + for (var property in properties) { + if (properties.hasOwnProperty(property) && property !== "__proto__") { + defineProperty(object, property, properties[property]); + } + } + return object; + }; - Ember.RSVP = __dependency41__["default"]; - // END EXPORTS + Object.defineProperties = defineProperties; + } - __exports__["default"] = Ember; + __exports__["default"] = defineProperties; }); -define("ember-runtime/mixins/action_handler", - ["ember-metal/merge","ember-metal/mixin","ember-metal/property_get","ember-metal/utils","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { +enifed("ember-metal/platform/define_property", + ["exports"], + function(__exports__) { "use strict"; + /*globals Node */ + /** - @module ember - @submodule ember-runtime + @class platform + @namespace Ember + @static */ - var merge = __dependency1__["default"]; - var Mixin = __dependency2__.Mixin; - var get = __dependency3__.get; - var typeOf = __dependency4__.typeOf; /** - The `Ember.ActionHandler` mixin implements support for moving an `actions` - property to an `_actions` property at extend time, and adding `_actions` - to the object's mergedProperties list. - - `Ember.ActionHandler` is available on some familiar classes including - `Ember.Route`, `Ember.View`, `Ember.Component`, and controllers such as - `Ember.Controller` and `Ember.ObjectController`. - (Internally the mixin is used by `Ember.CoreView`, `Ember.ControllerMixin`, - and `Ember.Route` and available to the above classes through - inheritance.) + Set to true if the platform supports native getters and setters. - @class ActionHandler - @namespace Ember + @property hasPropertyAccessors + @final */ - var ActionHandler = Mixin.create({ - mergedProperties: ['_actions'], - /** - The collection of functions, keyed by name, available on this - `ActionHandler` as action targets. + /** + Identical to `Object.defineProperty()`. Implements as much functionality + as possible if not available natively. - These functions will be invoked when a matching `{{action}}` is triggered - from within a template and the application's current route is this route. + @method defineProperty + @param {Object} obj The object to modify + @param {String} keyName property name to modify + @param {Object} desc descriptor hash + @return {void} + */ + var defineProperty = (function checkCompliance(defineProperty) { + if (!defineProperty) return; + try { + var a = 5; + var obj = {}; + defineProperty(obj, 'a', { + configurable: true, + enumerable: true, + get: function () { + return a; + }, + set: function (v) { + a = v; + } + }); + if (obj.a !== 5) return; + obj.a = 10; + if (a !== 10) return; - Actions can also be invoked from other parts of your application - via `ActionHandler#send`. + // check non-enumerability + defineProperty(obj, 'a', { + configurable: true, + enumerable: false, + writable: true, + value: true + }); + for (var key in obj) { + if (key === 'a') return; + } - The `actions` hash will inherit action handlers from - the `actions` hash defined on extended parent classes - or mixins rather than just replace the entire hash, e.g.: + // Detects a bug in Android <3.2 where you cannot redefine a property using + // Object.defineProperty once accessors have already been set. + if (obj.a !== true) return; - ```js - App.CanDisplayBanner = Ember.Mixin.create({ - actions: { - displayBanner: function(msg) { - // ... - } - } + // Detects a bug in Android <3 where redefining a property without a value changes the value + // Object.defineProperty once accessors have already been set. + defineProperty(obj, 'a', { + enumerable: false }); + if (obj.a !== true) return; - App.WelcomeRoute = Ember.Route.extend(App.CanDisplayBanner, { - actions: { - playMusic: function() { - // ... - } - } - }); + // defineProperty is compliant + return defineProperty; + } catch (e) { + // IE8 defines Object.defineProperty but calling it on an Object throws + return; + } + })(Object.defineProperty); - // `WelcomeRoute`, when active, will be able to respond - // to both actions, since the actions hash is merged rather - // then replaced when extending mixins / parent classes. - this.send('displayBanner'); - this.send('playMusic'); - ``` + var hasES5CompliantDefineProperty = !!defineProperty; - Within a Controller, Route, View or Component's action handler, - the value of the `this` context is the Controller, Route, View or - Component object: + if (hasES5CompliantDefineProperty && typeof document !== 'undefined') { + // This is for Safari 5.0, which supports Object.defineProperty, but not + // on DOM nodes. + var canDefinePropertyOnDOM = (function() { + try { + defineProperty(document.createElement('div'), 'definePropertyOnDOM', {}); + return true; + } catch(e) { } - ```js - App.SongRoute = Ember.Route.extend({ - actions: { - myAction: function() { - this.controllerFor("song"); - this.transitionTo("other.route"); - ... - } - } - }); - ``` + return false; + })(); - It is also possible to call `this._super()` from within an - action handler if it overrides a handler defined on a parent - class or mixin: + if (!canDefinePropertyOnDOM) { + defineProperty = function(obj, keyName, desc) { + var isNode; - Take for example the following routes: + if (typeof Node === "object") { + isNode = obj instanceof Node; + } else { + isNode = typeof obj === "object" && typeof obj.nodeType === "number" && typeof obj.nodeName === "string"; + } - ```js - App.DebugRoute = Ember.Mixin.create({ - actions: { - debugRouteInformation: function() { - console.debug("trololo"); - } + if (isNode) { + // TODO: Should we have a warning here? + return (obj[keyName] = desc.value); + } else { + return Object.defineProperty(obj, keyName, desc); } - }); + }; + } + } - App.AnnoyingDebugRoute = Ember.Route.extend(App.DebugRoute, { - actions: { - debugRouteInformation: function() { - // also call the debugRouteInformation of mixed in App.DebugRoute - this._super(); + if (!hasES5CompliantDefineProperty) { + defineProperty = function defineProperty(obj, keyName, desc) { + if (!desc.get) { obj[keyName] = desc.value; } + }; + } - // show additional annoyance - window.alert(...); - } - } - }); - ``` + __exports__.hasES5CompliantDefineProperty = hasES5CompliantDefineProperty; + __exports__.defineProperty = defineProperty; + }); +enifed("ember-metal/properties", + ["ember-metal/core","ember-metal/utils","ember-metal/platform","ember-metal/property_events","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + /** + @module ember-metal + */ - ## Bubbling + var Ember = __dependency1__["default"]; + var metaFor = __dependency2__.meta; + var objectDefineProperty = __dependency3__.defineProperty; + var hasPropertyAccessors = __dependency3__.hasPropertyAccessors; + var overrideChains = __dependency4__.overrideChains; + // .......................................................... + // DESCRIPTOR + // - By default, an action will stop bubbling once a handler defined - on the `actions` hash handles it. To continue bubbling the action, - you must return `true` from the handler: + /** + Objects of this type can implement an interface to respond to requests to + get and set. The default implementation handles simple properties. - ```js - App.Router.map(function() { - this.resource("album", function() { - this.route("song"); - }); - }); + You generally won't need to create or subclass this directly. - App.AlbumRoute = Ember.Route.extend({ - actions: { - startPlaying: function() { - } - } - }); + @class Descriptor + @namespace Ember + @private + @constructor + */ + function Descriptor() {} - App.AlbumSongRoute = Ember.Route.extend({ - actions: { - startPlaying: function() { - // ... + __exports__.Descriptor = Descriptor;// .......................................................... + // DEFINING PROPERTIES API + // - if (actionShouldAlsoBeTriggeredOnParentRoute) { - return true; - } - } - } - }); - ``` + function MANDATORY_SETTER_FUNCTION(name) { + return function SETTER_FUNCTION(value) { + Ember.assert("You must use Ember.set() to set the `" + name + "` property (of " + this + ") to `" + value + "`.", false); + }; + } - @property actions - @type Hash - @default null - */ + __exports__.MANDATORY_SETTER_FUNCTION = MANDATORY_SETTER_FUNCTION;function DEFAULT_GETTER_FUNCTION(name) { + return function GETTER_FUNCTION() { + var meta = this['__ember_meta__']; + return meta && meta.values[name]; + }; + } - /** - Moves `actions` to `_actions` at extend time. Note that this currently - modifies the mixin themselves, which is technically dubious but - is practically of little consequence. This may change in the future. + __exports__.DEFAULT_GETTER_FUNCTION = DEFAULT_GETTER_FUNCTION;/** + NOTE: This is a low-level method used by other parts of the API. You almost + never want to call this method directly. Instead you should use + `Ember.mixin()` to define new properties. - @private - @method willMergeMixin - */ - willMergeMixin: function(props) { - var hashName; + Defines a property on an object. This method works much like the ES5 + `Object.defineProperty()` method except that it can also accept computed + properties and other special descriptors. - if (!props._actions) { - Ember.assert("'actions' should not be a function", typeof(props.actions) !== 'function'); + Normally this method takes only three parameters. However if you pass an + instance of `Ember.Descriptor` as the third param then you can pass an + optional value as the fourth parameter. This is often more efficient than + creating new descriptor hashes for each property. - if (typeOf(props.actions) === 'object') { - hashName = 'actions'; - } else if (typeOf(props.events) === 'object') { - Ember.deprecate('Action handlers contained in an `events` object are deprecated in favor of putting them in an `actions` object', false); - hashName = 'events'; - } + ## Examples - if (hashName) { - props._actions = merge(props._actions || {}, props[hashName]); - } + ```javascript + // ES5 compatible mode + Ember.defineProperty(contact, 'firstName', { + writable: true, + configurable: false, + enumerable: true, + value: 'Charles' + }); - delete props[hashName]; - } - }, + // define a simple property + Ember.defineProperty(contact, 'lastName', undefined, 'Jolley'); - /** - Triggers a named action on the `ActionHandler`. Any parameters - supplied after the `actionName` string will be passed as arguments - to the action target function. + // define a computed property + Ember.defineProperty(contact, 'fullName', Ember.computed(function() { + return this.firstName+' '+this.lastName; + }).property('firstName', 'lastName')); + ``` - If the `ActionHandler` has its `target` property set, actions may - bubble to the `target`. Bubbling happens when an `actionName` can - not be found in the `ActionHandler`'s `actions` hash or if the - action target function returns `true`. + @private + @method defineProperty + @for Ember + @param {Object} obj the object to define this property on. This may be a prototype. + @param {String} keyName the name of the property + @param {Ember.Descriptor} [desc] an instance of `Ember.Descriptor` (typically a + computed property) or an ES5 descriptor. + You must provide this or `data` but not both. + @param {*} [data] something other than a descriptor, that will + become the explicit value of this property. + */ + function defineProperty(obj, keyName, desc, data, meta) { + var descs, existingDesc, watching, value; - Example + if (!meta) meta = metaFor(obj); + descs = meta.descs; + existingDesc = meta.descs[keyName]; + var watchEntry = meta.watching[keyName]; - ```js - App.WelcomeRoute = Ember.Route.extend({ - actions: { - playTheme: function() { - this.send('playMusic', 'theme.mp3'); - }, - playMusic: function(track) { - // ... - } - } - }); - ``` + watching = watchEntry !== undefined && watchEntry > 0; - @method send - @param {String} actionName The action to trigger - @param {*} context a context to send with the action - */ - send: function(actionName) { - var args = [].slice.call(arguments, 1), target; + if (existingDesc instanceof Descriptor) { + existingDesc.teardown(obj, keyName); + } - if (this._actions && this._actions[actionName]) { - if (this._actions[actionName].apply(this, args) === true) { - // handler returned true, so this action will bubble - } else { - return; - } - } else if (!Ember.FEATURES.isEnabled('ember-routing-drop-deprecated-action-style') && this.deprecatedSend && this.deprecatedSendHandles && this.deprecatedSendHandles(actionName)) { - Ember.warn("The current default is deprecated but will prefer to handle actions directly on the controller instead of a similarly named action in the actions hash. To turn off this deprecated feature set: Ember.FEATURES['ember-routing-drop-deprecated-action-style'] = true"); - if (this.deprecatedSend.apply(this, [].slice.call(arguments)) === true) { - // handler return true, so this action will bubble + if (desc instanceof Descriptor) { + value = desc; + + descs[keyName] = desc; + + if (watching && hasPropertyAccessors) { + objectDefineProperty(obj, keyName, { + configurable: true, + enumerable: true, + writable: true, + value: undefined // make enumerable + }); } else { - return; + obj[keyName] = undefined; // make enumerable } - } + if (desc.setup) { desc.setup(obj, keyName); } + } else { + descs[keyName] = undefined; // shadow descriptor in proto + if (desc == null) { + value = data; - if (target = get(this, 'target')) { - Ember.assert("The `target` for " + this + " (" + target + ") does not have a `send` method", typeof target.send === 'function'); - target.send.apply(target, arguments); + + if (watching && hasPropertyAccessors) { + meta.values[keyName] = data; + objectDefineProperty(obj, keyName, { + configurable: true, + enumerable: true, + set: MANDATORY_SETTER_FUNCTION(keyName), + get: DEFAULT_GETTER_FUNCTION(keyName) + }); + } else { + obj[keyName] = data; + } + } else { + value = desc; + + // compatibility with ES5 + objectDefineProperty(obj, keyName, desc); } } - }); - __exports__["default"] = ActionHandler; + // if key is being watched, override chains that + // were initialized with the prototype + if (watching) { overrideChains(obj, keyName, meta); } + + // The `value` passed to the `didDefineProperty` hook is + // either the descriptor or data, whichever was passed. + if (obj.didDefineProperty) { obj.didDefineProperty(obj, keyName, value); } + + return this; + } + + __exports__.defineProperty = defineProperty; }); -define("ember-runtime/mixins/array", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/computed","ember-metal/is_none","ember-runtime/mixins/enumerable","ember-metal/enumerable_utils","ember-metal/mixin","ember-metal/property_events","ember-metal/events","ember-metal/watching","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { +enifed("ember-metal/property_events", + ["ember-metal/utils","ember-metal/events","ember-metal/observer_set","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; - /** - @module ember - @submodule ember-runtime - */ + var guidFor = __dependency1__.guidFor; + var tryFinally = __dependency1__.tryFinally; + var sendEvent = __dependency2__.sendEvent; + var accumulateListeners = __dependency2__.accumulateListeners; + var ObserverSet = __dependency3__["default"]; + + var beforeObserverSet = new ObserverSet(); + var observerSet = new ObserverSet(); + var deferred = 0; // .......................................................... - // HELPERS + // PROPERTY CHANGES // - var Ember = __dependency1__["default"]; - // ES6TODO: Ember.A - var get = __dependency2__.get; - var set = __dependency3__.set; - var computed = __dependency4__.computed; - var cacheFor = __dependency4__.cacheFor; - var isNone = __dependency5__.isNone; - var none = __dependency5__.none; - var Enumerable = __dependency6__["default"]; - var EnumerableUtils = __dependency7__["default"]; - var Mixin = __dependency8__.Mixin; - var required = __dependency8__.required; - var propertyWillChange = __dependency9__.propertyWillChange; - var propertyDidChange = __dependency9__.propertyDidChange; - var addListener = __dependency10__.addListener; - var removeListener = __dependency10__.removeListener; - var sendEvent = __dependency10__.sendEvent; - var hasListeners = __dependency10__.hasListeners; - var isWatching = __dependency11__.isWatching; + /** + This function is called just before an object property is about to change. + It will notify any before observers and prepare caches among other things. - var map = EnumerableUtils.map; + Normally you will not need to call this method directly but if for some + reason you can't directly watch a property you can invoke this method + manually along with `Ember.propertyDidChange()` which you should call just + after the property value changes. - // .......................................................... - // ARRAY - // - /** - This mixin implements Observer-friendly Array-like behavior. It is not a - concrete implementation, but it can be used up by other classes that want - to appear like arrays. + @method propertyWillChange + @for Ember + @param {Object} obj The object with the property that will change + @param {String} keyName The property key (or path) that will change. + @return {void} + */ + function propertyWillChange(obj, keyName) { + var m = obj['__ember_meta__']; + var watching = (m && m.watching[keyName] > 0) || keyName === 'length'; + var proto = m && m.proto; + var desc = m && m.descs[keyName]; - For example, ArrayProxy and ArrayController are both concrete classes that can - be instantiated to implement array-like behavior. Both of these classes use - the Array Mixin by way of the MutableArray mixin, which allows observable - changes to be made to the underlying array. + if (!watching) { + return; + } - Unlike `Ember.Enumerable,` this mixin defines methods specifically for - collections that provide index-ordered access to their contents. When you - are designing code that needs to accept any kind of Array-like object, you - should use these methods instead of Array primitives because these will - properly notify observers of changes to the array. + if (proto === obj) { + return; + } - Although these methods are efficient, they do add a layer of indirection to - your application so it is a good idea to use them only when you need the - flexibility of using both true JavaScript arrays and "virtual" arrays such - as controllers and collections. + if (desc && desc.willChange) { + desc.willChange(obj, keyName); + } - You can use the methods defined in this module to access and modify array - contents in a KVO-friendly way. You can also be notified whenever the - membership of an array changes by using `.observes('myArray.[]')`. + dependentKeysWillChange(obj, keyName, m); + chainsWillChange(obj, keyName, m); + notifyBeforeObservers(obj, keyName); + } - To support `Ember.Array` in your own class, you must override two - primitives to use it: `replace()` and `objectAt()`. + /** + This function is called just after an object property has changed. + It will notify any observers and clear caches among other things. - Note that the Ember.Array mixin also incorporates the `Ember.Enumerable` - mixin. All `Ember.Array`-like objects are also enumerable. + Normally you will not need to call this method directly but if for some + reason you can't directly watch a property you can invoke this method + manually along with `Ember.propertyWillChange()` which you should call just + before the property value changes. - @class Array - @namespace Ember - @uses Ember.Enumerable - @since Ember 0.9.0 + @method propertyDidChange + @for Ember + @param {Object} obj The object with the property that will change + @param {String} keyName The property key (or path) that will change. + @return {void} */ - var EmberArray = Mixin.create(Enumerable, { - - /** - Your array must support the `length` property. Your replace methods should - set this property whenever it changes. + function propertyDidChange(obj, keyName) { + var m = obj['__ember_meta__']; + var watching = (m && m.watching[keyName] > 0) || keyName === 'length'; + var proto = m && m.proto; + var desc = m && m.descs[keyName]; - @property {Number} length - */ - length: required(), + if (proto === obj) { + return; + } - /** - Returns the object at the given `index`. If the given `index` is negative - or is greater or equal than the array length, returns `undefined`. + // shouldn't this mean that we're watching this key? + if (desc && desc.didChange) { + desc.didChange(obj, keyName); + } - This is one of the primitives you must implement to support `Ember.Array`. - If your object supports retrieving the value of an array item using `get()` - (i.e. `myArray.get(0)`), then you do not need to implement this method - yourself. + if (!watching && keyName !== 'length') { + return; + } - ```javascript - var arr = ['a', 'b', 'c', 'd']; - arr.objectAt(0); // "a" - arr.objectAt(3); // "d" - arr.objectAt(-1); // undefined - arr.objectAt(4); // undefined - arr.objectAt(5); // undefined - ``` + if (m && m.deps && m.deps[keyName]) { + dependentKeysDidChange(obj, keyName, m); + } - @method objectAt - @param {Number} idx The index of the item to return. - @return {*} item at index or undefined - */ - objectAt: function(idx) { - if ((idx < 0) || (idx >= get(this, 'length'))) return undefined; - return get(this, idx); - }, + chainsDidChange(obj, keyName, m, false); + notifyObservers(obj, keyName); + } - /** - This returns the objects at the specified indexes, using `objectAt`. + var WILL_SEEN, DID_SEEN; + // called whenever a property is about to change to clear the cache of any dependent keys (and notify those properties of changes, etc...) + function dependentKeysWillChange(obj, depKey, meta) { + if (obj.isDestroying) { return; } - ```javascript - var arr = ['a', 'b', 'c', 'd']; - arr.objectsAt([0, 1, 2]); // ["a", "b", "c"] - arr.objectsAt([2, 3, 4]); // ["c", "d", undefined] - ``` + var deps; + if (meta && meta.deps && (deps = meta.deps[depKey])) { + var seen = WILL_SEEN; + var top = !seen; - @method objectsAt - @param {Array} indexes An array of indexes of items to return. - @return {Array} - */ - objectsAt: function(indexes) { - var self = this; - return map(indexes, function(idx) { return self.objectAt(idx); }); - }, + if (top) { + seen = WILL_SEEN = {}; + } - // overrides Ember.Enumerable version - nextObject: function(idx) { - return this.objectAt(idx); - }, + iterDeps(propertyWillChange, obj, deps, depKey, seen, meta); - /** - This is the handler for the special array content property. If you get - this property, it will return this. If you set this property it a new - array, it will replace the current content. + if (top) { + WILL_SEEN = null; + } + } + } - This property overrides the default property defined in `Ember.Enumerable`. + // called whenever a property has just changed to update dependent keys + function dependentKeysDidChange(obj, depKey, meta) { + if (obj.isDestroying) { return; } - @property [] - @return this - */ - '[]': computed(function(key, value) { - if (value !== undefined) this.replace(0, get(this, 'length'), value) ; - return this ; - }), + var deps; + if (meta && meta.deps && (deps = meta.deps[depKey])) { + var seen = DID_SEEN; + var top = !seen; - firstObject: computed(function() { - return this.objectAt(0); - }), + if (top) { + seen = DID_SEEN = {}; + } - lastObject: computed(function() { - return this.objectAt(get(this, 'length')-1); - }), + iterDeps(propertyDidChange, obj, deps, depKey, seen, meta); - // optimized version from Enumerable - contains: function(obj) { - return this.indexOf(obj) >= 0; - }, + if (top) { + DID_SEEN = null; + } + } + } - // Add any extra methods to Ember.Array that are native to the built-in Array. - /** - Returns a new array that is a slice of the receiver. This implementation - uses the observable array methods to retrieve the objects for the new - slice. + function keysOf(obj) { + var keys = []; - ```javascript - var arr = ['red', 'green', 'blue']; - arr.slice(0); // ['red', 'green', 'blue'] - arr.slice(0, 2); // ['red', 'green'] - arr.slice(1, 100); // ['green', 'blue'] - ``` + for (var key in obj) { + keys.push(key); + } - @method slice - @param {Integer} beginIndex (Optional) index to begin slicing from. - @param {Integer} endIndex (Optional) index to end the slice at (but not included). - @return {Array} New array with specified slice - */ - slice: function(beginIndex, endIndex) { - var ret = Ember.A(); - var length = get(this, 'length') ; - if (isNone(beginIndex)) beginIndex = 0 ; - if (isNone(endIndex) || (endIndex > length)) endIndex = length ; + return keys; + } - if (beginIndex < 0) beginIndex = length + beginIndex; - if (endIndex < 0) endIndex = length + endIndex; + function iterDeps(method, obj, deps, depKey, seen, meta) { + var keys, key, i, desc; + var guid = guidFor(obj); + var current = seen[guid]; - while(beginIndex < endIndex) { - ret[ret.length] = this.objectAt(beginIndex++) ; - } - return ret ; - }, + if (!current) { + current = seen[guid] = {}; + } - /** - Returns the index of the given object's first occurrence. - If no `startAt` argument is given, the starting location to - search is 0. If it's negative, will count backward from - the end of the array. Returns -1 if no match is found. + if (current[depKey]) { + return; + } - ```javascript - var arr = ["a", "b", "c", "d", "a"]; - arr.indexOf("a"); // 0 - arr.indexOf("z"); // -1 - arr.indexOf("a", 2); // 4 - arr.indexOf("a", -1); // 4 - arr.indexOf("b", 3); // -1 - arr.indexOf("a", 100); // -1 - ``` + current[depKey] = true; - @method indexOf - @param {Object} object the item to search for - @param {Number} startAt optional starting location to search, default 0 - @return {Number} index or -1 if not found - */ - indexOf: function(object, startAt) { - var idx, len = get(this, 'length'); + if (deps) { + keys = keysOf(deps); + var descs = meta.descs; + for (i=0; i= len) startAt = len-1; - if (startAt < 0) startAt += len; + for(i = 0, l = nodes.length; i < l; i++) { + nodes[i].willChange(events); + } - for(idx = startAt; idx >= 0; idx--) { - if (this.objectAt(idx) === object) return idx; - } - return -1; - }, + for (i = 0, l = events.length; i < l; i += 2) { + propertyWillChange(events[i], events[i+1]); + } + } - // .......................................................... - // ARRAY OBSERVERS - // + function chainsDidChange(obj, keyName, m, suppressEvents) { + if (!(m && m.hasOwnProperty('chainWatchers') && + m.chainWatchers[keyName])) { + return; + } - /** - Adds an array observer to the receiving array. The array observer object - normally must implement two methods: + var nodes = m.chainWatchers[keyName]; + var events = suppressEvents ? null : []; + var i, l; - * `arrayWillChange(observedObj, start, removeCount, addCount)` - This method will be - called just before the array is modified. - * `arrayDidChange(observedObj, start, removeCount, addCount)` - This method will be - called just after the array is modified. + for(i = 0, l = nodes.length; i < l; i++) { + nodes[i].didChange(events); + } - Both callbacks will be passed the observed object, starting index of the - change as well a a count of the items to be removed and added. You can use - these callbacks to optionally inspect the array during the change, clear - caches, or do any other bookkeeping necessary. + if (suppressEvents) { + return; + } - In addition to passing a target, you can also include an options hash - which you can use to override the method names that will be invoked on the - target. + for (i = 0, l = events.length; i < l; i += 2) { + propertyDidChange(events[i], events[i+1]); + } + } - @method addArrayObserver - @param {Object} target The observer object. - @param {Hash} opts Optional hash of configuration options including - `willChange` and `didChange` option. - @return {Ember.Array} receiver - */ - addArrayObserver: function(target, opts) { - var willChange = (opts && opts.willChange) || 'arrayWillChange', - didChange = (opts && opts.didChange) || 'arrayDidChange'; - - var hasObservers = get(this, 'hasArrayObservers'); - if (!hasObservers) propertyWillChange(this, 'hasArrayObservers'); - addListener(this, '@array:before', target, willChange); - addListener(this, '@array:change', target, didChange); - if (!hasObservers) propertyDidChange(this, 'hasArrayObservers'); - return this; - }, + function overrideChains(obj, keyName, m) { + chainsDidChange(obj, keyName, m, true); + } - /** - Removes an array observer from the object if the observer is current - registered. Calling this method multiple times with the same object will - have no effect. + /** + @method beginPropertyChanges + @chainable + @private + */ + function beginPropertyChanges() { + deferred++; + } - @method removeArrayObserver - @param {Object} target The object observing the array. - @param {Hash} opts Optional hash of configuration options including - `willChange` and `didChange` option. - @return {Ember.Array} receiver - */ - removeArrayObserver: function(target, opts) { - var willChange = (opts && opts.willChange) || 'arrayWillChange', - didChange = (opts && opts.didChange) || 'arrayDidChange'; - - var hasObservers = get(this, 'hasArrayObservers'); - if (hasObservers) propertyWillChange(this, 'hasArrayObservers'); - removeListener(this, '@array:before', target, willChange); - removeListener(this, '@array:change', target, didChange); - if (hasObservers) propertyDidChange(this, 'hasArrayObservers'); - return this; - }, + /** + @method endPropertyChanges + @private + */ + function endPropertyChanges() { + deferred--; + if (deferred<=0) { + beforeObserverSet.clear(); + observerSet.flush(); + } + } - /** - Becomes true whenever the array currently has observers watching changes - on the array. + /** + Make a series of property changes together in an + exception-safe way. - @property {Boolean} hasArrayObservers - */ - hasArrayObservers: computed(function() { - return hasListeners(this, '@array:change') || hasListeners(this, '@array:before'); - }), + ```javascript + Ember.changeProperties(function() { + obj1.set('foo', mayBlowUpWhenSet); + obj2.set('bar', baz); + }); + ``` - /** - If you are implementing an object that supports `Ember.Array`, call this - method just before the array content changes to notify any observers and - invalidate any related properties. Pass the starting index of the change - as well as a delta of the amounts to change. + @method changeProperties + @param {Function} callback + @param [binding] + */ + function changeProperties(callback, binding) { + beginPropertyChanges(); + tryFinally(callback, endPropertyChanges, binding); + } - @method arrayContentWillChange - @param {Number} startIdx The starting index in the array that will change. - @param {Number} removeAmt The number of items that will be removed. If you - pass `null` assumes 0 - @param {Number} addAmt The number of items that will be added. If you - pass `null` assumes 0. - @return {Ember.Array} receiver - */ - arrayContentWillChange: function(startIdx, removeAmt, addAmt) { + function notifyBeforeObservers(obj, keyName) { + if (obj.isDestroying) { return; } - // if no args are passed assume everything changes - if (startIdx===undefined) { - startIdx = 0; - removeAmt = addAmt = -1; - } else { - if (removeAmt === undefined) removeAmt=-1; - if (addAmt === undefined) addAmt=-1; - } + var eventName = keyName + ':before'; + var listeners, added; + if (deferred) { + listeners = beforeObserverSet.add(obj, keyName, eventName); + added = accumulateListeners(obj, eventName, listeners); + sendEvent(obj, eventName, [obj, keyName], added); + } else { + sendEvent(obj, eventName, [obj, keyName]); + } + } - // Make sure the @each proxy is set up if anyone is observing @each - if (isWatching(this, '@each')) { get(this, '@each'); } + function notifyObservers(obj, keyName) { + if (obj.isDestroying) { return; } - sendEvent(this, '@array:before', [this, startIdx, removeAmt, addAmt]); + var eventName = keyName + ':change'; + var listeners; + if (deferred) { + listeners = observerSet.add(obj, keyName, eventName); + accumulateListeners(obj, eventName, listeners); + } else { + sendEvent(obj, eventName, [obj, keyName]); + } + } - var removing, lim; - if (startIdx>=0 && removeAmt>=0 && get(this, 'hasEnumerableObservers')) { - removing = []; - lim = startIdx+removeAmt; - for(var idx=startIdx;idx=0 && addAmt>=0 && get(this, 'hasEnumerableObservers')) { - adding = []; - lim = startIdx+addAmt; - for(var idx=startIdx;idx 0) { + ret = meta.values[keyName]; + } else { + ret = obj[keyName]; + } + + if (ret === undefined && + 'object' === typeof obj && !(keyName in obj) && 'function' === typeof obj.unknownProperty) { + return obj.unknownProperty(keyName); } - return this.__each; - }) - - }); - - __exports__["default"] = EmberArray; - }); -define("ember-runtime/mixins/comparable", - ["ember-metal/mixin","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Mixin = __dependency1__.Mixin; - var required = __dependency1__.required; + return ret; + } + }; /** - @module ember - @submodule ember-runtime - */ + Normalizes a target/path pair to reflect that actual target/path that should + be observed, etc. This takes into account passing in global property + paths (i.e. a path beginning with a capital letter not defined on the + target). + @private + @method normalizeTuple + @for Ember + @param {Object} target The current target. May be `null`. + @param {String} path A path on the target or a global property path. + @return {Array} a temporary array with the normalized target/path pair. + */ + function normalizeTuple(target, path) { + var hasThis = pathHasThis(path); + var isGlobal = !hasThis && isGlobalPath(path); + var key; - /** - Implements some standard methods for comparing objects. Add this mixin to - any class you create that can compare its instances. + if (!target || isGlobal) target = Ember.lookup; + if (hasThis) path = path.slice(5); - You should implement the `compare()` method. + Ember.deprecate( + "normalizeTuple will return '"+path+"' as a non-global. This behavior will change in the future (issue #3852)", + target === Ember.lookup || !target || hasThis || isGlobal || !isGlobalPath(path+'.') + ); - @class Comparable - @namespace Ember - @since Ember 0.9 - */ - var Comparable = Mixin.create({ + if (target === Ember.lookup) { + key = path.match(FIRST_KEY)[0]; + target = get(target, key); + path = path.slice(key.length+1); + } - /** - Override to return the result of the comparison of the two parameters. The - compare method should return: + // must return some kind of path to be valid else other things will break. + if (!path || path.length===0) throw new EmberError('Path cannot be empty'); - - `-1` if `a < b` - - `0` if `a == b` - - `1` if `a > b` + return [ target, path ]; + } - Default implementation raises an exception. + function _getPath(root, path) { + var hasThis, parts, tuple, idx, len; - @method compare - @param a {Object} the first object to compare - @param b {Object} the second object to compare - @return {Integer} the result of the comparison - */ - compare: required(Function) + // If there is no root and path is a key name, return that + // property from the global object. + // E.g. get('Ember') -> Ember + if (root === null && !isPath(path)) { + return get(Ember.lookup, path); + } - }); + // detect complicated paths and normalize them + hasThis = pathHasThis(path); - __exports__["default"] = Comparable; - }); -define("ember-runtime/mixins/copyable", - ["ember-metal/property_get","ember-metal/property_set","ember-metal/mixin","ember-runtime/mixins/freezable","ember-runtime/system/string","ember-metal/error","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-runtime - */ + if (!root || hasThis) { + tuple = normalizeTuple(root, path); + root = tuple[0]; + path = tuple[1]; + tuple.length = 0; + } + parts = path.split("."); + len = parts.length; + for (idx = 0; root != null && idx < len; idx++) { + root = get(root, parts[idx], true); + if (root && root.isDestroyed) { return undefined; } + } + return root; + } - var get = __dependency1__.get; - var set = __dependency2__.set; - var required = __dependency3__.required; - var Freezable = __dependency4__.Freezable; - var Mixin = __dependency3__.Mixin; - var fmt = __dependency5__.fmt; - var EmberError = __dependency6__["default"]; + function getWithDefault(root, key, defaultValue) { + var value = get(root, key); + if (value === undefined) { return defaultValue; } + return value; + } - /** - Implements some standard methods for copying an object. Add this mixin to - any object you create that can create a copy of itself. This mixin is - added automatically to the built-in array. + __exports__.getWithDefault = getWithDefault;__exports__["default"] = get; + __exports__.get = get; + __exports__.normalizeTuple = normalizeTuple; + __exports__._getPath = _getPath; + }); +enifed("ember-metal/property_set", + ["ember-metal/core","ember-metal/property_get","ember-metal/property_events","ember-metal/properties","ember-metal/error","ember-metal/path_cache","ember-metal/platform","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + var getPath = __dependency2__._getPath; + var propertyWillChange = __dependency3__.propertyWillChange; + var propertyDidChange = __dependency3__.propertyDidChange; + var defineProperty = __dependency4__.defineProperty; + var EmberError = __dependency5__["default"]; + var isPath = __dependency6__.isPath; + var hasPropertyAccessors = __dependency7__.hasPropertyAccessors; - You should generally implement the `copy()` method to return a copy of the - receiver. + var IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/; - Note that `frozenCopy()` will only work if you also implement - `Ember.Freezable`. + /** + Sets the value of a property on an object, respecting computed properties + and notifying observers and other listeners of the change. If the + property is not defined but the object implements the `setUnknownProperty` + method then that will be invoked as well. - @class Copyable - @namespace Ember - @since Ember 0.9 + @method set + @for Ember + @param {Object} obj The object to modify. + @param {String} keyName The property key to set + @param {Object} value The value to set + @return {Object} the passed value. */ - var Copyable = Mixin.create({ + var set = function set(obj, keyName, value, tolerant) { + if (typeof obj === 'string') { + Ember.assert("Path '" + obj + "' must be global if no obj is given.", IS_GLOBAL.test(obj)); + value = keyName; + keyName = obj; + obj = null; + } - /** - Override to return a copy of the receiver. Default implementation raises - an exception. + Ember.assert("Cannot call set with "+ keyName +" key.", !!keyName); - @method copy - @param {Boolean} deep if `true`, a deep copy of the object should be made - @return {Object} copy of receiver - */ - copy: required(Function), + if (!obj) { + return setPath(obj, keyName, value, tolerant); + } - /** - If the object implements `Ember.Freezable`, then this will return a new - copy if the object is not frozen and the receiver if the object is frozen. + var meta = obj['__ember_meta__']; + var desc = meta && meta.descs[keyName]; + var isUnknown, currentValue; - Raises an exception if you try to call this method on a object that does - not support freezing. + if (desc === undefined && isPath(keyName)) { + return setPath(obj, keyName, value, tolerant); + } - You should use this method whenever you want a copy of a freezable object - since a freezable object can simply return itself without actually - consuming more memory. + Ember.assert("You need to provide an object and key to `set`.", !!obj && keyName !== undefined); + Ember.assert('calling set on destroyed object', !obj.isDestroyed); - @method frozenCopy - @return {Object} copy of receiver or receiver - */ - frozenCopy: function() { - if (Freezable && Freezable.detect(this)) { - return get(this, 'isFrozen') ? this : this.copy().freeze(); - } else { - throw new EmberError(fmt("%@ does not support freezing", [this])); + if (desc !== undefined) { + desc.set(obj, keyName, value); + } else { + + if (typeof obj === 'object' && obj !== null && value !== undefined && obj[keyName] === value) { + return value; } - } - }); - __exports__["default"] = Copyable; - }); -define("ember-runtime/mixins/deferred", - ["ember-metal/core","ember-metal/property_get","ember-metal/mixin","ember-metal/computed","ember-metal/run_loop","ember-runtime/ext/rsvp","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // Ember.FEATURES, Ember.Test - var get = __dependency2__.get; - var Mixin = __dependency3__.Mixin; - var computed = __dependency4__.computed; - var run = __dependency5__["default"]; - var RSVP = __dependency6__["default"]; + isUnknown = 'object' === typeof obj && !(keyName in obj); - var asyncStart = function() { - if (Ember.Test && Ember.Test.adapter) { - Ember.Test.adapter.asyncStart(); + // setUnknownProperty is called if `obj` is an object, + // the property does not already exist, and the + // `setUnknownProperty` method exists on the object + if (isUnknown && 'function' === typeof obj.setUnknownProperty) { + obj.setUnknownProperty(keyName, value); + } else if (meta && meta.watching[keyName] > 0) { + if (meta.proto !== obj) { + + if (hasPropertyAccessors) { + currentValue = meta.values[keyName]; + } else { + currentValue = obj[keyName]; + } + } + // only trigger a change if the value has changed + if (value !== currentValue) { + propertyWillChange(obj, keyName); + + if (hasPropertyAccessors) { + if ( + (currentValue === undefined && !(keyName in obj)) || + !Object.prototype.propertyIsEnumerable.call(obj, keyName) + ) { + defineProperty(obj, keyName, null, value); // setup mandatory setter + } else { + meta.values[keyName] = value; + } + } else { + obj[keyName] = value; + } + propertyDidChange(obj, keyName); + } + } else { + obj[keyName] = value; + } } + return value; }; - var asyncEnd = function() { - if (Ember.Test && Ember.Test.adapter) { - Ember.Test.adapter.asyncEnd(); - } - }; + function setPath(root, path, value, tolerant) { + var keyName; - RSVP.configure('async', function(callback, promise) { - var async = !run.currentRunLoop; + // get the last part of the path + keyName = path.slice(path.lastIndexOf('.') + 1); - if (Ember.testing && async) { asyncStart(); } + // get the first part of the part + path = (path === keyName) ? keyName : path.slice(0, path.length-(keyName.length+1)); - run.backburner.schedule('actions', function(){ - if (Ember.testing && async) { asyncEnd(); } - callback(promise); - }); - }); + // unless the path is this, look up the first part to + // get the root + if (path !== 'this') { + root = getPath(root, path); + } - RSVP.Promise.prototype.fail = function(callback, label){ - Ember.deprecate('RSVP.Promise.fail has been renamed as RSVP.Promise.catch'); - return this['catch'](callback, label); - }; + if (!keyName || keyName.length === 0) { + throw new EmberError('Property set failed: You passed an empty path'); + } - /** - @module ember - @submodule ember-runtime - */ + if (!root) { + if (tolerant) { return; } + else { throw new EmberError('Property set failed: object in path "'+path+'" could not be found or was destroyed.'); } + } + return set(root, keyName, value); + } /** - @class Deferred - @namespace Ember - */ - var DeferredMixin = Mixin.create({ - /** - Add handlers to be called when the Deferred object is resolved or rejected. - - @method then - @param {Function} resolve a callback function to be called when done - @param {Function} reject a callback function to be called when failed - */ - then: function(resolve, reject, label) { - var deferred, promise, entity; - - entity = this; - deferred = get(this, '_deferred'); - promise = deferred.promise; - - function fulfillmentHandler(fulfillment) { - if (fulfillment === promise) { - return resolve(entity); - } else { - return resolve(fulfillment); - } - } - - return promise.then(resolve && fulfillmentHandler, reject, label); - }, - - /** - Resolve a Deferred object and call any `doneCallbacks` with the given args. + Error-tolerant form of `Ember.set`. Will not blow up if any part of the + chain is `undefined`, `null`, or destroyed. - @method resolve - */ - resolve: function(value) { - var deferred, promise; + This is primarily used when syncing bindings, which may try to update after + an object has been destroyed. - deferred = get(this, '_deferred'); - promise = deferred.promise; + @method trySet + @for Ember + @param {Object} obj The object to modify. + @param {String} path The property path to set + @param {Object} value The value to set + */ + function trySet(root, path, value) { + return set(root, path, value, true); + } - if (value === this) { - deferred.resolve(promise); - } else { - deferred.resolve(value); - } - }, + __exports__.trySet = trySet;__exports__.set = set; + }); +enifed("ember-metal/run_loop", + ["ember-metal/core","ember-metal/utils","ember-metal/array","ember-metal/property_events","backburner","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + var apply = __dependency2__.apply; + var GUID_KEY = __dependency2__.GUID_KEY; + var indexOf = __dependency3__.indexOf; + var beginPropertyChanges = __dependency4__.beginPropertyChanges; + var endPropertyChanges = __dependency4__.endPropertyChanges; + var Backburner = __dependency5__["default"]; - /** - Reject a Deferred object and call any `failCallbacks` with the given args. + function onBegin(current) { + run.currentRunLoop = current; + } - @method reject - */ - reject: function(value) { - get(this, '_deferred').reject(value); - }, + function onEnd(current, next) { + run.currentRunLoop = next; + } - _deferred: computed(function() { - return RSVP.defer('Ember: DeferredMixin - ' + this); - }) + // ES6TODO: should Backburner become es6? + var backburner = new Backburner(['sync', 'actions', 'destroy'], { + GUID_KEY: GUID_KEY, + sync: { + before: beginPropertyChanges, + after: endPropertyChanges + }, + defaultQueue: 'actions', + onBegin: onBegin, + onEnd: onEnd, + onErrorTarget: Ember, + onErrorMethod: 'onerror' }); - - __exports__["default"] = DeferredMixin; - }); -define("ember-runtime/mixins/enumerable", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/mixin","ember-metal/enumerable_utils","ember-metal/computed","ember-metal/property_events","ember-metal/events","ember-runtime/compare","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-runtime - */ + var slice = [].slice; // .......................................................... - // HELPERS + // run - this is ideally the only public API the dev sees // - var Ember = __dependency1__["default"]; - var get = __dependency2__.get; - var set = __dependency3__.set; - var apply = __dependency4__.apply; - var Mixin = __dependency5__.Mixin; - var required = __dependency5__.required; - var aliasMethod = __dependency5__.aliasMethod; - var EnumerableUtils = __dependency6__["default"]; - var computed = __dependency7__.computed; - var propertyWillChange = __dependency8__.propertyWillChange; - var propertyDidChange = __dependency8__.propertyDidChange; - var addListener = __dependency9__.addListener; - var removeListener = __dependency9__.removeListener; - var sendEvent = __dependency9__.sendEvent; - var hasListeners = __dependency9__.hasListeners; - var compare = __dependency10__["default"]; + /** + Runs the passed target and method inside of a RunLoop, ensuring any + deferred actions including bindings and views updates are flushed at the + end. - var a_slice = Array.prototype.slice; - var a_indexOf = EnumerableUtils.indexOf; + Normally you should not need to invoke this method yourself. However if + you are implementing raw event handlers when interfacing with other + libraries or plugins, you should probably wrap all of your code inside this + call. - var contexts = []; + ```javascript + run(function() { + // code to be executed within a RunLoop + }); + ``` - function popCtx() { - return contexts.length===0 ? {} : contexts.pop(); + @class run + @namespace Ember + @static + @constructor + @param {Object} [target] target of method to call + @param {Function|String} method Method to invoke. + May be a function or a string. If you pass a string + then it will be looked up on the passed target. + @param {Object} [args*] Any additional arguments you wish to pass to the method. + @return {Object} return value from invoking the passed function. + */ + __exports__["default"] = run; + function run() { + return backburner.run.apply(backburner, arguments); } - function pushCtx(ctx) { - contexts.push(ctx); - return null; - } + /** + If no run-loop is present, it creates a new one. If a run loop is + present it will queue itself to run on the existing run-loops action + queue. - function iter(key, value) { - var valueProvided = arguments.length === 2; + Please note: This is not for normal usage, and should be used sparingly. - function i(item) { - var cur = get(item, key); - return valueProvided ? value===cur : !!cur; - } - return i ; - } + If invoked when not within a run loop: - /** - This mixin defines the common interface implemented by enumerable objects - in Ember. Most of these methods follow the standard Array iteration - API defined up to JavaScript 1.8 (excluding language-specific features that - cannot be emulated in older versions of JavaScript). + ```javascript + run.join(function() { + // creates a new run-loop + }); + ``` - This mixin is applied automatically to the Array class on page load, so you - can use any of these methods on simple arrays. If Array already implements - one of these methods, the mixin will not override them. + Alternatively, if called within an existing run loop: - ## Writing Your Own Enumerable + ```javascript + run(function() { + // creates a new run-loop + run.join(function() { + // joins with the existing run-loop, and queues for invocation on + // the existing run-loops action queue. + }); + }); + ``` - To make your own custom class enumerable, you need two items: + @method join + @namespace Ember + @param {Object} [target] target of method to call + @param {Function|String} method Method to invoke. + May be a function or a string. If you pass a string + then it will be looked up on the passed target. + @param {Object} [args*] Any additional arguments you wish to pass to the method. + @return {Object} Return value from invoking the passed function. Please note, + when called within an existing loop, no return value is possible. + */ + run.join = function() { + return backburner.join.apply(backburner, arguments); + }; - 1. You must have a length property. This property should change whenever - the number of items in your enumerable object changes. If you use this - with an `Ember.Object` subclass, you should be sure to change the length - property using `set().` + /** + Allows you to specify which context to call the specified function in while + adding the execution of that function to the Ember run loop. This ability + makes this method a great way to asynchronusly integrate third-party libraries + into your Ember application. - 2. You must implement `nextObject().` See documentation. + `run.bind` takes two main arguments, the desired context and the function to + invoke in that context. Any additional arguments will be supplied as arguments + to the function that is passed in. - Once you have these two methods implemented, apply the `Ember.Enumerable` mixin - to your class and you will be able to enumerate the contents of your object - like any other collection. + Let's use the creation of a TinyMCE component as an example. Currently, + TinyMCE provides a setup configuration option we can use to do some processing + after the TinyMCE instance is initialized but before it is actually rendered. + We can use that setup option to do some additional setup for our component. + The component itself could look something like the following: - ## Using Ember Enumeration with Other Libraries + ```javascript + App.RichTextEditorComponent = Ember.Component.extend({ + initializeTinyMCE: function(){ + tinymce.init({ + selector: '#' + this.$().prop('id'), + setup: Ember.run.bind(this, this.setupEditor) + }); + }.on('didInsertElement'), - Many other libraries provide some kind of iterator or enumeration like - facility. This is often where the most common API conflicts occur. - Ember's API is designed to be as friendly as possible with other - libraries by implementing only methods that mostly correspond to the - JavaScript 1.8 API. + setupEditor: function(editor) { + this.set('editor', editor); + editor.on('change', function(){ console.log('content changed!')} ); + } + }); + ``` - @class Enumerable + In this example, we use Ember.run.bind to bind the setupEditor message to the + context of the App.RichTextEditorComponent and to have the invocation of that + method be safely handled and excuted by the Ember run loop. + + @method bind @namespace Ember - @since Ember 0.9 + @param {Object} [target] target of method to call + @param {Function|String} method Method to invoke. + May be a function or a string. If you pass a string + then it will be looked up on the passed target. + @param {Object} [args*] Any additional arguments you wish to pass to the method. + @return {Object} return value from invoking the passed function. Please note, + when called within an existing loop, no return value is possible. + @since 1.4.0 */ - var Enumerable = Mixin.create({ - - /** - Implement this method to make your class enumerable. + run.bind = function(target, method /* args */) { + var args = slice.call(arguments); + return function() { + return run.join.apply(run, args.concat(slice.call(arguments))); + }; + }; - This method will be call repeatedly during enumeration. The index value - will always begin with 0 and increment monotonically. You don't have to - rely on the index value to determine what object to return, but you should - always check the value and start from the beginning when you see the - requested index is 0. + run.backburner = backburner; + run.currentRunLoop = null; + run.queues = backburner.queueNames; - The `previousObject` is the object that was returned from the last call - to `nextObject` for the current iteration. This is a useful way to - manage iteration if you are tracing a linked list, for example. + /** + Begins a new RunLoop. Any deferred actions invoked after the begin will + be buffered until you invoke a matching call to `run.end()`. This is + a lower-level way to use a RunLoop instead of using `run()`. - Finally the context parameter will always contain a hash you can use as - a "scratchpad" to maintain any other state you need in order to iterate - properly. The context object is reused and is not reset between - iterations so make sure you setup the context with a fresh state whenever - the index parameter is 0. + ```javascript + run.begin(); + // code to be executed within a RunLoop + run.end(); + ``` - Generally iterators will continue to call `nextObject` until the index - reaches the your current length-1. If you run out of data before this - time for some reason, you should simply return undefined. + @method begin + @return {void} + */ + run.begin = function() { + backburner.begin(); + }; - The default implementation of this method simply looks up the index. - This works great on any Array-like objects. + /** + Ends a RunLoop. This must be called sometime after you call + `run.begin()` to flush any deferred actions. This is a lower-level way + to use a RunLoop instead of using `run()`. - @method nextObject - @param {Number} index the current index of the iteration - @param {Object} previousObject the value returned by the last call to - `nextObject`. - @param {Object} context a context object you can use to maintain state. - @return {Object} the next object in the iteration or undefined - */ - nextObject: required(Function), + ```javascript + run.begin(); + // code to be executed within a RunLoop + run.end(); + ``` - /** - Helper method returns the first object from a collection. This is usually - used by bindings and other parts of the framework to extract a single - object if the enumerable contains only one item. + @method end + @return {void} + */ + run.end = function() { + backburner.end(); + }; - If you override this method, you should implement it so that it will - always return the same value each time it is called. If your enumerable - contains only one object, this method should always return that object. - If your enumerable is empty, this method should return `undefined`. + /** + Array of named queues. This array determines the order in which queues + are flushed at the end of the RunLoop. You can define your own queues by + simply adding the queue name to this array. Normally you should not need + to inspect or modify this property. - ```javascript - var arr = ["a", "b", "c"]; - arr.get('firstObject'); // "a" + @property queues + @type Array + @default ['sync', 'actions', 'destroy'] + */ - var arr = []; - arr.get('firstObject'); // undefined - ``` + /** + Adds the passed target/method and any optional arguments to the named + queue to be executed at the end of the RunLoop. If you have not already + started a RunLoop when calling this method one will be started for you + automatically. - @property firstObject - @return {Object} the object or undefined - */ - firstObject: computed(function() { - if (get(this, 'length')===0) return undefined ; + At the end of a RunLoop, any methods scheduled in this way will be invoked. + Methods will be invoked in an order matching the named queues defined in + the `run.queues` property. - // handle generic enumerables - var context = popCtx(), ret; - ret = this.nextObject(0, null, context); - pushCtx(context); - return ret ; - }).property('[]'), + ```javascript + run.schedule('sync', this, function() { + // this will be executed in the first RunLoop queue, when bindings are synced + console.log("scheduled on sync queue"); + }); - /** - Helper method returns the last object from a collection. If your enumerable - contains only one object, this method should always return that object. - If your enumerable is empty, this method should return `undefined`. + run.schedule('actions', this, function() { + // this will be executed in the 'actions' queue, after bindings have synced. + console.log("scheduled on actions queue"); + }); - ```javascript - var arr = ["a", "b", "c"]; - arr.get('lastObject'); // "c" + // Note the functions will be run in order based on the run queues order. + // Output would be: + // scheduled on sync queue + // scheduled on actions queue + ``` - var arr = []; - arr.get('lastObject'); // undefined - ``` + @method schedule + @param {String} queue The name of the queue to schedule against. + Default queues are 'sync' and 'actions' + @param {Object} [target] target object to use as the context when invoking a method. + @param {String|Function} method The method to invoke. If you pass a string it + will be resolved on the target object at the time the scheduled item is + invoked allowing you to change the target function. + @param {Object} [arguments*] Optional arguments to be passed to the queued method. + @return {void} + */ + run.schedule = function(queue, target, method) { + checkAutoRun(); + backburner.schedule.apply(backburner, arguments); + }; - @property lastObject - @return {Object} the last object or undefined - */ - lastObject: computed(function() { - var len = get(this, 'length'); - if (len===0) return undefined ; - var context = popCtx(), idx=0, cur, last = null; - do { - last = cur; - cur = this.nextObject(idx++, last, context); - } while (cur !== undefined); - pushCtx(context); - return last; - }).property('[]'), + // Used by global test teardown + run.hasScheduledTimers = function() { + return backburner.hasTimers(); + }; - /** - Returns `true` if the passed object can be found in the receiver. The - default version will iterate through the enumerable until the object - is found. You may want to override this with a more efficient version. + // Used by global test teardown + run.cancelTimers = function () { + backburner.cancelTimers(); + }; - ```javascript - var arr = ["a", "b", "c"]; - arr.contains("a"); // true - arr.contains("z"); // false - ``` + /** + Immediately flushes any events scheduled in the 'sync' queue. Bindings + use this queue so this method is a useful way to immediately force all + bindings in the application to sync. - @method contains - @param {Object} obj The object to search for. - @return {Boolean} `true` if object is found in enumerable. - */ - contains: function(obj) { - return this.find(function(item) { return item===obj; }) !== undefined; - }, + You should call this method anytime you need any changed state to propagate + throughout the app immediately without repainting the UI (which happens + in the later 'render' queue added by the `ember-views` package). - /** - Iterates through the enumerable, calling the passed function on each - item. This method corresponds to the `forEach()` method defined in - JavaScript 1.6. + ```javascript + run.sync(); + ``` - The callback method you provide should have the following signature (all - parameters are optional): + @method sync + @return {void} + */ + run.sync = function() { + if (backburner.currentInstance) { + backburner.currentInstance.queues.sync.flush(); + } + }; - ```javascript - function(item, index, enumerable); - ``` + /** + Invokes the passed target/method and optional arguments after a specified + period of time. The last parameter of this method must always be a number + of milliseconds. - - `item` is the current item in the iteration. - - `index` is the current index in the iteration. - - `enumerable` is the enumerable object itself. + You should use this method whenever you need to run some action after a + period of time instead of using `setTimeout()`. This method will ensure that + items that expire during the same script execution cycle all execute + together, which is often more efficient than using a real setTimeout. - Note that in addition to a callback, you can also pass an optional target - object that will be set as `this` on the context. This is a good way - to give your iterator function access to the current object. + ```javascript + run.later(myContext, function() { + // code here will execute within a RunLoop in about 500ms with this == myContext + }, 500); + ``` + + @method later + @param {Object} [target] target of method to invoke + @param {Function|String} method The method to invoke. + If you pass a string it will be resolved on the + target at the time the method is invoked. + @param {Object} [args*] Optional arguments to pass to the timeout. + @param {Number} wait Number of milliseconds to wait. + @return {Object} Timer information for use in cancelling, see `run.cancel`. + */ + run.later = function(/*target, method*/) { + return backburner.later.apply(backburner, arguments); + }; + + /** + Schedule a function to run one time during the current RunLoop. This is equivalent + to calling `scheduleOnce` with the "actions" queue. + + @method once + @param {Object} [target] The target of the method to invoke. + @param {Function|String} method The method to invoke. + If you pass a string it will be resolved on the + target at the time the method is invoked. + @param {Object} [args*] Optional arguments to pass to the timeout. + @return {Object} Timer information for use in cancelling, see `run.cancel`. + */ + run.once = function(/*target, method */) { + checkAutoRun(); + var length = arguments.length; + var args = new Array(length); + args[0] = 'actions'; + for (var i = 0; i < length; i++) { + args[i + 1] = arguments[i]; + } + return apply(backburner, backburner.scheduleOnce, args); + }; + + /** + Schedules a function to run one time in a given queue of the current RunLoop. + Calling this method with the same queue/target/method combination will have + no effect (past the initial call). + + Note that although you can pass optional arguments these will not be + considered when looking for duplicates. New arguments will replace previous + calls. + + ```javascript + run(function() { + var sayHi = function() { console.log('hi'); } + run.scheduleOnce('afterRender', myContext, sayHi); + run.scheduleOnce('afterRender', myContext, sayHi); + // sayHi will only be executed once, in the afterRender queue of the RunLoop + }); + ``` + + Also note that passing an anonymous function to `run.scheduleOnce` will + not prevent additional calls with an identical anonymous function from + scheduling the items multiple times, e.g.: + + ```javascript + function scheduleIt() { + run.scheduleOnce('actions', myContext, function() { console.log("Closure"); }); + } + scheduleIt(); + scheduleIt(); + // "Closure" will print twice, even though we're using `run.scheduleOnce`, + // because the function we pass to it is anonymous and won't match the + // previously scheduled operation. + ``` + + Available queues, and their order, can be found at `run.queues` + + @method scheduleOnce + @param {String} [queue] The name of the queue to schedule against. Default queues are 'sync' and 'actions'. + @param {Object} [target] The target of the method to invoke. + @param {Function|String} method The method to invoke. + If you pass a string it will be resolved on the + target at the time the method is invoked. + @param {Object} [args*] Optional arguments to pass to the timeout. + @return {Object} Timer information for use in cancelling, see `run.cancel`. + */ + run.scheduleOnce = function(/*queue, target, method*/) { + checkAutoRun(); + return backburner.scheduleOnce.apply(backburner, arguments); + }; + + /** + Schedules an item to run from within a separate run loop, after + control has been returned to the system. This is equivalent to calling + `run.later` with a wait time of 1ms. + + ```javascript + run.next(myContext, function() { + // code to be executed in the next run loop, + // which will be scheduled after the current one + }); + ``` + + Multiple operations scheduled with `run.next` will coalesce + into the same later run loop, along with any other operations + scheduled by `run.later` that expire right around the same + time that `run.next` operations will fire. + + Note that there are often alternatives to using `run.next`. + For instance, if you'd like to schedule an operation to happen + after all DOM element operations have completed within the current + run loop, you can make use of the `afterRender` run loop queue (added + by the `ember-views` package, along with the preceding `render` queue + where all the DOM element operations happen). Example: + + ```javascript + App.MyCollectionView = Ember.CollectionView.extend({ + didInsertElement: function() { + run.scheduleOnce('afterRender', this, 'processChildElements'); + }, + processChildElements: function() { + // ... do something with collectionView's child view + // elements after they've finished rendering, which + // can't be done within the CollectionView's + // `didInsertElement` hook because that gets run + // before the child elements have been added to the DOM. + } + }); + ``` + + One benefit of the above approach compared to using `run.next` is + that you will be able to perform DOM/CSS operations before unprocessed + elements are rendered to the screen, which may prevent flickering or + other artifacts caused by delaying processing until after rendering. + + The other major benefit to the above approach is that `run.next` + introduces an element of non-determinism, which can make things much + harder to test, due to its reliance on `setTimeout`; it's much harder + to guarantee the order of scheduled operations when they are scheduled + outside of the current run loop, i.e. with `run.next`. + + @method next + @param {Object} [target] target of method to invoke + @param {Function|String} method The method to invoke. + If you pass a string it will be resolved on the + target at the time the method is invoked. + @param {Object} [args*] Optional arguments to pass to the timeout. + @return {Object} Timer information for use in cancelling, see `run.cancel`. + */ + run.next = function() { + var args = slice.call(arguments); + args.push(1); + return apply(backburner, backburner.later, args); + }; + + /** + Cancels a scheduled item. Must be a value returned by `run.later()`, + `run.once()`, `run.next()`, `run.debounce()`, or + `run.throttle()`. + + ```javascript + var runNext = run.next(myContext, function() { + // will not be executed + }); + run.cancel(runNext); + + var runLater = run.later(myContext, function() { + // will not be executed + }, 500); + run.cancel(runLater); + + var runOnce = run.once(myContext, function() { + // will not be executed + }); + run.cancel(runOnce); + + var throttle = run.throttle(myContext, function() { + // will not be executed + }, 1, false); + run.cancel(throttle); + + var debounce = run.debounce(myContext, function() { + // will not be executed + }, 1); + run.cancel(debounce); + + var debounceImmediate = run.debounce(myContext, function() { + // will be executed since we passed in true (immediate) + }, 100, true); + // the 100ms delay until this method can be called again will be cancelled + run.cancel(debounceImmediate); + ``` + + @method cancel + @param {Object} timer Timer object to cancel + @return {Boolean} true if cancelled or false/undefined if it wasn't found + */ + run.cancel = function(timer) { + return backburner.cancel(timer); + }; + + /** + Delay calling the target method until the debounce period has elapsed + with no additional debounce calls. If `debounce` is called again before + the specified time has elapsed, the timer is reset and the entire period + must pass again before the target method is called. + + This method should be used when an event may be called multiple times + but the action should only be called once when the event is done firing. + A common example is for scroll events where you only want updates to + happen once scrolling has ceased. + + ```javascript + var myFunc = function() { console.log(this.name + ' ran.'); }; + var myContext = {name: 'debounce'}; + + run.debounce(myContext, myFunc, 150); + + // less than 150ms passes + + run.debounce(myContext, myFunc, 150); + + // 150ms passes + // myFunc is invoked with context myContext + // console logs 'debounce ran.' one time. + ``` + + Immediate allows you to run the function immediately, but debounce + other calls for this function until the wait time has elapsed. If + `debounce` is called again before the specified time has elapsed, + the timer is reset and the entire period must pass again before + the method can be called again. + + ```javascript + var myFunc = function() { console.log(this.name + ' ran.'); }; + var myContext = {name: 'debounce'}; + + run.debounce(myContext, myFunc, 150, true); + + // console logs 'debounce ran.' one time immediately. + // 100ms passes + + run.debounce(myContext, myFunc, 150, true); + + // 150ms passes and nothing else is logged to the console and + // the debouncee is no longer being watched + + run.debounce(myContext, myFunc, 150, true); + + // console logs 'debounce ran.' one time immediately. + // 150ms passes and nothing else is logged to the console and + // the debouncee is no longer being watched + + ``` + + @method debounce + @param {Object} [target] target of method to invoke + @param {Function|String} method The method to invoke. + May be a function or a string. If you pass a string + then it will be looked up on the passed target. + @param {Object} [args*] Optional arguments to pass to the timeout. + @param {Number} wait Number of milliseconds to wait. + @param {Boolean} immediate Trigger the function on the leading instead + of the trailing edge of the wait interval. Defaults to false. + @return {Array} Timer information for use in cancelling, see `run.cancel`. + */ + run.debounce = function() { + return backburner.debounce.apply(backburner, arguments); + }; + + /** + Ensure that the target method is never called more frequently than + the specified spacing period. The target method is called immediately. + + ```javascript + var myFunc = function() { console.log(this.name + ' ran.'); }; + var myContext = {name: 'throttle'}; + + run.throttle(myContext, myFunc, 150); + // myFunc is invoked with context myContext + // console logs 'throttle ran.' + + // 50ms passes + run.throttle(myContext, myFunc, 150); + + // 50ms passes + run.throttle(myContext, myFunc, 150); + + // 150ms passes + run.throttle(myContext, myFunc, 150); + // myFunc is invoked with context myContext + // console logs 'throttle ran.' + ``` + + @method throttle + @param {Object} [target] target of method to invoke + @param {Function|String} method The method to invoke. + May be a function or a string. If you pass a string + then it will be looked up on the passed target. + @param {Object} [args*] Optional arguments to pass to the timeout. + @param {Number} spacing Number of milliseconds to space out requests. + @param {Boolean} immediate Trigger the function on the leading instead + of the trailing edge of the wait interval. Defaults to true. + @return {Array} Timer information for use in cancelling, see `run.cancel`. + */ + run.throttle = function() { + return backburner.throttle.apply(backburner, arguments); + }; + + // Make sure it's not an autorun during testing + function checkAutoRun() { + if (!run.currentRunLoop) { + Ember.assert("You have turned on testing mode, which disabled the run-loop's autorun." + + " You will need to wrap any code with asynchronous side-effects in an run", !Ember.testing); + } + } + + /** + Add a new named queue after the specified queue. + + The queue to add will only be added once. + + @method _addQueue + @param {String} name the name of the queue to add. + @param {String} after the name of the queue to add after. + @private + */ + run._addQueue = function(name, after) { + if (indexOf.call(run.queues, name) === -1) { + run.queues.splice(indexOf.call(run.queues, after)+1, 0, name); + } + }; + }); +enifed("ember-metal/set_properties", + ["ember-metal/property_events","ember-metal/property_set","ember-metal/keys","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var changeProperties = __dependency1__.changeProperties; + var set = __dependency2__.set; + var keys = __dependency3__["default"]; + + /** + Set a list of properties on an object. These properties are set inside + a single `beginPropertyChanges` and `endPropertyChanges` batch, so + observers will be buffered. + + ```javascript + var anObject = Ember.Object.create(); + + anObject.setProperties({ + firstName: 'Stanley', + lastName: 'Stuart', + age: 21 + }); + ``` + + @method setProperties + @param obj + @param {Object} properties + @return obj + */ + __exports__["default"] = function setProperties(obj, properties) { + if (!properties || typeof properties !== "object") { return obj; } + changeProperties(function() { + var props = keys(properties); + var propertyName; + + for (var i = 0, l = props.length; i < l; i++) { + propertyName = props[i]; + + set(obj, propertyName, properties[propertyName]); + } + }); + return obj; + } + }); +enifed("ember-metal/streams/simple", + ["ember-metal/merge","ember-metal/streams/stream","ember-metal/platform","ember-metal/streams/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + var merge = __dependency1__["default"]; + var Stream = __dependency2__["default"]; + var create = __dependency3__.create; + var read = __dependency4__.read; + var isStream = __dependency4__.isStream; + + function SimpleStream(source) { + this.init(); + this.source = source; + + if (isStream(source)) { + source.subscribe(this._didChange, this); + } + } + + SimpleStream.prototype = create(Stream.prototype); + + merge(SimpleStream.prototype, { + valueFn: function() { + return read(this.source); + }, + + setValue: function(value) { + var source = this.source; + + if (isStream(source)) { + source.setValue(value); + } + }, + + setSource: function(nextSource) { + var prevSource = this.source; + if (nextSource !== prevSource) { + if (isStream(prevSource)) { + prevSource.unsubscribe(this._didChange, this); + } + + if (isStream(nextSource)) { + nextSource.subscribe(this._didChange, this); + } + + this.source = nextSource; + this.notify(); + } + }, + + _didChange: function() { + this.notify(); + }, + + _super$destroy: Stream.prototype.destroy, + + destroy: function() { + if (this._super$destroy()) { + if (isStream(this.source)) { + this.source.unsubscribe(this._didChange, this); + } + this.source = undefined; + return true; + } + } + }); + + __exports__["default"] = SimpleStream; + }); +enifed("ember-metal/streams/stream", + ["ember-metal/platform","ember-metal/path_cache","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var create = __dependency1__.create; + var getFirstKey = __dependency2__.getFirstKey; + var getTailPath = __dependency2__.getTailPath; + + function Stream(fn) { + this.init(); + this.valueFn = fn; + } + + Stream.prototype = { + isStream: true, + + init: function() { + this.state = 'dirty'; + this.cache = undefined; + this.subscribers = undefined; + this.children = undefined; + this._label = undefined; + }, + + get: function(path) { + var firstKey = getFirstKey(path); + var tailPath = getTailPath(path); + + if (this.children === undefined) { + this.children = create(null); + } + + var keyStream = this.children[firstKey]; + + if (keyStream === undefined) { + keyStream = this._makeChildStream(firstKey, path); + this.children[firstKey] = keyStream; + } + + if (tailPath === undefined) { + return keyStream; + } else { + return keyStream.get(tailPath); + } + }, + + value: function() { + if (this.state === 'clean') { + return this.cache; + } else if (this.state === 'dirty') { + this.state = 'clean'; + return this.cache = this.valueFn(); + } + // TODO: Ensure value is never called on a destroyed stream + // so that we can uncomment this assertion. + // + // Ember.assert("Stream error: value was called in an invalid state: " + this.state); + }, + + valueFn: function() { + throw new Error("Stream error: valueFn not implemented"); + }, + + setValue: function() { + throw new Error("Stream error: setValue not implemented"); + }, + + notify: function() { + this.notifyExcept(); + }, + + notifyExcept: function(callbackToSkip, contextToSkip) { + if (this.state === 'clean') { + this.state = 'dirty'; + this._notifySubscribers(callbackToSkip, contextToSkip); + } + }, + + subscribe: function(callback, context) { + if (this.subscribers === undefined) { + this.subscribers = [callback, context]; + } else { + this.subscribers.push(callback, context); + } + }, + + unsubscribe: function(callback, context) { + var subscribers = this.subscribers; + + if (subscribers !== undefined) { + for (var i = 0, l = subscribers.length; i < l; i += 2) { + if (subscribers[i] === callback && subscribers[i+1] === context) { + subscribers.splice(i, 2); + return; + } + } + } + }, + + _notifySubscribers: function(callbackToSkip, contextToSkip) { + var subscribers = this.subscribers; + + if (subscribers !== undefined) { + for (var i = 0, l = subscribers.length; i < l; i += 2) { + var callback = subscribers[i]; + var context = subscribers[i+1]; + + if (callback === callbackToSkip && context === contextToSkip) { + continue; + } + + if (context === undefined) { + callback(this); + } else { + callback.call(context, this); + } + } + } + }, + + destroy: function() { + if (this.state !== 'destroyed') { + this.state = 'destroyed'; + + var children = this.children; + for (var key in children) { + children[key].destroy(); + } + + return true; + } + }, + + isGlobal: function() { + var stream = this; + while (stream !== undefined) { + if (stream._isRoot) { + return stream._isGlobal; + } + stream = stream.source; + } + } + }; + + __exports__["default"] = Stream; + }); +enifed("ember-metal/streams/stream_binding", + ["ember-metal/platform","ember-metal/merge","ember-metal/run_loop","ember-metal/streams/stream","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + var create = __dependency1__.create; + var merge = __dependency2__["default"]; + var run = __dependency3__["default"]; + var Stream = __dependency4__["default"]; + + function StreamBinding(stream) { + Ember.assert("StreamBinding error: tried to bind to object that is not a stream", stream && stream.isStream); + + this.init(); + this.stream = stream; + this.senderCallback = undefined; + this.senderContext = undefined; + this.senderValue = undefined; + + stream.subscribe(this._onNotify, this); + } + + StreamBinding.prototype = create(Stream.prototype); + + merge(StreamBinding.prototype, { + valueFn: function() { + return this.stream.value(); + }, + + _onNotify: function() { + this._scheduleSync(undefined, undefined, this); + }, + + setValue: function(value, callback, context) { + this._scheduleSync(value, callback, context); + }, + + _scheduleSync: function(value, callback, context) { + if (this.senderCallback === undefined && this.senderContext === undefined) { + this.senderCallback = callback; + this.senderContext = context; + this.senderValue = value; + run.schedule('sync', this, this._sync); + } else if (this.senderContext !== this) { + this.senderCallback = callback; + this.senderContext = context; + this.senderValue = value; + } + }, + + _sync: function() { + if (this.state === 'destroyed') { + return; + } + + if (this.senderContext !== this) { + this.stream.setValue(this.senderValue); + } + + var senderCallback = this.senderCallback; + var senderContext = this.senderContext; + this.senderCallback = undefined; + this.senderContext = undefined; + this.senderValue = undefined; + + // Force StreamBindings to always notify + this.state = 'clean'; + + this.notifyExcept(senderCallback, senderContext); + }, + + _super$destroy: Stream.prototype.destroy, + + destroy: function() { + if (this._super$destroy()) { + this.stream.unsubscribe(this._onNotify, this); + return true; + } + } + }); + + __exports__["default"] = StreamBinding; + }); +enifed("ember-metal/streams/utils", + ["./stream","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Stream = __dependency1__["default"]; + + function isStream(object) { + return object && object.isStream; + } + + __exports__.isStream = isStream;function subscribe(object, callback, context) { + if (object && object.isStream) { + object.subscribe(callback, context); + } + } + + __exports__.subscribe = subscribe;function unsubscribe(object, callback, context) { + if (object && object.isStream) { + object.unsubscribe(callback, context); + } + } + + __exports__.unsubscribe = unsubscribe;function read(object) { + if (object && object.isStream) { + return object.value(); + } else { + return object; + } + } + + __exports__.read = read;function readArray(array) { + var length = array.length; + var ret = new Array(length); + for (var i = 0; i < length; i++) { + ret[i] = read(array[i]); + } + return ret; + } + + __exports__.readArray = readArray;function readHash(object) { + var ret = {}; + for (var key in object) { + ret[key] = read(object[key]); + } + return ret; + } + + __exports__.readHash = readHash;/** + * @function scanArray + * @param array Array array given to a handlebars helper + * @return Boolean whether the array contains a stream/bound value + */ + function scanArray(array) { + var length = array.length; + var containsStream = false; + + for (var i = 0; i < length; i++){ + if (isStream(array[i])) { + containsStream = true; + break; + } + } + + return containsStream; + } + + __exports__.scanArray = scanArray;/** + * @function scanHash + * @param Object hash "hash" argument given to a handlebars helper + * @return Boolean whether the object contains a stream/bound value + */ + function scanHash(hash) { + var containsStream = false; + + for (var prop in hash) { + if (isStream(hash[prop])) { + containsStream = true; + break; + } + } + + return containsStream; + } + + __exports__.scanHash = scanHash;// TODO: Create subclass ConcatStream < Stream. Defer + // subscribing to streams until the value() is called. + function concat(array, key) { + var hasStream = scanArray(array); + if (hasStream) { + var i, l; + var stream = new Stream(function() { + return readArray(array).join(key); + }); + + for (i = 0, l=array.length; i < l; i++) { + subscribe(array[i], stream.notify, stream); + } + + return stream; + } else { + return array.join(key); + } + } + + __exports__.concat = concat;function chainStream(value, fn) { + if (isStream(value)) { + var stream = new Stream(fn); + subscribe(value, stream.notify, stream); + return stream; + } else { + return fn(); + } + } + + __exports__.chainStream = chainStream; + }); +enifed("ember-metal/utils", + ["ember-metal/core","ember-metal/platform","ember-metal/array","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + // Remove "use strict"; from transpiled module until + // https://bugs.webkit.org/show_bug.cgi?id=138038 is fixed + // + // REMOVE_USE_STRICT: true + + var Ember = __dependency1__["default"]; + var o_defineProperty = __dependency2__.defineProperty; + var canDefineNonEnumerableProperties = __dependency2__.canDefineNonEnumerableProperties; + var hasPropertyAccessors = __dependency2__.hasPropertyAccessors; + var o_create = __dependency2__.create; + + var forEach = __dependency3__.forEach; + + /** + @module ember-metal + */ + + /** + Previously we used `Ember.$.uuid`, however `$.uuid` has been removed from + jQuery master. We'll just bootstrap our own uuid now. + + @private + @return {Number} the uuid + */ + var _uuid = 0; + + /** + Generates a universally unique identifier. This method + is used internally by Ember for assisting with + the generation of GUID's and other unique identifiers + such as `bind-attr` data attributes. + + @public + @return {Number} [description] + */ + function uuid() { + return ++_uuid; + } + + __exports__.uuid = uuid;/** + Prefix used for guids through out Ember. + @private + @property GUID_PREFIX + @for Ember + @type String + @final + */ + var GUID_PREFIX = 'ember'; + + // Used for guid generation... + var numberCache = []; + var stringCache = {}; + + /** + Strongly hint runtimes to intern the provided string. + + When do I need to use this function? + + For the most part, never. Pre-mature optimization is bad, and often the + runtime does exactly what you need it to, and more often the trade-off isn't + worth it. + + Why? + + Runtimes store strings in at least 2 different representations: + Ropes and Symbols (interned strings). The Rope provides a memory efficient + data-structure for strings created from concatenation or some other string + manipulation like splitting. + + Unfortunately checking equality of different ropes can be quite costly as + runtimes must resort to clever string comparison algorithims. These + algorithims typically cost in proportion to the length of the string. + Luckily, this is where the Symbols (interned strings) shine. As Symbols are + unique by their string content, equality checks can be done by pointer + comparison. + + How do I know if my string is a rope or symbol? + + Typically (warning general sweeping statement, but truthy in runtimes at + present) static strings created as part of the JS source are interned. + Strings often used for comparisons can be interned at runtime if some + criteria are met. One of these criteria can be the size of the entire rope. + For example, in chrome 38 a rope longer then 12 characters will not + intern, nor will segments of that rope. + + Some numbers: http://jsperf.com/eval-vs-keys/8 + + Known Trick™ + + @private + @return {String} interned version of the provided string + */ + function intern(str) { + var obj = {}; + obj[str] = 1; + for (var key in obj) { + if (key === str) return key; + } + return str; + } + + /** + A unique key used to assign guids and other private metadata to objects. + If you inspect an object in your browser debugger you will often see these. + They can be safely ignored. + + On browsers that support it, these properties are added with enumeration + disabled so they won't show up when you iterate over your properties. + + @private + @property GUID_KEY + @for Ember + @type String + @final + */ + var GUID_KEY = intern('__ember' + (+ new Date())); + + var GUID_DESC = { + writable: false, + configurable: false, + enumerable: false, + value: null + }; + + /** + Generates a new guid, optionally saving the guid to the object that you + pass in. You will rarely need to use this method. Instead you should + call `Ember.guidFor(obj)`, which return an existing guid if available. + + @private + @method generateGuid + @for Ember + @param {Object} [obj] Object the guid will be used for. If passed in, the guid will + be saved on the object and reused whenever you pass the same object + again. + + If no object is passed, just generate a new guid. + @param {String} [prefix] Prefix to place in front of the guid. Useful when you want to + separate the guid into separate namespaces. + @return {String} the guid + */ + function generateGuid(obj, prefix) { + if (!prefix) prefix = GUID_PREFIX; + var ret = (prefix + uuid()); + if (obj) { + if (obj[GUID_KEY] === null) { + obj[GUID_KEY] = ret; + } else { + GUID_DESC.value = ret; + o_defineProperty(obj, GUID_KEY, GUID_DESC); + } + } + return ret; + } + + __exports__.generateGuid = generateGuid;/** + Returns a unique id for the object. If the object does not yet have a guid, + one will be assigned to it. You can call this on any object, + `Ember.Object`-based or not, but be aware that it will add a `_guid` + property. + + You can also use this method on DOM Element objects. + + @private + @method guidFor + @for Ember + @param {Object} obj any object, string, number, Element, or primitive + @return {String} the unique guid for this instance. + */ + function guidFor(obj) { + + // special cases where we don't want to add a key to object + if (obj === undefined) return "(undefined)"; + if (obj === null) return "(null)"; + + var ret; + var type = typeof obj; + + // Don't allow prototype changes to String etc. to change the guidFor + switch(type) { + case 'number': + ret = numberCache[obj]; + if (!ret) ret = numberCache[obj] = 'nu'+obj; + return ret; + + case 'string': + ret = stringCache[obj]; + if (!ret) ret = stringCache[obj] = 'st' + uuid(); + return ret; + + case 'boolean': + return obj ? '(true)' : '(false)'; + + default: + if (obj[GUID_KEY]) return obj[GUID_KEY]; + if (obj === Object) return '(Object)'; + if (obj === Array) return '(Array)'; + ret = GUID_PREFIX + uuid(); + + if (obj[GUID_KEY] === null) { + obj[GUID_KEY] = ret; + } else { + GUID_DESC.value = ret; + o_defineProperty(obj, GUID_KEY, GUID_DESC); + } + return ret; + } + } + + __exports__.guidFor = guidFor;// .......................................................... + // META + // + + var META_DESC = { + writable: true, + configurable: false, + enumerable: false, + value: null + }; + + function Meta(obj) { + this.descs = {}; + this.watching = {}; + this.cache = {}; + this.cacheMeta = {}; + this.source = obj; + this.deps = undefined; + this.listeners = undefined; + this.mixins = undefined; + this.bindings = undefined; + this.chains = undefined; + this.values = undefined; + this.proto = undefined; + } + + Meta.prototype = { + chainWatchers: null + }; + + if (!canDefineNonEnumerableProperties) { + // on platforms that don't support enumerable false + // make meta fail jQuery.isPlainObject() to hide from + // jQuery.extend() by having a property that fails + // hasOwnProperty check. + Meta.prototype.__preventPlainObject__ = true; + + // Without non-enumerable properties, meta objects will be output in JSON + // unless explicitly suppressed + Meta.prototype.toJSON = function () { }; + } + + // Placeholder for non-writable metas. + var EMPTY_META = new Meta(null); + + + if (hasPropertyAccessors) { + EMPTY_META.values = {}; + } + + + /** + Retrieves the meta hash for an object. If `writable` is true ensures the + hash is writable for this object as well. + + The meta object contains information about computed property descriptors as + well as any watched properties and other information. You generally will + not access this information directly but instead work with higher level + methods that manipulate this hash indirectly. + + @method meta + @for Ember + @private + + @param {Object} obj The object to retrieve meta for + @param {Boolean} [writable=true] Pass `false` if you do not intend to modify + the meta hash, allowing the method to avoid making an unnecessary copy. + @return {Object} the meta hash for an object + */ + function meta(obj, writable) { + var ret = obj['__ember_meta__']; + if (writable===false) return ret || EMPTY_META; + + if (!ret) { + if (canDefineNonEnumerableProperties) o_defineProperty(obj, '__ember_meta__', META_DESC); + + ret = new Meta(obj); + + + if (hasPropertyAccessors) { + ret.values = {}; + } + + + obj['__ember_meta__'] = ret; + + // make sure we don't accidentally try to create constructor like desc + ret.descs.constructor = null; + + } else if (ret.source !== obj) { + if (canDefineNonEnumerableProperties) o_defineProperty(obj, '__ember_meta__', META_DESC); + + ret = o_create(ret); + ret.descs = o_create(ret.descs); + ret.watching = o_create(ret.watching); + ret.cache = {}; + ret.cacheMeta = {}; + ret.source = obj; + + + if (hasPropertyAccessors) { + ret.values = o_create(ret.values); + } + + + obj['__ember_meta__'] = ret; + } + return ret; + } + + function getMeta(obj, property) { + var _meta = meta(obj, false); + return _meta[property]; + } + + __exports__.getMeta = getMeta;function setMeta(obj, property, value) { + var _meta = meta(obj, true); + _meta[property] = value; + return value; + } + + __exports__.setMeta = setMeta;/** + @deprecated + @private + + In order to store defaults for a class, a prototype may need to create + a default meta object, which will be inherited by any objects instantiated + from the class's constructor. + + However, the properties of that meta object are only shallow-cloned, + so if a property is a hash (like the event system's `listeners` hash), + it will by default be shared across all instances of that class. + + This method allows extensions to deeply clone a series of nested hashes or + other complex objects. For instance, the event system might pass + `['listeners', 'foo:change', 'ember157']` to `prepareMetaPath`, which will + walk down the keys provided. + + For each key, if the key does not exist, it is created. If it already + exists and it was inherited from its constructor, the constructor's + key is cloned. + + You can also pass false for `writable`, which will simply return + undefined if `prepareMetaPath` discovers any part of the path that + shared or undefined. + + @method metaPath + @for Ember + @param {Object} obj The object whose meta we are examining + @param {Array} path An array of keys to walk down + @param {Boolean} writable whether or not to create a new meta + (or meta property) if one does not already exist or if it's + shared with its constructor + */ + function metaPath(obj, path, writable) { + Ember.deprecate("Ember.metaPath is deprecated and will be removed from future releases."); + var _meta = meta(obj, writable); + var keyName, value; + + for (var i=0, l=path.length; i 1) { + watching[keyName]--; + } + } + + __exports__.unwatchKey = unwatchKey; + }); +enifed("ember-metal/watch_path", + ["ember-metal/utils","ember-metal/chains","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var metaFor = __dependency1__.meta; + var typeOf = __dependency1__.typeOf; + var ChainNode = __dependency2__.ChainNode; + + // get the chains for the current object. If the current object has + // chains inherited from the proto they will be cloned and reconfigured for + // the current object. + function chainsFor(obj, meta) { + var m = meta || metaFor(obj); + var ret = m.chains; + if (!ret) { + ret = m.chains = new ChainNode(null, null, obj); + } else if (ret.value() !== obj) { + ret = m.chains = ret.copy(obj); + } + return ret; + } + + function watchPath(obj, keyPath, meta) { + // can't watch length on Array - it is special... + if (keyPath === 'length' && typeOf(obj) === 'array') { return; } + + var m = meta || metaFor(obj); + var watching = m.watching; + + if (!watching[keyPath]) { // activate watching first time + watching[keyPath] = 1; + chainsFor(obj, m).add(keyPath); + } else { + watching[keyPath] = (watching[keyPath] || 0) + 1; + } + } + + __exports__.watchPath = watchPath;function unwatchPath(obj, keyPath, meta) { + var m = meta || metaFor(obj); + var watching = m.watching; + + if (watching[keyPath] === 1) { + watching[keyPath] = 0; + chainsFor(obj, m).remove(keyPath); + } else if (watching[keyPath] > 1) { + watching[keyPath]--; + } + } + + __exports__.unwatchPath = unwatchPath; + }); +enifed("ember-metal/watching", + ["ember-metal/utils","ember-metal/chains","ember-metal/watch_key","ember-metal/watch_path","ember-metal/path_cache","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; + /** + @module ember-metal + */ + + var typeOf = __dependency1__.typeOf; + var removeChainWatcher = __dependency2__.removeChainWatcher; + var flushPendingChains = __dependency2__.flushPendingChains; + var watchKey = __dependency3__.watchKey; + var unwatchKey = __dependency3__.unwatchKey; + var watchPath = __dependency4__.watchPath; + var unwatchPath = __dependency4__.unwatchPath; + + var isPath = __dependency5__.isPath; + + /** + Starts watching a property on an object. Whenever the property changes, + invokes `Ember.propertyWillChange` and `Ember.propertyDidChange`. This is the + primitive used by observers and dependent keys; usually you will never call + this method directly but instead use higher level methods like + `Ember.addObserver()` + + @private + @method watch + @for Ember + @param obj + @param {String} keyName + */ + function watch(obj, _keyPath, m) { + // can't watch length on Array - it is special... + if (_keyPath === 'length' && typeOf(obj) === 'array') { return; } + + if (!isPath(_keyPath)) { + watchKey(obj, _keyPath, m); + } else { + watchPath(obj, _keyPath, m); + } + } + + __exports__.watch = watch; + + function isWatching(obj, key) { + var meta = obj['__ember_meta__']; + return (meta && meta.watching[key]) > 0; + } + + __exports__.isWatching = isWatching;watch.flushPending = flushPendingChains; + + function unwatch(obj, _keyPath, m) { + // can't watch length on Array - it is special... + if (_keyPath === 'length' && typeOf(obj) === 'array') { return; } + + if (!isPath(_keyPath)) { + unwatchKey(obj, _keyPath, m); + } else { + unwatchPath(obj, _keyPath, m); + } + } + + __exports__.unwatch = unwatch;var NODE_STACK = []; + + /** + Tears down the meta on an object so that it can be garbage collected. + Multiple calls will have no effect. + + @method destroy + @for Ember + @param {Object} obj the object to destroy + @return {void} + */ + function destroy(obj) { + var meta = obj['__ember_meta__'], node, nodes, key, nodeObject; + if (meta) { + obj['__ember_meta__'] = null; + // remove chainWatchers to remove circular references that would prevent GC + node = meta.chains; + if (node) { + NODE_STACK.push(node); + // process tree + while (NODE_STACK.length > 0) { + node = NODE_STACK.pop(); + // push children + nodes = node._chains; + if (nodes) { + for (key in nodes) { + if (nodes.hasOwnProperty(key)) { + NODE_STACK.push(nodes[key]); + } + } + } + // remove chainWatcher in node object + if (node._watching) { + nodeObject = node._object; + if (nodeObject) { + removeChainWatcher(nodeObject, node._key, node); + } + } + } + } + } + } + + __exports__.destroy = destroy; + }); +enifed("ember-routing-htmlbars", + ["ember-metal/core","ember-htmlbars/helpers","ember-routing-htmlbars/helpers/outlet","ember-routing-htmlbars/helpers/render","ember-routing-htmlbars/helpers/link-to","ember-routing-htmlbars/helpers/action","ember-routing-htmlbars/helpers/query-params","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; + /** + Ember Routing HTMLBars Helpers + + @module ember + @submodule ember-routing-htmlbars + @requires ember-routing + */ + + var Ember = __dependency1__["default"]; + + var registerHelper = __dependency2__.registerHelper; + + var outletHelper = __dependency3__.outletHelper; + var renderHelper = __dependency4__.renderHelper; + var linkToHelper = __dependency5__.linkToHelper; + var deprecatedLinkToHelper = __dependency5__.deprecatedLinkToHelper; + var actionHelper = __dependency6__.actionHelper; + var queryParamsHelper = __dependency7__.queryParamsHelper; + + registerHelper('outlet', outletHelper); + registerHelper('render', renderHelper); + registerHelper('link-to', linkToHelper); + registerHelper('linkTo', deprecatedLinkToHelper); + registerHelper('action', actionHelper); + registerHelper('query-params', queryParamsHelper); + + __exports__["default"] = Ember; + }); +enifed("ember-routing-htmlbars/helpers/action", + ["ember-metal/core","ember-metal/utils","ember-metal/run_loop","ember-views/streams/utils","ember-views/system/utils","ember-views/system/action_manager","ember-metal/array","ember-metal/streams/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-routing-htmlbars + */ + + var Ember = __dependency1__["default"]; + // Handlebars, uuid, FEATURES, assert, deprecate + var uuid = __dependency2__.uuid; + var run = __dependency3__["default"]; + var readUnwrappedModel = __dependency4__.readUnwrappedModel; + var isSimpleClick = __dependency5__.isSimpleClick; + var ActionManager = __dependency6__["default"]; + var indexOf = __dependency7__.indexOf; + var isStream = __dependency8__.isStream; + + function actionArgs(parameters, actionName) { + var ret, i, l; + + if (actionName === undefined) { + ret = new Array(parameters.length); + for (i=0, l=parameters.length;i= 0) { + return true; + } + + for (var i=0, l=keys.length;i + click me + + ``` + + And application code + + ```javascript + App.ApplicationController = Ember.Controller.extend({ + actions: { + anActionName: function() { + } + } + }); + ``` + + Will result in the following rendered HTML + + ```html +
    +
    + click me +
    +
    + ``` + + Clicking "click me" will trigger the `anActionName` action of the + `App.ApplicationController`. In this case, no additional parameters will be passed. + + If you provide additional parameters to the helper: + + ```handlebars + + ``` + + Those parameters will be passed along as arguments to the JavaScript + function implementing the action. + + ### Event Propagation + + Events triggered through the action helper will automatically have + `.preventDefault()` called on them. You do not need to do so in your event + handlers. If you need to allow event propagation (to handle file inputs for + example) you can supply the `preventDefault=false` option to the `{{action}}` helper: + + ```handlebars +
    + + +
    + ``` + + To disable bubbling, pass `bubbles=false` to the helper: + + ```handlebars + + ``` + + If you need the default handler to trigger you should either register your + own event handler, or use event methods on your view class. See [Ember.View](/api/classes/Ember.View.html) + 'Responding to Browser Events' for more information. + + ### Specifying DOM event type + + By default the `{{action}}` helper registers for DOM `click` events. You can + supply an `on` option to the helper to specify a different DOM event name: + + ```handlebars +
    + click me +
    + ``` + + See `Ember.View` 'Responding to Browser Events' for a list of + acceptable DOM event names. + + ### Specifying whitelisted modifier keys + + By default the `{{action}}` helper will ignore click event with pressed modifier + keys. You can supply an `allowedKeys` option to specify which keys should not be ignored. + + ```handlebars +
    + click me +
    + ``` + + This way the `{{action}}` will fire when clicking with the alt key pressed down. + + Alternatively, supply "any" to the `allowedKeys` option to accept any combination of modifier keys. + + ```handlebars +
    + click me with any key pressed +
    + ``` + + ### Specifying a Target + + There are several possible target objects for `{{action}}` helpers: + + In a typical Ember application, where templates are managed through use of the + `{{outlet}}` helper, actions will bubble to the current controller, then + to the current route, and then up the route hierarchy. + + Alternatively, a `target` option can be provided to the helper to change + which object will receive the method call. This option must be a path + to an object, accessible in the current context: + + ```handlebars + {{! the application template }} +
    + click me +
    + ``` + + ```javascript + App.ApplicationView = Ember.View.extend({ + actions: { + anActionName: function(){} + } + }); + + ``` + + ### Additional Parameters + + You may specify additional parameters to the `{{action}}` helper. These + parameters are passed along as the arguments to the JavaScript function + implementing the action. + + ```handlebars + {{#each person in people}} +
    + click me +
    + {{/each}} + ``` + + Clicking "click me" will trigger the `edit` method on the current controller + with the value of `person` as a parameter. + + @method action + @for Ember.Handlebars.helpers + @param {String} actionName + @param {Object} [context]* + @param {Hash} options + */ + function actionHelper(params, hash, options, env) { + + var target; + if (!hash.target) { + target = this.getStream('controller'); + } else if (isStream(hash.target)) { + target = hash.target; + } else { + target = this.getStream(hash.target); + } + + // Ember.assert("You specified a quoteless path to the {{action}} helper which did not resolve to an action name (a string). Perhaps you meant to use a quoted actionName? (e.g. {{action 'save'}}).", !params[0].isStream); + // Ember.deprecate("You specified a quoteless path to the {{action}} helper which did not resolve to an action name (a string). Perhaps you meant to use a quoted actionName? (e.g. {{action 'save'}}).", params[0].isStream); + + var actionOptions = { + eventName: hash.on || "click", + parameters: params.slice(1), + view: this, + bubbles: hash.bubbles, + preventDefault: hash.preventDefault, + target: target, + withKeyCode: hash.withKeyCode + }; + + var actionId = ActionHelper.registerAction(params[0], actionOptions, hash.allowedKeys); + env.dom.setAttribute(options.element, 'data-ember-action', actionId); + } + + __exports__.actionHelper = actionHelper; + }); +enifed("ember-routing-htmlbars/helpers/link-to", + ["ember-metal/core","ember-routing-views/views/link","ember-metal/streams/utils","ember-runtime/mixins/controller","ember-htmlbars/utils/string","ember-htmlbars","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-routing-handlebars + */ + + var Ember = __dependency1__["default"]; + // assert + var LinkView = __dependency2__.LinkView; + var read = __dependency3__.read; + var isStream = __dependency3__.isStream; + var ControllerMixin = __dependency4__["default"]; + var escapeExpression = __dependency5__.escapeExpression; + + // We need the HTMLBars view helper from ensure ember-htmlbars. + // This ensures it is loaded first: + + /** + The `{{link-to}}` helper renders a link to the supplied + `routeName` passing an optionally supplied model to the + route as its `model` context of the route. The block + for `{{link-to}}` becomes the innerHTML of the rendered + element: + + ```handlebars + {{#link-to 'photoGallery'}} + Great Hamster Photos + {{/link-to}} + ``` + + ```html + + Great Hamster Photos + + ``` + + ### Supplying a tagName + By default `{{link-to}}` renders an `` element. This can + be overridden for a single use of `{{link-to}}` by supplying + a `tagName` option: + + ```handlebars + {{#link-to 'photoGallery' tagName="li"}} + Great Hamster Photos + {{/link-to}} + ``` + + ```html +
  • + Great Hamster Photos +
  • + ``` + + To override this option for your entire application, see + "Overriding Application-wide Defaults". + + ### Disabling the `link-to` helper + By default `{{link-to}}` is enabled. + any passed value to `disabled` helper property will disable the `link-to` helper. + + static use: the `disabled` option: + + ```handlebars + {{#link-to 'photoGallery' disabled=true}} + Great Hamster Photos + {{/link-to}} + ``` + + dynamic use: the `disabledWhen` option: + + ```handlebars + {{#link-to 'photoGallery' disabledWhen=controller.someProperty}} + Great Hamster Photos + {{/link-to}} + ``` + + any passed value to `disabled` will disable it except `undefined`. + to ensure that only `true` disable the `link-to` helper you can + override the global behaviour of `Ember.LinkView`. + + ```javascript + Ember.LinkView.reopen({ + disabled: Ember.computed(function(key, value) { + if (value !== undefined) { + this.set('_isDisabled', value === true); + } + return value === true ? get(this, 'disabledClass') : false; + }) + }); + ``` + + see "Overriding Application-wide Defaults" for more. + + ### Handling `href` + `{{link-to}}` will use your application's Router to + fill the element's `href` property with a url that + matches the path to the supplied `routeName` for your + routers's configured `Location` scheme, which defaults + to Ember.HashLocation. + + ### Handling current route + `{{link-to}}` will apply a CSS class name of 'active' + when the application's current route matches + the supplied routeName. For example, if the application's + current route is 'photoGallery.recent' the following + use of `{{link-to}}`: + + ```handlebars + {{#link-to 'photoGallery.recent'}} + Great Hamster Photos from the last week + {{/link-to}} + ``` + + will result in + + ```html +
    + Great Hamster Photos + + ``` + + The CSS class name used for active classes can be customized + for a single use of `{{link-to}}` by passing an `activeClass` + option: + + ```handlebars + {{#link-to 'photoGallery.recent' activeClass="current-url"}} + Great Hamster Photos from the last week + {{/link-to}} + ``` + + ```html + + Great Hamster Photos + + ``` + + To override this option for your entire application, see + "Overriding Application-wide Defaults". + + ### Supplying a model + An optional model argument can be used for routes whose + paths contain dynamic segments. This argument will become + the model context of the linked route: + + ```javascript + App.Router.map(function() { + this.resource("photoGallery", {path: "hamster-photos/:photo_id"}); + }); + ``` + + ```handlebars + {{#link-to 'photoGallery' aPhoto}} + {{aPhoto.title}} + {{/link-to}} + ``` + + ```html + + Tomster + + ``` + + ### Supplying multiple models + For deep-linking to route paths that contain multiple + dynamic segments, multiple model arguments can be used. + As the router transitions through the route path, each + supplied model argument will become the context for the + route with the dynamic segments: + + ```javascript + App.Router.map(function() { + this.resource("photoGallery", {path: "hamster-photos/:photo_id"}, function() { + this.route("comment", {path: "comments/:comment_id"}); + }); + }); + ``` + This argument will become the model context of the linked route: + + ```handlebars + {{#link-to 'photoGallery.comment' aPhoto comment}} + {{comment.body}} + {{/link-to}} + ``` + + ```html + + A+++ would snuggle again. + + ``` + + ### Supplying an explicit dynamic segment value + If you don't have a model object available to pass to `{{link-to}}`, + an optional string or integer argument can be passed for routes whose + paths contain dynamic segments. This argument will become the value + of the dynamic segment: + + ```javascript + App.Router.map(function() { + this.resource("photoGallery", {path: "hamster-photos/:photo_id"}); + }); + ``` + + ```handlebars + {{#link-to 'photoGallery' aPhotoId}} + {{aPhoto.title}} + {{/link-to}} + ``` + + ```html + + Tomster + + ``` + + When transitioning into the linked route, the `model` hook will + be triggered with parameters including this passed identifier. + + ### Allowing Default Action + + By default the `{{link-to}}` helper prevents the default browser action + by calling `preventDefault()` as this sort of action bubbling is normally + handled internally and we do not want to take the browser to a new URL (for + example). + + If you need to override this behavior specify `preventDefault=false` in + your template: + + ```handlebars + {{#link-to 'photoGallery' aPhotoId preventDefault=false}} + {{aPhotoId.title}} + {{/link-to}} + ``` + + ### Overriding attributes + You can override any given property of the Ember.LinkView + that is generated by the `{{link-to}}` helper by passing + key/value pairs, like so: + + ```handlebars + {{#link-to aPhoto tagName='li' title='Following this link will change your life' classNames='pic sweet'}} + Uh-mazing! + {{/link-to}} + ``` + + See [Ember.LinkView](/api/classes/Ember.LinkView.html) for a + complete list of overrideable properties. Be sure to also + check out inherited properties of `LinkView`. + + ### Overriding Application-wide Defaults + ``{{link-to}}`` creates an instance of Ember.LinkView + for rendering. To override options for your entire + application, reopen Ember.LinkView and supply the + desired values: + + ``` javascript + Ember.LinkView.reopen({ + activeClass: "is-active", + tagName: 'li' + }) + ``` + + It is also possible to override the default event in + this manner: + + ``` javascript + Ember.LinkView.reopen({ + eventName: 'customEventName' + }); + ``` + + @method link-to + @for Ember.Handlebars.helpers + @param {String} routeName + @param {Object} [context]* + @param [options] {Object} Handlebars key/value pairs of options, you can override any property of Ember.LinkView + @return {String} HTML string + @see {Ember.LinkView} + */ + function linkToHelper(params, hash, options, env) { + var shouldEscape = !hash.unescaped; + var queryParamsObject; + + Ember.assert("You must provide one or more parameters to the link-to helper.", params.length); + + var lastParam = params[params.length - 1]; + + if (lastParam && lastParam.isQueryParams) { + hash.queryParamsObject = queryParamsObject = params.pop(); + } + + if (hash.disabledWhen) { + hash.disabled = hash.disabledWhen; + delete hash.disabledWhen; + } + + if (!options.template) { + var linkTitle = params.shift(); + + if (isStream(linkTitle)) { + hash.linkTitle = { stream: linkTitle }; + } + + options.template = { + isHTMLBars: true, + render: function() { + var value = read(linkTitle); + if (value) { + return shouldEscape ? escapeExpression(value) : value; + } else { + return ""; + } + } + }; + } + + for (var i = 0; i < params.length; i++) { + if (isStream(params[i])) { + var lazyValue = params[i]; + + if (!lazyValue._isController) { + while (ControllerMixin.detect(lazyValue.value())) { + lazyValue = lazyValue.get('model'); + } + } + + params[i] = lazyValue; + } + } + + hash.params = params; + + options.helperName = options.helperName || 'link-to'; + + return env.helpers.view.helperFunction.call(this, [LinkView], hash, options, env); + } + + /** + See [link-to](/api/classes/Ember.Handlebars.helpers.html#method_link-to) + + @method linkTo + @for Ember.Handlebars.helpers + @deprecated + @param {String} routeName + @param {Object} [context]* + @return {String} HTML string + */ + function deprecatedLinkToHelper(params, hash, options, env) { + Ember.deprecate("The 'linkTo' view helper is deprecated in favor of 'link-to'"); + + return env.helpers['link-to'].helperFunction.call(this, params, hash, options, env); + } + + __exports__.deprecatedLinkToHelper = deprecatedLinkToHelper; + __exports__.linkToHelper = linkToHelper; + }); +enifed("ember-routing-htmlbars/helpers/outlet", + ["ember-metal/core","ember-metal/property_set","ember-routing-views/views/outlet","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-routing-htmlbars + */ + + var Ember = __dependency1__["default"]; + // assert + var set = __dependency2__.set; + var OutletView = __dependency3__.OutletView; + + /** + The `outlet` helper is a placeholder that the router will fill in with + the appropriate template based on the current state of the application. + + ``` handlebars + {{outlet}} + ``` + + By default, a template based on Ember's naming conventions will be rendered + into the `outlet` (e.g. `App.PostsRoute` will render the `posts` template). + + You can render a different template by using the `render()` method in the + route's `renderTemplate` hook. The following will render the `favoritePost` + template into the `outlet`. + + ``` javascript + App.PostsRoute = Ember.Route.extend({ + renderTemplate: function() { + this.render('favoritePost'); + } + }); + ``` + + You can create custom named outlets for more control. + + ``` handlebars + {{outlet 'favoritePost'}} + {{outlet 'posts'}} + ``` + + Then you can define what template is rendered into each outlet in your + route. + + + ``` javascript + App.PostsRoute = Ember.Route.extend({ + renderTemplate: function() { + this.render('favoritePost', { outlet: 'favoritePost' }); + this.render('posts', { outlet: 'posts' }); + } + }); + ``` + + You can specify the view that the outlet uses to contain and manage the + templates rendered into it. + + ``` handlebars + {{outlet view='sectionContainer'}} + ``` + + ``` javascript + App.SectionContainer = Ember.ContainerView.extend({ + tagName: 'section', + classNames: ['special'] + }); + ``` + + @method outlet + @for Ember.Handlebars.helpers + @param {String} property the property on the controller + that holds the view for this outlet + @return {String} HTML string + */ + function outletHelper(params, hash, options, env) { + var outletSource; + var viewName; + var viewClass; + var viewFullName; + + Ember.assert( + "Using {{outlet}} with an unquoted name is not supported.", + params.length === 0 || typeof params[0] === 'string' + ); + + var property = params[0] || 'main'; + + outletSource = this; + while (!outletSource.get('template.isTop')) { + outletSource = outletSource.get('_parentView'); + } + set(this, 'outletSource', outletSource); + + // provide controller override + viewName = hash.view; + + if (viewName) { + viewFullName = 'view:' + viewName; + Ember.assert( + "Using a quoteless view parameter with {{outlet}} is not supported." + + " Please update to quoted usage '{{outlet ... view=\"" + viewName + "\"}}.", + typeof hash.view === 'string' + ); + Ember.assert( + "The view name you supplied '" + viewName + "' did not resolve to a view.", + this.container.has(viewFullName) + ); + } + + viewClass = viewName ? this.container.lookupFactory(viewFullName) : hash.viewClass || OutletView; + + hash.currentViewBinding = '_view.outletSource._outlets.' + property; + + options.helperName = options.helperName || 'outlet'; + + return env.helpers.view.helperFunction.call(this, [viewClass], hash, options, env); + } + + __exports__.outletHelper = outletHelper; + }); +enifed("ember-routing-htmlbars/helpers/query-params", + ["ember-metal/core","ember-routing/system/query_params","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-routing-htmlbars + */ + + var Ember = __dependency1__["default"]; + // assert + var QueryParams = __dependency2__["default"]; + + /** + This is a sub-expression to be used in conjunction with the link-to helper. + It will supply url query parameters to the target route. + + Example + + {{#link-to 'posts' (query-params direction="asc")}}Sort{{/link-to}} + + @method query-params + @for Ember.Handlebars.helpers + @param {Object} hash takes a hash of query parameters + @return {String} HTML string + */ + function queryParamsHelper(params, hash) { + Ember.assert("The `query-params` helper only accepts hash parameters, e.g. (query-params queryParamPropertyName='foo') as opposed to just (query-params 'foo')", params.length === 0); + + return QueryParams.create({ + values: hash + }); + } + + __exports__.queryParamsHelper = queryParamsHelper; + }); +enifed("ember-routing-htmlbars/helpers/render", + ["ember-metal/core","ember-metal/error","ember-runtime/system/string","ember-routing/system/generate_controller","ember-htmlbars/helpers/view","ember-metal/streams/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-routing-htmlbars + */ + + var Ember = __dependency1__["default"]; + // assert, deprecate + var EmberError = __dependency2__["default"]; + var camelize = __dependency3__.camelize; + var generateControllerFactory = __dependency4__.generateControllerFactory; + var generateController = __dependency4__["default"]; + var ViewHelper = __dependency5__.ViewHelper; + var isStream = __dependency6__.isStream; + + /** + Calling ``{{render}}`` from within a template will insert another + template that matches the provided name. The inserted template will + access its properties on its own controller (rather than the controller + of the parent template). + + If a view class with the same name exists, the view class also will be used. + + Note: A given controller may only be used *once* in your app in this manner. + A singleton instance of the controller will be created for you. + + Example: + + ```javascript + App.NavigationController = Ember.Controller.extend({ + who: "world" + }); + ``` + + ```handlebars + + Hello, {{who}}. + ``` + + ```handlebars + +

    My great app

    + {{render "navigation"}} + ``` + + ```html +

    My great app

    +
    + Hello, world. +
    + ``` + + Optionally you may provide a second argument: a property path + that will be bound to the `model` property of the controller. + + If a `model` property path is specified, then a new instance of the + controller will be created and `{{render}}` can be used multiple times + with the same name. + + For example if you had this `author` template. + + ```handlebars +
    + Written by {{firstName}} {{lastName}}. + Total Posts: {{postCount}} +
    + ``` + + You could render it inside the `post` template using the `render` helper. + + ```handlebars +
    +

    {{title}}

    +
    {{body}}
    + {{render "author" author}} +
    + ``` + + @method render + @for Ember.Handlebars.helpers + @param {String} name + @param {Object?} context + @param {Hash} options + @return {String} HTML string + */ + function renderHelper(params, hash, options, env) { + var container, router, controller, view, initialContext; + + var name = params[0]; + var context = params[1]; + + container = this._keywords.controller.value().container; + router = container.lookup('router:main'); + + Ember.assert( + "The first argument of {{render}} must be quoted, e.g. {{render \"sidebar\"}}.", + typeof name === 'string' + ); + + Ember.assert( + "The second argument of {{render}} must be a path, e.g. {{render \"post\" post}}.", + params.length < 2 || isStream(params[1]) + ); + + + if (params.length === 1) { + // use the singleton controller + Ember.assert("You can only use the {{render}} helper once without a model object as its" + + " second argument, as in {{render \"post\" post}}.", !router || !router._lookupActiveView(name)); + } else if (params.length === 2) { + // create a new controller + initialContext = context.value(); + } else { + throw new EmberError("You must pass a templateName to render"); + } + + // # legacy namespace + name = name.replace(/\//g, '.'); + // \ legacy slash as namespace support + + + view = container.lookup('view:' + name) || container.lookup('view:default'); + + // provide controller override + var controllerName = hash.controller || name; + var controllerFullName = 'controller:' + controllerName; + + Ember.assert("The controller name you supplied '" + controllerName + + "' did not resolve to a controller.", !hash.controller || container.has(controllerFullName)); + + var parentController = this._keywords.controller.value(); + + // choose name + if (params.length > 1) { + var factory = container.lookupFactory(controllerFullName) || + generateControllerFactory(container, controllerName, initialContext); + + controller = factory.create({ + modelBinding: context, // TODO: Use a StreamBinding + parentController: parentController, + target: parentController + }); + + view.one('willDestroyElement', function() { + controller.destroy(); + }); + } else { + controller = container.lookup(controllerFullName) || + generateController(container, controllerName); + + controller.setProperties({ + target: parentController, + parentController: parentController + }); + } + + hash.viewName = camelize(name); + + var templateName = 'template:' + name; + Ember.assert("You used `{{render '" + name + "'}}`, but '" + name + "' can not be found as either" + + " a template or a view.", container.has("view:" + name) || container.has(templateName) || !!options.template); + hash.template = container.lookup(templateName); + + hash.controller = controller; + + if (router && !initialContext) { + router._connectActiveView(name, view); + } + + options.helperName = options.helperName || ('render "' + name + '"'); + + ViewHelper.instanceHelper(view, hash, options, env); + } + + __exports__.renderHelper = renderHelper; + }); +enifed("ember-routing-views", + ["ember-metal/core","ember-routing-views/views/link","ember-routing-views/views/outlet","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + /** + Ember Routing Views + + @module ember + @submodule ember-routing-views + @requires ember-routing + */ + + var Ember = __dependency1__["default"]; + + var LinkView = __dependency2__.LinkView; + var OutletView = __dependency3__.OutletView; + + Ember.LinkView = LinkView; + Ember.OutletView = OutletView; + + __exports__["default"] = Ember; + }); +enifed("ember-routing-views/views/link", + ["ember-metal/core","ember-metal/property_get","ember-metal/merge","ember-metal/run_loop","ember-metal/computed","ember-runtime/system/string","ember-metal/keys","ember-views/system/utils","ember-views/views/component","ember-routing/utils","ember-metal/streams/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-routing-views + */ + + var Ember = __dependency1__["default"]; + // FEATURES, Logger, assert + + var get = __dependency2__.get; + var merge = __dependency3__["default"]; + var run = __dependency4__["default"]; + var computed = __dependency5__.computed; + var fmt = __dependency6__.fmt; + var keys = __dependency7__["default"]; + var isSimpleClick = __dependency8__.isSimpleClick; + var EmberComponent = __dependency9__["default"]; + var routeArgs = __dependency10__.routeArgs; + var read = __dependency11__.read; + var subscribe = __dependency11__.subscribe; + + var numberOfContextsAcceptedByHandler = function(handler, handlerInfos) { + var req = 0; + for (var i = 0, l = handlerInfos.length; i < l; i++) { + req = req + handlerInfos[i].names.length; + if (handlerInfos[i].handler === handler) + break; + } + + return req; + }; + + /** + `Ember.LinkView` renders an element whose `click` event triggers a + transition of the application's instance of `Ember.Router` to + a supplied route by name. + + Instances of `LinkView` will most likely be created through + the `link-to` Handlebars helper, but properties of this class + can be overridden to customize application-wide behavior. + + @class LinkView + @namespace Ember + @extends Ember.View + @see {Handlebars.helpers.link-to} + **/ + var LinkView = Ember.LinkView = EmberComponent.extend({ + tagName: 'a', + + /** + @deprecated Use current-when instead. + @property currentWhen + */ + currentWhen: null, + + /** + Used to determine when this LinkView is active. + + @property currentWhen + */ + 'current-when': null, + + /** + Sets the `title` attribute of the `LinkView`'s HTML element. + + @property title + @default null + **/ + title: null, + + /** + Sets the `rel` attribute of the `LinkView`'s HTML element. + + @property rel + @default null + **/ + rel: null, + + /** + Sets the `tabindex` attribute of the `LinkView`'s HTML element. + + @property tabindex + @default null + **/ + tabindex: null, + + /** + Sets the `target` attribute of the `LinkView`'s HTML element. + + @property target + @default null + **/ + target: null, + + /** + The CSS class to apply to `LinkView`'s element when its `active` + property is `true`. + + @property activeClass + @type String + @default active + **/ + activeClass: 'active', + + /** + The CSS class to apply to `LinkView`'s element when its `loading` + property is `true`. + + @property loadingClass + @type String + @default loading + **/ + loadingClass: 'loading', + + /** + The CSS class to apply to a `LinkView`'s element when its `disabled` + property is `true`. + + @property disabledClass + @type String + @default disabled + **/ + disabledClass: 'disabled', + _isDisabled: false, + + /** + Determines whether the `LinkView` will trigger routing via + the `replaceWith` routing strategy. + + @property replace + @type Boolean + @default false + **/ + replace: false, + + /** + By default the `{{link-to}}` helper will bind to the `href` and + `title` attributes. It's discouraged that you override these defaults, + however you can push onto the array if needed. + + @property attributeBindings + @type Array | String + @default ['href', 'title', 'rel', 'tabindex', 'target'] + **/ + attributeBindings: ['href', 'title', 'rel', 'tabindex'], + + /** + By default the `{{link-to}}` helper will bind to the `active`, `loading`, and + `disabled` classes. It is discouraged to override these directly. + + @property classNameBindings + @type Array + @default ['active', 'loading', 'disabled'] + **/ + classNameBindings: ['active', 'loading', 'disabled'], + + /** + By default the `{{link-to}}` helper responds to the `click` event. You + can override this globally by setting this property to your custom + event name. + + This is particularly useful on mobile when one wants to avoid the 300ms + click delay using some sort of custom `tap` event. + + @property eventName + @type String + @default click + */ + eventName: 'click', + + // this is doc'ed here so it shows up in the events + // section of the API documentation, which is where + // people will likely go looking for it. + /** + Triggers the `LinkView`'s routing behavior. If + `eventName` is changed to a value other than `click` + the routing behavior will trigger on that custom event + instead. + + @event click + **/ + + /** + An overridable method called when LinkView objects are instantiated. + + Example: + + ```javascript + App.MyLinkView = Ember.LinkView.extend({ + init: function() { + this._super(); + Ember.Logger.log('Event is ' + this.get('eventName')); + } + }); + ``` + + 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. + + @method init + */ + init: function() { + this._super.apply(this, arguments); + + Ember.deprecate('Using currentWhen with {{link-to}} is deprecated in favor of `current-when`.', !this.currentWhen); + + // Map desired event name to invoke function + var eventName = get(this, 'eventName'); + this.on(eventName, this, this._invoke); + }, + + /** + This method is invoked by observers installed during `init` that fire + whenever the params change + + @private + @method _paramsChanged + @since 1.3.0 + */ + _paramsChanged: function() { + this.notifyPropertyChange('resolvedParams'); + }, + + /** + This is called to setup observers that will trigger a rerender. + + @private + @method _setupPathObservers + @since 1.3.0 + **/ + _setupPathObservers: function(){ + var params = this.params; + + var scheduledRerender = this._wrapAsScheduled(this.rerender); + var scheduledParamsChanged = this._wrapAsScheduled(this._paramsChanged); + + if (this.linkTitle) { + var linkTitle = this.linkTitle.stream || this.linkTitle; + subscribe(linkTitle, scheduledRerender, this); + } + + for (var i = 0; i < params.length; i++) { + subscribe(params[i], scheduledParamsChanged, this); + } + + var queryParamsObject = this.queryParamsObject; + if (queryParamsObject) { + var values = queryParamsObject.values; + for (var k in values) { + if (!values.hasOwnProperty(k)) { + continue; + } + + subscribe(values[k], scheduledParamsChanged, this); + } + } + }, + + afterRender: function(){ + this._super.apply(this, arguments); + this._setupPathObservers(); + }, + + /** + + Accessed as a classname binding to apply the `LinkView`'s `disabledClass` + CSS `class` to the element when the link is disabled. + + When `true` interactions with the element will not trigger route changes. + @property disabled + */ + disabled: computed(function computeLinkViewDisabled(key, value) { + if (value !== undefined) { this.set('_isDisabled', value); } + + return value ? get(this, 'disabledClass') : false; + }), + + /** + Accessed as a classname binding to apply the `LinkView`'s `activeClass` + CSS `class` to the element when the link is active. + + A `LinkView` is considered active when its `currentWhen` property is `true` + or the application's current route is the route the `LinkView` would trigger + transitions into. + + The `currentWhen` property can match against multiple routes by separating + route names using the ` ` (space) character. + + @property active + **/ + active: computed('loadedParams', function computeLinkViewActive() { + if (get(this, 'loading')) { return false; } + + var router = get(this, 'router'); + var loadedParams = get(this, 'loadedParams'); + var contexts = loadedParams.models; + var currentWhen = this['current-when'] || this.currentWhen; + var isCurrentWhenSpecified = Boolean(currentWhen); + currentWhen = currentWhen || loadedParams.targetRouteName; + + function isActiveForRoute(routeName) { + var handlers = router.router.recognizer.handlersFor(routeName); + var leafName = handlers[handlers.length-1].handler; + var maximumContexts = numberOfContextsAcceptedByHandler(routeName, handlers); + + // NOTE: any ugliness in the calculation of activeness is largely + // due to the fact that we support automatic normalizing of + // `resource` -> `resource.index`, even though there might be + // dynamic segments / query params defined on `resource.index` + // which complicates (and makes somewhat ambiguous) the calculation + // of activeness for links that link to `resource` instead of + // directly to `resource.index`. + + // if we don't have enough contexts revert back to full route name + // this is because the leaf route will use one of the contexts + if (contexts.length > maximumContexts) { + routeName = leafName; + } + + var args = routeArgs(routeName, contexts, null); + var isActive = router.isActive.apply(router, args); + if (!isActive) { return false; } + + var emptyQueryParams = Ember.isEmpty(Ember.keys(loadedParams.queryParams)); + + if (!isCurrentWhenSpecified && !emptyQueryParams && isActive) { + var visibleQueryParams = {}; + merge(visibleQueryParams, loadedParams.queryParams); + router._prepareQueryParams(loadedParams.targetRouteName, loadedParams.models, visibleQueryParams); + isActive = shallowEqual(visibleQueryParams, router.router.state.queryParams); + } + + return isActive; + } + + + currentWhen = currentWhen.split(' '); + for (var i = 0, len = currentWhen.length; i < len; i++) { + if (isActiveForRoute(currentWhen[i])) { + return get(this, 'activeClass'); + } + } + }), + + /** + Accessed as a classname binding to apply the `LinkView`'s `loadingClass` + CSS `class` to the element when the link is loading. + + A `LinkView` is considered loading when it has at least one + parameter whose value is currently null or undefined. During + this time, clicking the link will perform no transition and + emit a warning that the link is still in a loading state. + + @property loading + **/ + loading: computed('loadedParams', function computeLinkViewLoading() { + if (!get(this, 'loadedParams')) { return get(this, 'loadingClass'); } + }), + + /** + Returns the application's main router from the container. + + @private + @property router + **/ + router: computed(function() { + var controller = get(this, 'controller'); + if (controller && controller.container) { + return controller.container.lookup('router:main'); + } + }), + + /** + Event handler that invokes the link, activating the associated route. + + @private + @method _invoke + @param {Event} event + */ + _invoke: function(event) { + if (!isSimpleClick(event)) { return true; } + + if (this.preventDefault !== false) { + + var targetAttribute = get(this, 'target'); + if (!targetAttribute || targetAttribute === '_self') { + event.preventDefault(); + } + } + + if (this.bubbles === false) { event.stopPropagation(); } + + if (get(this, '_isDisabled')) { return false; } + + if (get(this, 'loading')) { + Ember.Logger.warn("This link-to is in an inactive loading state because at least one of its parameters presently has a null/undefined value, or the provided route name is invalid."); + return false; + } + + + var targetAttribute2 = get(this, 'target'); + if (targetAttribute2 && targetAttribute2 !== '_self') { + return false; + } + + + var router = get(this, 'router'); + var loadedParams = get(this, 'loadedParams'); + + var transition = router._doTransition(loadedParams.targetRouteName, loadedParams.models, loadedParams.queryParams); + if (get(this, 'replace')) { + transition.method('replace'); + } + + // Schedule eager URL update, but after we've given the transition + // a chance to synchronously redirect. + // We need to always generate the URL instead of using the href because + // the href will include any rootURL set, but the router expects a URL + // without it! Note that we don't use the first level router because it + // calls location.formatURL(), which also would add the rootURL! + var args = routeArgs(loadedParams.targetRouteName, loadedParams.models, transition.state.queryParams); + var url = router.router.generate.apply(router.router, args); + + run.scheduleOnce('routerTransitions', this, this._eagerUpdateUrl, transition, url); + }, + + /** + @private + @method _eagerUpdateUrl + @param transition + @param href + */ + _eagerUpdateUrl: function(transition, href) { + if (!transition.isActive || !transition.urlMethod) { + // transition was aborted, already ran to completion, + // or it has a null url-updated method. + return; + } + + if (href.indexOf('#') === 0) { + href = href.slice(1); + } + + // Re-use the routerjs hooks set up by the Ember router. + var routerjs = get(this, 'router.router'); + if (transition.urlMethod === 'update') { + routerjs.updateURL(href); + } else if (transition.urlMethod === 'replace') { + routerjs.replaceURL(href); + } + + // Prevent later update url refire. + transition.method(null); + }, + + /** + Computed property that returns an array of the + resolved parameters passed to the `link-to` helper, + e.g.: + + ```hbs + {{link-to a b '123' c}} + ``` + + will generate a `resolvedParams` of: + + ```js + [aObject, bObject, '123', cObject] + ``` + + @private + @property + @return {Array} + */ + resolvedParams: computed('router.url', function() { + var params = this.params; + var targetRouteName; + var models = []; + var onlyQueryParamsSupplied = (params.length === 0); + + if (onlyQueryParamsSupplied) { + var appController = this.container.lookup('controller:application'); + targetRouteName = get(appController, 'currentRouteName'); + } else { + targetRouteName = read(params[0]); + + for (var i = 1; i < params.length; i++) { + models.push(read(params[i])); + } + } + + var suppliedQueryParams = getResolvedQueryParams(this, targetRouteName); + + return { + targetRouteName: targetRouteName, + models: models, + queryParams: suppliedQueryParams + }; + }), + + /** + Computed property that returns the current route name, + dynamic segments, and query params. Returns falsy if + for null/undefined params to indicate that the link view + is still in a loading state. + + @private + @property + @return {Array} An array with the route name and any dynamic segments + **/ + loadedParams: computed('resolvedParams', function computeLinkViewRouteArgs() { + var router = get(this, 'router'); + if (!router) { return; } + + var resolvedParams = get(this, 'resolvedParams'); + var namedRoute = resolvedParams.targetRouteName; + + if (!namedRoute) { return; } + + Ember.assert(fmt("The attempt to link-to route '%@' failed. " + + "The router did not find '%@' in its possible routes: '%@'", + [namedRoute, namedRoute, keys(router.router.recognizer.names).join("', '")]), + router.hasRoute(namedRoute)); + + if (!paramsAreLoaded(resolvedParams.models)) { return; } + + return resolvedParams; + }), + + queryParamsObject: null, + + /** + Sets the element's `href` attribute to the url for + the `LinkView`'s targeted route. + + If the `LinkView`'s `tagName` is changed to a value other + than `a`, this property will be ignored. + + @property href + **/ + href: computed('loadedParams', function computeLinkViewHref() { + if (get(this, 'tagName') !== 'a') { return; } + + var router = get(this, 'router'); + var loadedParams = get(this, 'loadedParams'); + + if (!loadedParams) { + return get(this, 'loadingHref'); + } + + var visibleQueryParams = {}; + merge(visibleQueryParams, loadedParams.queryParams); + router._prepareQueryParams(loadedParams.targetRouteName, loadedParams.models, visibleQueryParams); + + var args = routeArgs(loadedParams.targetRouteName, loadedParams.models, visibleQueryParams); + var result = router.generate.apply(router, args); + return result; + }), + + /** + The default href value to use while a link-to is loading. + Only applies when tagName is 'a' + + @property loadingHref + @type String + @default # + */ + loadingHref: '#' + }); + + LinkView.toString = function() { return "LinkView"; }; + + + LinkView.reopen({ + attributeBindings: ['target'], + + /** + Sets the `target` attribute of the `LinkView`'s anchor element. + + @property target + @default null + **/ + target: null + }); + + + function getResolvedQueryParams(linkView, targetRouteName) { + var queryParamsObject = linkView.queryParamsObject; + var resolvedQueryParams = {}; + + if (!queryParamsObject) { return resolvedQueryParams; } + + var values = queryParamsObject.values; + for (var key in values) { + if (!values.hasOwnProperty(key)) { continue; } + resolvedQueryParams[key] = read(values[key]); + } + + return resolvedQueryParams; + } + + function paramsAreLoaded(params) { + for (var i = 0, len = params.length; i < len; ++i) { + var param = params[i]; + if (param === null || typeof param === 'undefined') { + return false; + } + } + return true; + } + + function shallowEqual(a, b) { + var k; + for (k in a) { + if (a.hasOwnProperty(k) && a[k] !== b[k]) { return false; } + } + for (k in b) { + if (b.hasOwnProperty(k) && a[k] !== b[k]) { return false; } + } + return true; + } + + __exports__.LinkView = LinkView; + }); +enifed("ember-routing-views/views/outlet", + ["ember-views/views/container_view","ember-views/views/metamorph_view","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-routing-views + */ + + var ContainerView = __dependency1__["default"]; + var _Metamorph = __dependency2__._Metamorph; + + var OutletView = ContainerView.extend(_Metamorph); + __exports__.OutletView = OutletView; + }); +enifed("ember-routing", + ["ember-metal/core","ember-routing/ext/run_loop","ember-routing/ext/controller","ember-routing/ext/view","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/generate_controller","ember-routing/system/controller_for","ember-routing/system/dsl","ember-routing/system/router","ember-routing/system/route","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __exports__) { + "use strict"; + /** + Ember Routing + + @module ember + @submodule ember-routing + @requires ember-views + */ + + var Ember = __dependency1__["default"]; + + // ES6TODO: Cleanup modules with side-effects below + + var EmberLocation = __dependency5__["default"]; + var NoneLocation = __dependency6__["default"]; + var HashLocation = __dependency7__["default"]; + var HistoryLocation = __dependency8__["default"]; + var AutoLocation = __dependency9__["default"]; + + var generateControllerFactory = __dependency10__.generateControllerFactory; + var generateController = __dependency10__["default"]; + var controllerFor = __dependency11__["default"]; + var RouterDSL = __dependency12__["default"]; + var Router = __dependency13__["default"]; + var Route = __dependency14__["default"]; + + Ember.Location = EmberLocation; + Ember.AutoLocation = AutoLocation; + Ember.HashLocation = HashLocation; + Ember.HistoryLocation = HistoryLocation; + Ember.NoneLocation = NoneLocation; + + Ember.controllerFor = controllerFor; + Ember.generateControllerFactory = generateControllerFactory; + Ember.generateController = generateController; + Ember.RouterDSL = RouterDSL; + Ember.Router = Router; + Ember.Route = Route; + + __exports__["default"] = Ember; + }); +enifed("ember-routing/ext/controller", + ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/computed","ember-metal/utils","ember-metal/merge","ember-runtime/mixins/controller","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // FEATURES, deprecate + var get = __dependency2__.get; + var set = __dependency3__.set; + var computed = __dependency4__.computed; + var typeOf = __dependency5__.typeOf; + var meta = __dependency5__.meta; + var merge = __dependency6__["default"]; + + var ControllerMixin = __dependency7__["default"]; + + /** + @module ember + @submodule ember-routing + */ + + ControllerMixin.reopen({ + concatenatedProperties: ['queryParams', '_pCacheMeta'], + + init: function() { + this._super.apply(this, arguments); + listenForQueryParamChanges(this); + }, + + /** + Defines which query parameters the controller accepts. + If you give the names ['category','page'] it will bind + the values of these query parameters to the variables + `this.category` and `this.page` + + @property queryParams + @public + */ + queryParams: null, + + /** + @property _qpDelegate + @private + */ + _qpDelegate: null, + + /** + @property _normalizedQueryParams + @private + */ + _normalizedQueryParams: computed(function() { + var m = meta(this); + if (m.proto !== this) { + return get(m.proto, '_normalizedQueryParams'); + } + + var queryParams = get(this, 'queryParams'); + if (queryParams._qpMap) { + return queryParams._qpMap; + } + + var qpMap = queryParams._qpMap = {}; + + for (var i = 0, len = queryParams.length; i < len; ++i) { + accumulateQueryParamDescriptors(queryParams[i], qpMap); + } + + return qpMap; + }), + + /** + @property _cacheMeta + @private + */ + _cacheMeta: computed(function() { + var m = meta(this); + if (m.proto !== this) { + return get(m.proto, '_cacheMeta'); + } + + var cacheMeta = {}; + var qpMap = get(this, '_normalizedQueryParams'); + for (var prop in qpMap) { + if (!qpMap.hasOwnProperty(prop)) { continue; } + + var qp = qpMap[prop]; + var scope = qp.scope; + var parts; + + if (scope === 'controller') { + parts = []; + } + + cacheMeta[prop] = { + parts: parts, // provided by route if 'model' scope + values: null, // provided by route + scope: scope, + prefix: "", + def: get(this, prop) + }; + } + + return cacheMeta; + }), + + /** + @method _updateCacheParams + @private + */ + _updateCacheParams: function(params) { + var cacheMeta = get(this, '_cacheMeta'); + for (var prop in cacheMeta) { + if (!cacheMeta.hasOwnProperty(prop)) { continue; } + var propMeta = cacheMeta[prop]; + propMeta.values = params; + + var cacheKey = this._calculateCacheKey(propMeta.prefix, propMeta.parts, propMeta.values); + var cache = this._bucketCache; + + if (cache) { + var value = cache.lookup(cacheKey, prop, propMeta.def); + set(this, prop, value); + } + } + }, + + /** + @method _qpChanged + @private + */ + _qpChanged: function(controller, _prop) { + var prop = _prop.substr(0, _prop.length-3); + var cacheMeta = get(controller, '_cacheMeta'); + var propCache = cacheMeta[prop]; + var cacheKey = controller._calculateCacheKey(propCache.prefix || "", propCache.parts, propCache.values); + var value = get(controller, prop); + + // 1. Update model-dep cache + var cache = this._bucketCache; + if (cache) { + controller._bucketCache.stash(cacheKey, prop, value); + } + + // 2. Notify a delegate (e.g. to fire a qp transition) + var delegate = controller._qpDelegate; + if (delegate) { + delegate(controller, prop); + } + }, + + /** + @method _calculateCacheKey + @private + */ + _calculateCacheKey: function(prefix, _parts, values) { + var parts = _parts || [], suffixes = ""; + for (var i = 0, len = parts.length; i < len; ++i) { + var part = parts[i]; + var value = get(values, part); + suffixes += "::" + part + ":" + value; + } + return prefix + suffixes.replace(ALL_PERIODS_REGEX, '-'); + }, + + /** + Transition the application into another route. The route may + be either a single route or route path: + + ```javascript + aController.transitionToRoute('blogPosts'); + aController.transitionToRoute('blogPosts.recentEntries'); + ``` + + Optionally supply a model for the route in question. The model + will be serialized into the URL using the `serialize` hook of + the route: + + ```javascript + aController.transitionToRoute('blogPost', aPost); + ``` + + If a literal is passed (such as a number or a string), it will + be treated as an identifier instead. In this case, the `model` + hook of the route will be triggered: + + ```javascript + aController.transitionToRoute('blogPost', 1); + ``` + + Multiple models will be applied last to first recursively up the + resource tree. + + ```javascript + App.Router.map(function() { + this.resource('blogPost', {path:':blogPostId'}, function(){ + this.resource('blogComment', {path: ':blogCommentId'}); + }); + }); + + aController.transitionToRoute('blogComment', aPost, aComment); + aController.transitionToRoute('blogComment', 1, 13); + ``` + + It is also possible to pass a URL (a string that starts with a + `/`). This is intended for testing and debugging purposes and + should rarely be used in production code. + + ```javascript + aController.transitionToRoute('/'); + aController.transitionToRoute('/blog/post/1/comment/13'); + aController.transitionToRoute('/blog/posts?sort=title'); + ``` + + An options hash with a `queryParams` property may be provided as + the final argument to add query parameters to the destination URL. + + ```javascript + aController.transitionToRoute('blogPost', 1, { + queryParams: {showComments: 'true'} + }); + + // if you just want to transition the query parameters without changing the route + aController.transitionToRoute({queryParams: {sort: 'date'}}); + ``` + + See also [replaceRoute](/api/classes/Ember.ControllerMixin.html#method_replaceRoute). + + @param {String} name the name of the route or a URL + @param {...Object} models the model(s) or identifier(s) to be used + while transitioning to the route. + @param {Object} [options] optional hash with a queryParams property + containing a mapping of query parameters + @for Ember.ControllerMixin + @method transitionToRoute + */ + transitionToRoute: function() { + // target may be either another controller or a router + var target = get(this, 'target'); + var method = target.transitionToRoute || target.transitionTo; + return method.apply(target, arguments); + }, + + /** + @deprecated + @for Ember.ControllerMixin + @method transitionTo + */ + transitionTo: function() { + Ember.deprecate("transitionTo is deprecated. Please use transitionToRoute."); + return this.transitionToRoute.apply(this, arguments); + }, + + /** + Transition into another route while replacing the current URL, if possible. + This will replace the current history entry instead of adding a new one. + Beside that, it is identical to `transitionToRoute` in all other respects. + + ```javascript + aController.replaceRoute('blogPosts'); + aController.replaceRoute('blogPosts.recentEntries'); + ``` + + Optionally supply a model for the route in question. The model + will be serialized into the URL using the `serialize` hook of + the route: + + ```javascript + aController.replaceRoute('blogPost', aPost); + ``` + + If a literal is passed (such as a number or a string), it will + be treated as an identifier instead. In this case, the `model` + hook of the route will be triggered: + + ```javascript + aController.replaceRoute('blogPost', 1); + ``` + + Multiple models will be applied last to first recursively up the + resource tree. + + ```javascript + App.Router.map(function() { + this.resource('blogPost', {path:':blogPostId'}, function(){ + this.resource('blogComment', {path: ':blogCommentId'}); + }); + }); + + aController.replaceRoute('blogComment', aPost, aComment); + aController.replaceRoute('blogComment', 1, 13); + ``` + + It is also possible to pass a URL (a string that starts with a + `/`). This is intended for testing and debugging purposes and + should rarely be used in production code. + + ```javascript + aController.replaceRoute('/'); + aController.replaceRoute('/blog/post/1/comment/13'); + ``` + + @param {String} name the name of the route or a URL + @param {...Object} models the model(s) or identifier(s) to be used + while transitioning to the route. + @for Ember.ControllerMixin + @method replaceRoute + */ + replaceRoute: function() { + // target may be either another controller or a router + var target = get(this, 'target'); + var method = target.replaceRoute || target.replaceWith; + return method.apply(target, arguments); + }, + + /** + @deprecated + @for Ember.ControllerMixin + @method replaceWith + */ + replaceWith: function() { + Ember.deprecate("replaceWith is deprecated. Please use replaceRoute."); + return this.replaceRoute.apply(this, arguments); + } + }); + + var ALL_PERIODS_REGEX = /\./g; + + function accumulateQueryParamDescriptors(_desc, accum) { + var desc = _desc; + var tmp; + if (typeOf(desc) === 'string') { + tmp = {}; + tmp[desc] = { as: null }; + desc = tmp; + } + + for (var key in desc) { + if (!desc.hasOwnProperty(key)) { return; } + + var singleDesc = desc[key]; + if (typeOf(singleDesc) === 'string') { + singleDesc = { as: singleDesc }; + } + + tmp = accum[key] || { as: null, scope: 'model' }; + merge(tmp, singleDesc); + + accum[key] = tmp; + } + } + + function listenForQueryParamChanges(controller) { + var qpMap = get(controller, '_normalizedQueryParams'); + for (var prop in qpMap) { + if (!qpMap.hasOwnProperty(prop)) { continue; } + controller.addObserver(prop + '.[]', controller, controller._qpChanged); + } + } + + + __exports__["default"] = ControllerMixin; + }); +enifed("ember-routing/ext/run_loop", + ["ember-metal/run_loop"], + function(__dependency1__) { + "use strict"; + var run = __dependency1__["default"]; + + /** + @module ember + @submodule ember-views + */ + + // Add a new named queue after the 'actions' queue (where RSVP promises + // resolve), which is used in router transitions to prevent unnecessary + // loading state entry if all context promises resolve on the + // 'actions' queue first. + run._addQueue('routerTransitions', 'actions'); + }); +enifed("ember-routing/ext/view", + ["ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-views/views/view","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + var get = __dependency1__.get; + var set = __dependency2__.set; + var run = __dependency3__["default"]; + var EmberView = __dependency4__["default"]; + + /** + @module ember + @submodule ember-routing + */ + + EmberView.reopen({ + + /** + Sets the private `_outlets` object on the view. + + @method init + */ + init: function() { + this._outlets = {}; + this._super(); + }, + + /** + Manually fill any of a view's `{{outlet}}` areas with the + supplied view. + + Example + + ```javascript + var MyView = Ember.View.extend({ + template: Ember.Handlebars.compile('Child view: {{outlet "main"}} ') + }); + var myView = MyView.create(); + myView.appendTo('body'); + // The html for myView now looks like: + //
    Child view:
    + + var FooView = Ember.View.extend({ + template: Ember.Handlebars.compile('

    Foo

    ') + }); + var fooView = FooView.create(); + myView.connectOutlet('main', fooView); + // The html for myView now looks like: + //
    Child view: + //

    Foo

    + //
    + ``` + @method connectOutlet + @param {String} outletName A unique name for the outlet + @param {Object} view An Ember.View + */ + connectOutlet: function(outletName, view) { + if (this._pendingDisconnections) { + delete this._pendingDisconnections[outletName]; + } + + if (this._hasEquivalentView(outletName, view)) { + view.destroy(); + return; + } + + var outlets = get(this, '_outlets'); + var container = get(this, 'container'); + var router = container && container.lookup('router:main'); + var renderedName = get(view, 'renderedName'); + + set(outlets, outletName, view); + + if (router && renderedName) { + router._connectActiveView(renderedName, view); + } + }, + + /** + Determines if the view has already been created by checking if + the view has the same constructor, template, and context as the + view in the `_outlets` object. + + @private + @method _hasEquivalentView + @param {String} outletName The name of the outlet we are checking + @param {Object} view An Ember.View + @return {Boolean} + */ + _hasEquivalentView: function(outletName, view) { + var existingView = get(this, '_outlets.'+outletName); + return existingView && + existingView.constructor === view.constructor && + existingView.get('template') === view.get('template') && + existingView.get('context') === view.get('context'); + }, + + /** + Removes an outlet from the view. + + Example + + ```javascript + var MyView = Ember.View.extend({ + template: Ember.Handlebars.compile('Child view: {{outlet "main"}} ') + }); + var myView = MyView.create(); + myView.appendTo('body'); + // myView's html: + //
    Child view:
    + + var FooView = Ember.View.extend({ + template: Ember.Handlebars.compile('

    Foo

    ') + }); + var fooView = FooView.create(); + myView.connectOutlet('main', fooView); + // myView's html: + //
    Child view: + //

    Foo

    + //
    + + myView.disconnectOutlet('main'); + // myView's html: + //
    Child view:
    + ``` + + @method disconnectOutlet + @param {String} outletName The name of the outlet to be removed + */ + disconnectOutlet: function(outletName) { + if (!this._pendingDisconnections) { + this._pendingDisconnections = {}; + } + this._pendingDisconnections[outletName] = true; + run.once(this, '_finishDisconnections'); + }, + + /** + Gets an outlet that is pending disconnection and then + nullifys the object on the `_outlet` object. + + @private + @method _finishDisconnections + */ + _finishDisconnections: function() { + if (this.isDestroyed) return; // _outlets will be gone anyway + var outlets = get(this, '_outlets'); + var pendingDisconnections = this._pendingDisconnections; + this._pendingDisconnections = null; + + for (var outletName in pendingDisconnections) { + set(outlets, outletName, null); + } + } + }); + + __exports__["default"] = EmberView; + }); +enifed("ember-routing/location/api", + ["ember-metal/core","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // deprecate, assert + + /** + @module ember + @submodule ember-routing + */ + + /** + Ember.Location returns an instance of the correct implementation of + the `location` API. + + ## Implementations + + You can pass an implementation name (`hash`, `history`, `none`) to force a + particular implementation to be used in your application. + + ### HashLocation + + 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. + + Example: + + ```javascript + App.Router.map(function() { + this.resource('posts', function() { + this.route('new'); + }); + }); + + App.Router.reopen({ + location: 'hash' + }); + ``` + + This will result in a posts.new url of `/#/posts/new`. + + ### HistoryLocation + + Using `HistoryLocation` results in URLs that are indistinguishable from a + standard URL. This relies upon the browser's `history` API. + + Example: + + ```javascript + App.Router.map(function() { + this.resource('posts', function() { + this.route('new'); + }); + }); + + App.Router.reopen({ + location: 'history' + }); + ``` + + This will result in a posts.new url of `/posts/new`. + + Keep in mind that your server must serve the Ember app at all the routes you + define. + + ### AutoLocation + + Using `AutoLocation`, the router will use the best Location class supported by + the browser it is running in. + + 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`. + + Example: + + ```javascript + App.Router.map(function() { + this.resource('posts', function() { + this.route('new'); + }); + }); + + App.Router.reopen({ + location: 'auto' + }); + ``` + + 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. + + 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. + + 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. + + ### NoneLocation + + 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()`. + + ## Location API + + Each location implementation must provide the following methods: + + * 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. + + Calling setURL or replaceURL will not trigger onUpdateURL callbacks. + + @class Location + @namespace Ember + @static + */ + __exports__["default"] = { + /** + This is deprecated in favor of using the container to lookup the location + implementation as desired. + + For example: + + ```javascript + // Given a location registered as follows: + container.register('location:history-test', HistoryTestLocation); + + // You could create a new instance via: + container.lookup('location:history-test'); + ``` + + @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); + + var implementationClass = this.implementations[implementation]; + Ember.assert("Ember.Location.create: " + implementation + " is not a valid implementation", !!implementationClass); + + return implementationClass.create.apply(implementationClass, arguments); + }, + + /** + This is deprecated in favor of using the container to register the + location implementation as desired. + + Example: + + ```javascript + Application.initializer({ + name: "history-test-location", + + initialize: function(container, application) { + application.register('location:history-test', HistoryTestLocation); + } + }); + ``` + + @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); + + this.implementations[name] = implementation; + }, + + implementations: {}, + _location: window.location, + + /** + Returns the current `location.hash` by parsing location.href since browsers + inconsistently URL-decode `location.hash`. + + https://bugzilla.mozilla.org/show_bug.cgi?id=483304 + + @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; + var hashIndex = href.indexOf('#'); + + if (hashIndex === -1) { + return ''; + } else { + return href.substr(hashIndex); + } + } + }; + }); +enifed("ember-routing/location/auto_location", + ["ember-metal/core","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__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // FEATURES + var set = __dependency2__.set; + + var EmberLocation = __dependency3__["default"]; + var HistoryLocation = __dependency4__["default"]; + var HashLocation = __dependency5__["default"]; + var NoneLocation = __dependency6__["default"]; + + /** + @module ember + @submodule ember-routing + */ + + /** + Ember.AutoLocation will select the best location option based off browser + support with the priority order: history, hash, none. + + Clean pushState paths accessed by hashchange-only browsers will be redirected + to the hash-equivalent and vice versa so future transitions are consistent. + + 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 AutoLocation + @namespace Ember + @static + */ + __exports__["default"] = { + + /** + @private + + 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, + + /** + @private + + Will be pre-pended to path upon state change. + + @since 1.5.1 + @property rootURL + @default '/' + */ + rootURL: '/', + + /** + @private + + Attached for mocking in tests + + @since 1.5.1 + @property _window + @default window + */ + _window: window, + + /** + @private + + Attached for mocking in tests + + @property location + @default window.location + */ + _location: window.location, + + /** + @private + + Attached for mocking in tests + + @since 1.5.1 + @property _history + @default window.history + */ + _history: window.history, + + /** + @private + + Attached for mocking in tests + + @since 1.5.1 + @property _HistoryLocation + @default Ember.HistoryLocation + */ + _HistoryLocation: HistoryLocation, + + /** + @private + + Attached for mocking in tests + + @since 1.5.1 + @property _HashLocation + @default Ember.HashLocation + */ + _HashLocation: HashLocation, + + /** + @private + + Attached for mocking in tests + + @since 1.5.1 + @property _NoneLocation + @default Ember.NoneLocation + */ + _NoneLocation: NoneLocation, + + /** + @private + + Returns location.origin or builds it if device doesn't support it. + + @method _getOrigin + */ + _getOrigin: function () { + var location = this._location; + var origin = location.origin; + + // Older browsers, especially IE, don't have origin + if (!origin) { + origin = location.protocol + '//' + location.hostname; + + if (location.port) { + origin += ':' + location.port; + } + } + + return origin; + }, + + /** + @private + + 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; + + // 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._history && 'pushState' in this._history); + }, + + /** + @private + + IE8 running in IE7 compatibility mode gives false positive, so we must also + check documentMode. + + @method _getSupportsHashChange + */ + _getSupportsHashChange: function () { + var _window = this._window; + var documentMode = _window.document.documentMode; + + return ('onhashchange' in _window && (documentMode === undefined || documentMode > 7 )); + }, + + /** + @private + + Redirects the browser using location.replace, prepending the locatin.origin + to prevent phishing attempts + + @method _replacePath + */ + _replacePath: function (path) { + this._location.replace(this._getOrigin() + path); + }, + + /** + @since 1.5.1 + @private + @method _getRootURL + */ + _getRootURL: function () { + return this.rootURL; + }, + + /** + @private + + Returns the current `location.pathname`, normalized for IE inconsistencies. + + @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 pathname; + }, + + /** + @private + + Returns normalized location.hash as an alias to Ember.Location._getHash + + @since 1.5.1 + @method _getHash + */ + _getHash: EmberLocation._getHash, + + /** + @private + + Returns location.search + + @since 1.5.1 + @method _getQuery + */ + _getQuery: function () { + return this._location.search; + }, + + /** + @private + + Returns the full pathname including query and hash + + @method _getFullPath + */ + _getFullPath: function () { + return this._getPath() + this._getQuery() + this._getHash(); + }, + + /** + @private + + 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 + */ + _getHistoryPath: function () { + var rootURL = this._getRootURL(); + var path = this._getPath(); + var hash = this._getHash(); + var query = this._getQuery(); + var rootURLIndex = path.indexOf(rootURL); + var routeHash, hashParts; + + Ember.assert('Path ' + path + ' does not start with the provided rootURL ' + rootURL, rootURLIndex === 0); + + // 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(); + + // 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); + } + + // This is the "expected" final order + path += routeHash; + path += query; + + if (hashParts.length) { + path += '#' + hashParts.join('#'); + } + } else { + path += query; + path += hash; + } + + return path; + }, + + /** + @private + + Returns the current path as it should appear for HashLocation supported + browsers. This may very well differ from the real current path. + + @method _getHashPath + */ + _getHashPath: function () { + var rootURL = this._getRootURL(); + var path = rootURL; + var historyPath = this._getHistoryPath(); + var routePath = historyPath.substr(rootURL.length); + + if (routePath !== '') { + if (routePath.charAt(0) !== '/') { + routePath = '/' + routePath; + } + + path += '#' + routePath; + } + + return path; + }, + + /** + Selects the best location option based off browser support and returns an + instance of that Location class. + + @see Ember.AutoLocation + @method create + */ + 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; + } + + var historyPath, hashPath; + var cancelRouterSetup = false; + var implementationClass = this._NoneLocation; + var currentPath = this._getFullPath(); + + if (this._getSupportsHistory()) { + historyPath = this._getHistoryPath(); + + // 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 { + + if (currentPath.substr(0, 2) === '/#') { + this._history.replaceState({ path: historyPath }, null, historyPath); + implementationClass = this._HistoryLocation; + } else { + cancelRouterSetup = true; + this._replacePath(historyPath); + } + } + + } else if (this._getSupportsHashChange()) { + hashPath = this._getHashPath(); + + // 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); + } + } + + var implementation = implementationClass.create.apply(implementationClass, arguments); + + if (cancelRouterSetup) { + set(implementation, 'cancelRouterSetup', true); + } + + return implementation; + } + }; + }); +enifed("ember-routing/location/hash_location", + ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-metal/utils","ember-runtime/system/object","ember-routing/location/api","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + var get = __dependency2__.get; + var set = __dependency3__.set; + var run = __dependency4__["default"]; + var guidFor = __dependency5__.guidFor; + + var EmberObject = __dependency6__["default"]; + var EmberLocation = __dependency7__["default"]; + + /** + @module ember + @submodule ember-routing + */ + + /** + `Ember.HashLocation` implements the location API using the browser's + hash. At present, it relies on a `hashchange` event existing in the + browser. + + @class HashLocation + @namespace Ember + @extends Ember.Object + */ + __exports__["default"] = EmberObject.extend({ + implementation: 'hash', + + init: function() { + set(this, 'location', get(this, '_location') || window.location); + }, + + /** + @private + + Returns normalized location.hash + + @since 1.5.1 + @method getHash + */ + getHash: EmberLocation._getHash, + + /** + Returns the normalized URL, constructed from `location.hash`. + + e.g. `#/foo` => `/foo` as well as `#/foo#bar` => `/foo#bar`. + + By convention, hashed paths must begin with a forward slash, otherwise they + are not treated as a path so we can distinguish intent. + + @private + @method getURL + */ + getURL: function() { + var originalPath = this.getHash().substr(1); + var outPath = originalPath; + + if (outPath.charAt(0) !== '/') { + outPath = '/'; + + // Only add the # if the path isn't empty. + // We do NOT want `/#` since the ampersand + // is only included (conventionally) when + // the location.hash has a value + if (originalPath) { + outPath += '#' + originalPath; + } + } + + return outPath; + }, + + /** + Set the `location.hash` and remembers what was set. This prevents + `onUpdateURL` callbacks from triggering when the hash was set by + `HashLocation`. + + @private + @method setURL + @param path {String} + */ + setURL: function(path) { + get(this, 'location').hash = path; + set(this, 'lastSetURL', path); + }, + + /** + Uses location.replace to update the url without a page reload + or history modification. + + @private + @method replaceURL + @param path {String} + */ + replaceURL: function(path) { + get(this, 'location').replace('#' + path); + set(this, 'lastSetURL', path); + }, + + /** + 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. + + @private + @method onUpdateURL + @param callback {Function} + */ + onUpdateURL: function(callback) { + var self = this; + var guid = guidFor(this); + + Ember.$(window).on('hashchange.ember-location-'+guid, function() { + run(function() { + var path = self.getURL(); + if (get(self, 'lastSetURL') === path) { return; } + + set(self, 'lastSetURL', null); + + callback(path); + }); + }); + }, + + /** + 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. + + @private + @method formatURL + @param url {String} + */ + formatURL: function(url) { + return '#' + url; + }, + + /** + Cleans up the HashLocation event listener. + + @private + @method willDestroy + */ + willDestroy: function() { + var guid = guidFor(this); + + Ember.$(window).off('hashchange.ember-location-'+guid); + } + }); + }); +enifed("ember-routing/location/history_location", + ["ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-runtime/system/object","ember-routing/location/api","ember-views/system/jquery","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { + "use strict"; + var get = __dependency1__.get; + var set = __dependency2__.set; + var guidFor = __dependency3__.guidFor; + + var EmberObject = __dependency4__["default"]; + var EmberLocation = __dependency5__["default"]; + var jQuery = __dependency6__["default"]; + + /** + @module ember + @submodule ember-routing + */ + + var popstateFired = false; + var supportsHistoryState = window.history && 'state' in window.history; + + /** + Ember.HistoryLocation implements the location API using the browser's + history.pushState API. + + @class HistoryLocation + @namespace Ember + @extends Ember.Object + */ + __exports__["default"] = EmberObject.extend({ + implementation: 'history', + + init: function() { + set(this, 'location', get(this, 'location') || window.location); + set(this, 'baseURL', jQuery('base').attr('href') || ''); + }, + + /** + Used to set state on first call to setURL + + @private + @method initState + */ + initState: function() { + set(this, 'history', get(this, 'history') || window.history); + this.replaceState(this.formatURL(this.getURL())); + }, + + /** + Will be pre-pended to path upon state change + + @property rootURL + @default '/' + */ + rootURL: '/', + + /** + Returns the current `location.pathname` without `rootURL` or `baseURL` + + @private + @method getURL + @return url {String} + */ + getURL: function() { + var rootURL = get(this, 'rootURL'); + var location = get(this, 'location'); + var path = location.pathname; + var baseURL = get(this, 'baseURL'); + + rootURL = rootURL.replace(/\/$/, ''); + baseURL = baseURL.replace(/\/$/, ''); + + var url = path.replace(baseURL, '').replace(rootURL, ''); + var search = location.search || ''; + + url += search; + url += this.getHash(); + + return url; + }, + + /** + Uses `history.pushState` to update the url without a page reload. + + @private + @method setURL + @param path {String} + */ + setURL: function(path) { + var state = this.getState(); + path = this.formatURL(path); + + if (!state || state.path !== path) { + this.pushState(path); + } + }, + + /** + Uses `history.replaceState` to update the url without a page reload + or history modification. + + @private + @method replaceURL + @param path {String} + */ + replaceURL: function(path) { + var state = this.getState(); + path = this.formatURL(path); + + if (!state || state.path !== path) { + this.replaceState(path); + } + }, + + /** + 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. + + @private + @method getState + @return state {Object} + */ + getState: function() { + return supportsHistoryState ? get(this, 'history').state : this._historyState; + }, + + /** + Pushes a new state. + + @private + @method pushState + @param path {String} + */ + pushState: function(path) { + var state = { path: path }; + + get(this, 'history').pushState(state, null, path); + + // store state if browser doesn't support `history.state` + if (!supportsHistoryState) { + this._historyState = state; + } + + // used for webkit workaround + this._previousURL = this.getURL(); + }, + + /** + Replaces the current state. + + @private + @method replaceState + @param path {String} + */ + replaceState: function(path) { + var state = { path: path }; + get(this, 'history').replaceState(state, null, path); + + // store state if browser doesn't support `history.state` + if (!supportsHistoryState) { + this._historyState = state; + } + + // used for webkit workaround + this._previousURL = this.getURL(); + }, + + /** + Register a callback to be invoked whenever the browser + history changes, including using forward and back buttons. + + @private + @method onUpdateURL + @param callback {Function} + */ + onUpdateURL: function(callback) { + var guid = guidFor(this); + var 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()); + }); + }, + + /** + Used when using `{{action}}` helper. The url is always appended to the rootURL. + + @private + @method formatURL + @param url {String} + @return formatted url {String} + */ + formatURL: function(url) { + var rootURL = get(this, 'rootURL'); + var baseURL = get(this, 'baseURL'); + + if (url !== '') { + rootURL = rootURL.replace(/\/$/, ''); + baseURL = baseURL.replace(/\/$/, ''); + } else if(baseURL.match(/^\//) && rootURL.match(/^\//)) { + baseURL = baseURL.replace(/\/$/, ''); + } + + return baseURL + rootURL + url; + }, + + /** + Cleans up the HistoryLocation event listener. + + @private + @method willDestroy + */ + willDestroy: function() { + var guid = guidFor(this); - @method forEach - @param {Function} callback The callback to execute - @param {Object} [target] The target object to use - @return {Object} receiver + jQuery(window).off('popstate.ember-location-'+guid); + }, + + /** + @private + + Returns normalized location.hash + + @method getHash */ - forEach: function(callback, target) { - if (typeof callback !== "function") throw new TypeError() ; - var len = get(this, 'length'), last = null, context = popCtx(); + getHash: EmberLocation._getHash + }); + }); +enifed("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"]; - if (target === undefined) target = null; + /** + @module ember + @submodule ember-routing + */ - for(var idx=0;idx " + fullName, { fullName: fullName }); + } + + return instance; + } + }); +enifed("ember-routing/system/query_params", + ["ember-runtime/system/object","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var EmberObject = __dependency1__["default"]; + + __exports__["default"] = EmberObject.extend({ + isQueryParams: true, + values: null + }); + }); +enifed("ember-routing/system/route", + ["ember-metal/core","ember-metal/error","ember-metal/property_get","ember-metal/property_set","ember-metal/get_properties","ember-metal/enumerable_utils","ember-metal/is_none","ember-metal/computed","ember-metal/merge","ember-metal/utils","ember-metal/run_loop","ember-metal/keys","ember-runtime/copy","ember-runtime/system/string","ember-runtime/system/object","ember-runtime/mixins/evented","ember-runtime/mixins/action_handler","ember-routing/system/generate_controller","ember-routing/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // FEATURES, A, deprecate, assert, Logger + var EmberError = __dependency2__["default"]; + var get = __dependency3__.get; + var set = __dependency4__.set; + var getProperties = __dependency5__["default"]; + var forEach = __dependency6__.forEach; + var replace = __dependency6__.replace; + var isNone = __dependency7__["default"]; + var computed = __dependency8__.computed; + var merge = __dependency9__["default"]; + var isArray = __dependency10__.isArray; + var typeOf = __dependency10__.typeOf; + var run = __dependency11__["default"]; + var keys = __dependency12__["default"]; + var copy = __dependency13__["default"]; + var classify = __dependency14__.classify; + var EmberObject = __dependency15__["default"]; + var Evented = __dependency16__["default"]; + var ActionHandler = __dependency17__["default"]; + var generateController = __dependency18__["default"]; + var stashParamNames = __dependency19__.stashParamNames; + + var slice = Array.prototype.slice; + + function K() { return this; } + + /** + @module ember + @submodule ember-routing + */ + + /** + The `Ember.Route` class is used to define individual routes. Refer to + the [routing guide](http://emberjs.com/guides/routing/) for documentation. + + @class Route + @namespace Ember + @extends Ember.Object + @uses Ember.ActionHandler + */ + var Route = EmberObject.extend(ActionHandler, { /** - Similar to map, this specialized function returns the value of the named - property on all items in the enumeration. + Configuration hash for this route's queryParams. The possible + configuration options and their defaults are as follows + (assuming a query param whose URL key is `page`): - @method mapBy - @param {String} key name of the property - @return {Array} The mapped array. + ```javascript + queryParams: { + page: { + // By default, controller query param properties don't + // cause a full transition when they are changed, but + // rather only cause the URL to update. Setting + // `refreshModel` to true will cause an "in-place" + // transition to occur, whereby the model hooks for + // this route (and any child routes) will re-fire, allowing + // you to reload models (e.g., from the server) using the + // updated query param values. + refreshModel: false, + + // By default, changes to controller query param properties + // cause the URL to update via `pushState`, which means an + // item will be added to the browser's history, allowing + // you to use the back button to restore the app to the + // previous state before the query param property was changed. + // Setting `replace` to true will use `replaceState` (or its + // hash location equivalent), which causes no browser history + // item to be added. This options name and default value are + // the same as the `link-to` helper's `replace` option. + replace: false + } + } + ``` + + @property queryParams + @for Ember.Route + @type Hash */ - mapBy: function(key) { - return this.map(function(next) { - return get(next, key); - }); - }, + queryParams: {}, /** - Similar to map, this specialized function returns the value of the named - property on all items in the enumeration. + @private - @method mapProperty - @param {String} key name of the property - @return {Array} The mapped array. - @deprecated Use `mapBy` instead + @property _qp */ + _qp: computed(function() { + var controllerName = this.controllerName || this.routeName; + var controllerClass = this.container.lookupFactory('controller:' + controllerName); - mapProperty: aliasMethod('mapBy'), + if (!controllerClass) { + return defaultQPMeta; + } + + var controllerProto = controllerClass.proto(); + var qpProps = get(controllerProto, '_normalizedQueryParams'); + var cacheMeta = get(controllerProto, '_cacheMeta'); + + var qps = [], map = {}, self = this; + for (var propName in qpProps) { + if (!qpProps.hasOwnProperty(propName)) { continue; } + + var desc = qpProps[propName]; + var urlKey = desc.as || this.serializeQueryParamKey(propName); + var defaultValue = get(controllerProto, propName); + + if (isArray(defaultValue)) { + defaultValue = Ember.A(defaultValue.slice()); + } + + var type = typeOf(defaultValue); + var defaultValueSerialized = this.serializeQueryParam(defaultValue, urlKey, type); + var fprop = controllerName + ':' + propName; + var qp = { + def: defaultValue, + sdef: defaultValueSerialized, + type: type, + urlKey: urlKey, + prop: propName, + fprop: fprop, + ctrl: controllerName, + cProto: controllerProto, + svalue: defaultValueSerialized, + cacheType: desc.scope, + route: this, + cacheMeta: cacheMeta[propName] + }; + + map[propName] = map[urlKey] = map[fprop] = qp; + qps.push(qp); + } + + return { + qps: qps, + map: map, + states: { + active: function(controller, prop) { + return self._activeQPChanged(controller, map[prop]); + }, + allowOverrides: function(controller, prop) { + return self._updatingQPChanged(controller, map[prop]); + }, + changingKeys: function(controller, prop) { + return self._updateSerializedQPValue(controller, map[prop]); + } + } + }; + }), /** - Returns an array with all of the items in the enumeration that the passed - function returns true for. This method corresponds to `filter()` defined in - JavaScript 1.6. + @private - The callback method you provide should have the following signature (all - parameters are optional): + @property _names + */ + _names: null, - ```javascript - function(item, index, enumerable); - ``` + /** + @private - - `item` is the current item in the iteration. - - `index` is the current index in the iteration. - - `enumerable` is the enumerable object itself. + @method _stashNames + */ + _stashNames: function(_handlerInfo, dynamicParent) { + var handlerInfo = _handlerInfo; + if (this._names) { return; } + var names = this._names = handlerInfo._names; - It should return the `true` to include the item in the results, `false` - otherwise. + if (!names.length) { + handlerInfo = dynamicParent; + names = handlerInfo && handlerInfo._names || []; + } - Note that in addition to a callback, you can also pass an optional target - object that will be set as `this` on the context. This is a good way - to give your iterator function access to the current object. + var qps = get(this, '_qp.qps'); + var len = qps.length; - @method filter - @param {Function} callback The callback to execute - @param {Object} [target] The target object to use - @return {Array} A filtered array. + var namePaths = new Array(names.length); + for (var a = 0, nlen = names.length; a < nlen; ++a) { + namePaths[a] = handlerInfo.name + '.' + names[a]; + } + + for (var i = 0; i < len; ++i) { + var qp = qps[i]; + var cacheMeta = qp.cacheMeta; + if (cacheMeta.scope === 'model') { + cacheMeta.parts = namePaths; + } + cacheMeta.prefix = qp.ctrl; + } + }, + + /** + @private + + @property _updateSerializedQPValue */ - filter: function(callback, target) { - var ret = Ember.A(); - this.forEach(function(x, idx, i) { - if (callback.call(target, x, idx, i)) ret.push(x); - }); - return ret ; + _updateSerializedQPValue: function(controller, qp) { + var value = get(controller, qp.prop); + qp.svalue = this.serializeQueryParam(value, qp.urlKey, qp.type); }, /** - Returns an array with all of the items in the enumeration where the passed - function returns false for. This method is the inverse of filter(). + @private - The callback method you provide should have the following signature (all - parameters are optional): + @property _activeQPChanged + */ + _activeQPChanged: function(controller, qp) { + var value = get(controller, qp.prop); + this.router._queuedQPChanges[qp.fprop] = value; + run.once(this, this._fireQueryParamTransition); + }, - ```javascript - function(item, index, enumerable); - ``` + /** + @private + @method _updatingQPChanged + */ + _updatingQPChanged: function(controller, qp) { + var router = this.router; + if (!router._qpUpdates) { + router._qpUpdates = {}; + } + router._qpUpdates[qp.urlKey] = true; + }, - - *item* is the current item in the iteration. - - *index* is the current index in the iteration - - *enumerable* is the enumerable object itself. + mergedProperties: ['events', 'queryParams'], - It should return the a falsey value to include the item in the results. + /** + Retrieves parameters, for current route using the state.params + variable and getQueryParamsFor, using the supplied routeName. - Note that in addition to a callback, you can also pass an optional target - object that will be set as "this" on the context. This is a good way - to give your iterator function access to the current object. + @method paramsFor + @param {String} routename - @method reject - @param {Function} callback The callback to execute - @param {Object} [target] The target object to use - @return {Array} A rejected array. - */ - reject: function(callback, target) { - return this.filter(function() { - return !(apply(target, callback, arguments)); - }); + */ + paramsFor: function(name) { + var route = this.container.lookup('route:' + name); + + if (!route) { + return {}; + } + + var transition = this.router.router.activeTransition; + var state = transition ? transition.state : this.router.router.state; + + var params = {}; + merge(params, state.params[name]); + merge(params, getQueryParamsFor(route, state)); + + return params; + }, + + /** + Serializes the query parameter key + + @method serializeQueryParamKey + @param {String} controllerPropertyName + */ + serializeQueryParamKey: function(controllerPropertyName) { + return controllerPropertyName; }, /** - Returns an array with just the items with the matched property. You - can pass an optional second argument with the target value. Otherwise - this will match any property that evaluates to `true`. + Serializes value of the query parameter based on defaultValueType - @method filterBy - @param {String} key the property to test - @param {*} [value] optional value to test against. - @return {Array} filtered array + @method serializeQueryParam + @param {Object} value + @param {String} urlKey + @param {String} defaultValueType */ - filterBy: function(key, value) { - return this.filter(apply(this, iter, arguments)); + serializeQueryParam: function(value, urlKey, defaultValueType) { + // urlKey isn't used here, but anyone overriding + // can use it to provide serialization specific + // to a certain query param. + if (defaultValueType === 'array') { + return JSON.stringify(value); + } + return '' + value; }, /** - Returns an array with just the items with the matched property. You - can pass an optional second argument with the target value. Otherwise - this will match any property that evaluates to `true`. + Deserializes value of the query parameter based on defaultValueType - @method filterProperty - @param {String} key the property to test - @param {String} [value] optional value to test against. - @return {Array} filtered array - @deprecated Use `filterBy` instead - */ - filterProperty: aliasMethod('filterBy'), + @method deserializeQueryParam + @param {Object} value + @param {String} urlKey + @param {String} defaultValueType + */ + deserializeQueryParam: function(value, urlKey, defaultValueType) { + // urlKey isn't used here, but anyone overriding + // can use it to provide deserialization specific + // to a certain query param. + + // Use the defaultValueType of the default value (the initial value assigned to a + // controller query param property), to intelligently deserialize and cast. + if (defaultValueType === 'boolean') { + return (value === 'true') ? true : false; + } else if (defaultValueType === 'number') { + return (Number(value)).valueOf(); + } else if (defaultValueType === 'array') { + return Ember.A(JSON.parse(value)); + } + return value; + }, - /** - Returns an array with the items that do not have truthy values for - key. You can pass an optional second argument with the target value. Otherwise - this will match any property that evaluates to false. - @method rejectBy - @param {String} key the property to test - @param {String} [value] optional value to test against. - @return {Array} rejected array + /** + @private + @property _fireQueryParamTransition */ - rejectBy: function(key, value) { - var exactValue = function(item) { return get(item, key) === value; }, - hasValue = function(item) { return !!get(item, key); }, - use = (arguments.length === 2 ? exactValue : hasValue); - - return this.reject(use); + _fireQueryParamTransition: function() { + this.transitionTo({ queryParams: this.router._queuedQPChanges }); + this.router._queuedQPChanges = {}; }, /** - Returns an array with the items that do not have truthy values for - key. You can pass an optional second argument with the target value. Otherwise - this will match any property that evaluates to false. + @private - @method rejectProperty - @param {String} key the property to test - @param {String} [value] optional value to test against. - @return {Array} rejected array - @deprecated Use `rejectBy` instead + @property _optionsForQueryParam */ - rejectProperty: aliasMethod('rejectBy'), + _optionsForQueryParam: function(qp) { + return get(this, 'queryParams.' + qp.urlKey) || get(this, 'queryParams.' + qp.prop) || {}; + }, /** - Returns the first item in the array for which the callback returns true. - This method works similar to the `filter()` method defined in JavaScript 1.6 - except that it will stop working on the array once a match is found. - - The callback method you provide should have the following signature (all - parameters are optional): + A hook you can use to reset controller values either when the model + changes or the route is exiting. ```javascript - function(item, index, enumerable); + App.ArticlesRoute = Ember.Route.extend({ + // ... + + resetController: function (controller, isExiting, transition) { + if (isExiting) { + controller.set('page', 1); + } + } + }); ``` - - `item` is the current item in the iteration. - - `index` is the current index in the iteration. - - `enumerable` is the enumerable object itself. + @method resetController + @param {Controller} controller instance + @param {Boolean} isExiting + @param {Object} transition + @since 1.7.0 + */ + resetController: K, - It should return the `true` to include the item in the results, `false` - otherwise. + /** + @private - Note that in addition to a callback, you can also pass an optional target - object that will be set as `this` on the context. This is a good way - to give your iterator function access to the current object. + @method exit + */ + exit: function() { + this.deactivate(); + + this.trigger('deactivate'); + + this.teardownViews(); + }, - @method find - @param {Function} callback The callback to execute - @param {Object} [target] The target object to use - @return {Object} Found item or `undefined`. + /** + @private + + @method _reset + @since 1.7.0 */ - find: function(callback, target) { - var len = get(this, 'length') ; - if (target === undefined) target = null; + _reset: function(isExiting, transition) { + var controller = this.controller; - var last = null, next, found = false, ret ; - var context = popCtx(); - for(var idx=0;idx1) args = a_slice.call(arguments, 1); + ```javascript + App.Router.map(function() { + this.resource('articles', { path: '/articles' }, function() { + this.route('new'); + }); + }); - this.forEach(function(x, idx) { - var method = x && x[methodName]; - if ('function' === typeof method) { - ret[idx] = args ? apply(x, method, args) : x[methodName](); + App.IndexRoute = Ember.Route.extend({ + actions: { + transitionToNewArticle: function() { + this.transitionTo('articles.new'); + } } - }, this); + }); + ``` - return ret; - }, + Multiple Models Example - /** - Simply converts the enumerable into a genuine array. The order is not - guaranteed. Corresponds to the method implemented by Prototype. + ```javascript + App.Router.map(function() { + this.route('index'); - @method toArray - @return {Array} the enumerable as an array. - */ - toArray: function() { - var ret = Ember.A(); - this.forEach(function(o, idx) { ret[idx] = o; }); - return ret; - }, + this.resource('breakfast', { path: ':breakfastId' }, function() { + this.resource('cereal', { path: ':cerealId' }); + }); + }); - /** - Returns a copy of the array with all null and undefined elements removed. + App.IndexRoute = Ember.Route.extend({ + actions: { + moveToChocolateCereal: function() { + var cereal = { cerealId: 'ChocolateYumminess' }; + var breakfast = { breakfastId: 'CerealAndMilk' }; - ```javascript - var arr = ["a", null, "c", undefined]; - arr.compact(); // ["a", "c"] + this.transitionTo('cereal', breakfast, cereal); + } + } + }); ``` - @method compact - @return {Array} the array without null and undefined elements. - */ - compact: function() { - return this.filter(function(value) { return value != null; }); - }, - - /** - Returns a new enumerable that excludes the passed value. The default - implementation returns an array regardless of the receiver type unless - the receiver does not contain the value. + Nested Route with Query String Example ```javascript - var arr = ["a", "b", "a", "c"]; - arr.without("a"); // ["b", "c"] + App.Router.map(function() { + this.resource('fruits', function() { + this.route('apples'); + }); + }); + + App.IndexRoute = Ember.Route.extend({ + actions: { + transitionToApples: function() { + this.transitionTo('fruits.apples', {queryParams: {color: 'red'}}); + } + } + }); ``` - @method without - @param {Object} value - @return {Ember.Enumerable} + @method transitionTo + @param {String} name the name of the route or a URL + @param {...Object} models the model(s) or identifier(s) to be used while + transitioning to the route. + @param {Object} [options] optional hash with a queryParams property + containing a mapping of query parameters + @return {Transition} the transition object associated with this + attempted transition */ - without: function(value) { - if (!this.contains(value)) return this; // nothing to do - var ret = Ember.A(); - this.forEach(function(k) { - if (k !== value) ret[ret.length] = k; - }) ; - return ret ; + transitionTo: function(name, context) { + var router = this.router; + return router.transitionTo.apply(router, arguments); }, /** - Returns a new enumerable that contains only unique values. The default - implementation returns an array regardless of the receiver type. + Perform a synchronous transition into another route without attempting + to resolve promises, update the URL, or abort any currently active + asynchronous transitions (i.e. regular transitions caused by + `transitionTo` or URL changes). - ```javascript - var arr = ["a", "a", "b", "b"]; - arr.uniq(); // ["a", "b"] - ``` + This method is handy for performing intermediate transitions on the + way to a final destination route, and is called internally by the + default implementations of the `error` and `loading` handlers. - @method uniq - @return {Ember.Enumerable} - */ - uniq: function() { - var ret = Ember.A(); - this.forEach(function(k) { - if (a_indexOf(ret, k)<0) ret.push(k); - }); - return ret; + @method intermediateTransitionTo + @param {String} name the name of the route + @param {...Object} models the model(s) to be used while transitioning + to the route. + @since 1.2.0 + */ + intermediateTransitionTo: function() { + var router = this.router; + router.intermediateTransitionTo.apply(router, arguments); }, /** - This property will trigger anytime the enumerable's content changes. - You can observe this property to be notified of changes to the enumerables - content. - - For plain enumerables, this property is read only. `Array` overrides - this method. - - @property [] - @type Array - @return this - */ - '[]': computed(function(key, value) { - return this; - }), - - // .......................................................... - // ENUMERABLE OBSERVERS - // + Refresh the model on this route and any child routes, firing the + `beforeModel`, `model`, and `afterModel` hooks in a similar fashion + to how routes are entered when transitioning in from other route. + The current route params (e.g. `article_id`) will be passed in + to the respective model hooks, and if a different model is returned, + `setupController` and associated route hooks will re-fire as well. - /** - Registers an enumerable observer. Must implement `Ember.EnumerableObserver` - mixin. + An example usage of this method is re-querying the server for the + latest information using the same parameters as when the route + was first entered. - @method addEnumerableObserver - @param {Object} target - @param {Hash} [opts] - @return this - */ - addEnumerableObserver: function(target, opts) { - var willChange = (opts && opts.willChange) || 'enumerableWillChange', - didChange = (opts && opts.didChange) || 'enumerableDidChange'; + Note that this will cause `model` hooks to fire even on routes + that were provided a model object when the route was initially + entered. - var hasObservers = get(this, 'hasEnumerableObservers'); - if (!hasObservers) propertyWillChange(this, 'hasEnumerableObservers'); - addListener(this, '@enumerable:before', target, willChange); - addListener(this, '@enumerable:change', target, didChange); - if (!hasObservers) propertyDidChange(this, 'hasEnumerableObservers'); - return this; + @method refresh + @return {Transition} the transition object associated with this + attempted transition + @since 1.4.0 + */ + refresh: function() { + return this.router.router.refresh(this); }, /** - Removes a registered enumerable observer. + Transition into another route while replacing the current URL, if possible. + This will replace the current history entry instead of adding a new one. + Beside that, it is identical to `transitionTo` in all other respects. See + 'transitionTo' for additional information regarding multiple models. - @method removeEnumerableObserver - @param {Object} target - @param {Hash} [opts] - @return this - */ - removeEnumerableObserver: function(target, opts) { - var willChange = (opts && opts.willChange) || 'enumerableWillChange', - didChange = (opts && opts.didChange) || 'enumerableDidChange'; + Example - var hasObservers = get(this, 'hasEnumerableObservers'); - if (hasObservers) propertyWillChange(this, 'hasEnumerableObservers'); - removeListener(this, '@enumerable:before', target, willChange); - removeListener(this, '@enumerable:change', target, didChange); - if (hasObservers) propertyDidChange(this, 'hasEnumerableObservers'); - return this; - }, + ```javascript + App.Router.map(function() { + this.route('index'); + this.route('secret'); + }); - /** - Becomes true whenever the array currently has observers watching changes - on the array. + App.SecretRoute = Ember.Route.extend({ + afterModel: function() { + if (!authorized()){ + this.replaceWith('index'); + } + } + }); + ``` - @property hasEnumerableObservers - @type Boolean + @method replaceWith + @param {String} name the name of the route or a URL + @param {...Object} models the model(s) or identifier(s) to be used while + transitioning to the route. + @return {Transition} the transition object associated with this + attempted transition */ - hasEnumerableObservers: computed(function() { - return hasListeners(this, '@enumerable:change') || hasListeners(this, '@enumerable:before'); - }), - + replaceWith: function() { + var router = this.router; + return router.replaceWith.apply(router, arguments); + }, /** - Invoke this method just before the contents of your enumerable will - change. You can either omit the parameters completely or pass the objects - to be removed or added if available or just a count. - - @method enumerableContentWillChange - @param {Ember.Enumerable|Number} removing An enumerable of the objects to - be removed or the number of items to be removed. - @param {Ember.Enumerable|Number} adding An enumerable of the objects to be - added or the number of items to be added. - @chainable - */ - enumerableContentWillChange: function(removing, adding) { - - var removeCnt, addCnt, hasDelta; - - if ('number' === typeof removing) removeCnt = removing; - else if (removing) removeCnt = get(removing, 'length'); - else removeCnt = removing = -1; + Sends an action to the router, which will delegate it to the currently + active route hierarchy per the bubbling rules explained under `actions`. - if ('number' === typeof adding) addCnt = adding; - else if (adding) addCnt = get(adding,'length'); - else addCnt = adding = -1; + Example - hasDelta = addCnt<0 || removeCnt<0 || addCnt-removeCnt!==0; + ```javascript + App.Router.map(function() { + this.route('index'); + }); - if (removing === -1) removing = null; - if (adding === -1) adding = null; + App.ApplicationRoute = Ember.Route.extend({ + actions: { + track: function(arg) { + console.log(arg, 'was clicked'); + } + } + }); - propertyWillChange(this, '[]'); - if (hasDelta) propertyWillChange(this, 'length'); - sendEvent(this, '@enumerable:before', [this, removing, adding]); + App.IndexRoute = Ember.Route.extend({ + actions: { + trackIfDebug: function(arg) { + if (debug) { + this.send('track', arg); + } + } + } + }); + ``` - return this; + @method send + @param {String} name the name of the action to trigger + @param {...*} args + */ + send: function() { + if (this.router || !Ember.testing) { + this.router.send.apply(this.router, arguments); + } else { + var name = arguments[0]; + var args = slice.call(arguments, 1); + var action = this._actions[name]; + if (action) { + return this._actions[name].apply(this, args); + } + } }, /** - Invoke this method when the contents of your enumerable has changed. - This will notify any observers watching for content changes. If your are - implementing an ordered enumerable (such as an array), also pass the - start and end values where the content changed so that it can be used to - notify range observers. + This hook is the entry point for router.js - @method enumerableContentDidChange - @param {Ember.Enumerable|Number} removing An enumerable of the objects to - be removed or the number of items to be removed. - @param {Ember.Enumerable|Number} adding An enumerable of the objects to - be added or the number of items to be added. - @chainable + @private + @method setup */ - enumerableContentDidChange: function(removing, adding) { - var removeCnt, addCnt, hasDelta; + setup: function(context, transition) { + var controllerName = this.controllerName || this.routeName; + var controller = this.controllerFor(controllerName, true); - if ('number' === typeof removing) removeCnt = removing; - else if (removing) removeCnt = get(removing, 'length'); - else removeCnt = removing = -1; + if (!controller) { + controller = this.generateController(controllerName, context); + } - if ('number' === typeof adding) addCnt = adding; - else if (adding) addCnt = get(adding, 'length'); - else addCnt = adding = -1; + // Assign the route's controller so that it can more easily be + // referenced in action handlers + this.controller = controller; - hasDelta = addCnt<0 || removeCnt<0 || addCnt-removeCnt!==0; + if (this.setupControllers) { + Ember.deprecate("Ember.Route.setupControllers is deprecated. Please use Ember.Route.setupController(controller, model) instead."); + this.setupControllers(controller, context); + } else { + var states = get(this, '_qp.states'); + if (transition) { + // Update the model dep values used to calculate cache keys. + stashParamNames(this.router, transition.state.handlerInfos); + controller._qpDelegate = states.changingKeys; + controller._updateCacheParams(transition.params); + } + controller._qpDelegate = states.allowOverrides; - if (removing === -1) removing = null; - if (adding === -1) adding = null; + if (transition) { + var qpValues = getQueryParamsFor(this, transition.state); + controller.setProperties(qpValues); + } - sendEvent(this, '@enumerable:change', [this, removing, adding]); - if (hasDelta) propertyDidChange(this, 'length'); - propertyDidChange(this, '[]'); + this.setupController(controller, context, transition); + } - return this ; + if (this.renderTemplates) { + Ember.deprecate("Ember.Route.renderTemplates is deprecated. Please use Ember.Route.renderTemplate(controller, model) instead."); + this.renderTemplates(context); + } else { + this.renderTemplate(controller, context); + } }, /** - Converts the enumerable into an array and sorts by the keys - specified in the argument. - - You may provide multiple arguments to sort by multiple properties. - - @method sortBy - @param {String} property name(s) to sort on - @return {Array} The sorted array. - @since 1.2.0 - */ - sortBy: function() { - var sortKeys = arguments; - return this.toArray().sort(function(a, b){ - for(var i = 0; i < sortKeys.length; i++) { - var key = sortKeys[i], - propA = get(a, key), - propB = get(b, key); - // return 1 or -1 else continue to the next sortKey - var compareValue = compare(propA, propB); - if (compareValue) { return compareValue; } - } - return 0; - }); - } - }); - - __exports__["default"] = Enumerable; - }); -define("ember-runtime/mixins/evented", - ["ember-metal/mixin","ember-metal/events","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var Mixin = __dependency1__.Mixin; - var addListener = __dependency2__.addListener; - var removeListener = __dependency2__.removeListener; - var hasListeners = __dependency2__.hasListeners; - var sendEvent = __dependency2__.sendEvent; + This hook is the first of the route entry validation hooks + called when an attempt is made to transition into a route + or one of its children. It is called before `model` and + `afterModel`, and is appropriate for cases when: - /** - @module ember - @submodule ember-runtime - */ + 1) A decision can be made to redirect elsewhere without + needing to resolve the model first. + 2) Any async operations need to occur first before the + model is attempted to be resolved. - /** - This mixin allows for Ember objects to subscribe to and emit events. + This hook is provided the current `transition` attempt + as a parameter, which can be used to `.abort()` the transition, + save it for a later `.retry()`, or retrieve values set + on it from a previous hook. You can also just call + `this.transitionTo` to another route to implicitly + abort the `transition`. - ```javascript - App.Person = Ember.Object.extend(Ember.Evented, { - greet: function() { - // ... - this.trigger('greet'); - } - }); + You can return a promise from this hook to pause the + transition until the promise resolves (or rejects). This could + be useful, for instance, for retrieving async code from + the server that is required to enter a route. - var person = App.Person.create(); + ```javascript + App.PostRoute = Ember.Route.extend({ + beforeModel: function(transition) { + if (!App.Post) { + return Ember.$.getScript('/models/post.js'); + } + } + }); + ``` - person.on('greet', function() { - console.log('Our person has greeted'); - }); + If `App.Post` doesn't exist in the above example, + `beforeModel` will use jQuery's `getScript`, which + returns a promise that resolves after the server has + successfully retrieved and executed the code from the + server. Note that if an error were to occur, it would + be passed to the `error` hook on `Ember.Route`, but + it's also possible to handle errors specific to + `beforeModel` right from within the hook (to distinguish + from the shared error handling behavior of the `error` + hook): - person.greet(); + ```javascript + App.PostRoute = Ember.Route.extend({ + beforeModel: function(transition) { + if (!App.Post) { + var self = this; + return Ember.$.getScript('post.js').then(null, function(e) { + self.transitionTo('help'); - // outputs: 'Our person has greeted' - ``` + // Note that the above transitionTo will implicitly + // halt the transition. If you were to return + // nothing from this promise reject handler, + // according to promise semantics, that would + // convert the reject into a resolve and the + // transition would continue. To propagate the + // error so that it'd be handled by the `error` + // hook, you would have to + return Ember.RSVP.reject(e); + }); + } + } + }); + ``` - You can also chain multiple event subscriptions: + @method beforeModel + @param {Transition} transition + @return {Promise} if the value returned from this hook is + a promise, the transition will pause until the transition + resolves. Otherwise, non-promise return values are not + utilized in any way. + */ + beforeModel: K, - ```javascript - person.on('greet', function() { - console.log('Our person has greeted'); - }).one('greet', function() { - console.log('Offer one-time special'); - }).off('event', this, forgetThis); - ``` + /** + This hook is called after this route's model has resolved. + It follows identical async/promise semantics to `beforeModel` + but is provided the route's resolved model in addition to + the `transition`, and is therefore suited to performing + logic that can only take place after the model has already + resolved. - @class Evented - @namespace Ember - */ - var Evented = Mixin.create({ + ```javascript + App.PostsRoute = Ember.Route.extend({ + afterModel: function(posts, transition) { + if (posts.get('length') === 1) { + this.transitionTo('post.show', posts.get('firstObject')); + } + } + }); + ``` - /** - Subscribes to a named event with given function. + Refer to documentation for `beforeModel` for a description + of transition-pausing semantics when a promise is returned + from this hook. - ```javascript - person.on('didLoad', function() { - // fired once the person has loaded - }); - ``` + @method afterModel + @param {Object} resolvedModel the value returned from `model`, + or its resolved value if it was a promise + @param {Transition} transition + @return {Promise} if the value returned from this hook is + a promise, the transition will pause until the transition + resolves. Otherwise, non-promise return values are not + utilized in any way. + */ + afterModel: K, - An optional target can be passed in as the 2nd argument that will - be set as the "this" for the callback. This is a good way to give your - function access to the object triggering the event. When the target - parameter is used the callback becomes the third argument. + /** + A hook you can implement to optionally redirect to another route. - @method on - @param {String} name The name of the event - @param {Object} [target] The "this" binding for the callback - @param {Function} method The callback to execute - @return this + If you call `this.transitionTo` from inside of this hook, this route + will not be entered in favor of the other hook. + + `redirect` and `afterModel` behave very similarly and are + called almost at the same time, but they have an important + distinction in the case that, from one of these hooks, a + redirect into a child route of this route occurs: redirects + from `afterModel` essentially invalidate the current attempt + to enter this route, and will result in this route's `beforeModel`, + `model`, and `afterModel` hooks being fired again within + the new, redirecting transition. Redirects that occur within + the `redirect` hook, on the other hand, will _not_ cause + these hooks to be fired again the second time around; in + other words, by the time the `redirect` hook has been called, + both the resolved model and attempted entry into this route + are considered to be fully validated. + + @method redirect + @param {Object} model the model for this route + @param {Transition} transition the transition object associated with the current transition */ - on: function(name, target, method) { - addListener(this, name, target, method); - return this; - }, + redirect: K, /** - Subscribes a function to a named event and then cancels the subscription - after the first time the event is triggered. It is good to use ``one`` when - you only care about the first time an event has taken place. - - This function takes an optional 2nd argument that will become the "this" - value for the callback. If this argument is passed then the 3rd argument - becomes the function. + Called when the context is changed by router.js. - @method one - @param {String} name The name of the event - @param {Object} [target] The "this" binding for the callback - @param {Function} method The callback to execute - @return this + @private + @method contextDidChange */ - one: function(name, target, method) { - if (!method) { - method = target; - target = null; - } - - addListener(this, name, target, method, true); - return this; + contextDidChange: function() { + this.currentModel = this.context; }, /** - Triggers a named event for the object. Any additional arguments - will be passed as parameters to the functions that are subscribed to the - event. + A hook you can implement to convert the URL into the model for + this route. ```javascript - person.on('didEat', function(food) { - console.log('person ate some ' + food); + App.Router.map(function() { + this.resource('post', { path: '/posts/:post_id' }); }); + ``` - person.trigger('didEat', 'broccoli'); + The model for the `post` route is `store.find('post', params.post_id)`. - // outputs: person ate some broccoli - ``` - @method trigger - @param {String} name The name of the event - @param {Object...} args Optional arguments to pass on - */ - trigger: function(name) { - var args = [], i, l; - for (i = 1, l = arguments.length; i < l; i++) { - args.push(arguments[i]); - } - sendEvent(this, name, args); - }, + By default, if your route has a dynamic segment ending in `_id`: - /** - Cancels subscription for given name, target, and method. + * The model class is determined from the segment (`post_id`'s + class is `App.Post`) + * The find method is called on the model class with the value of + the dynamic segment. - @method off - @param {String} name The name of the event - @param {Object} target The target of the subscription - @param {Function} method The function of the subscription - @return this - */ - off: function(name, target, method) { - removeListener(this, name, target, method); - return this; - }, + Note that for routes with dynamic segments, this hook is not always + executed. If the route is entered through a transition (e.g. when + using the `link-to` Handlebars helper or the `transitionTo` method + of routes), and a model context is already provided this hook + is not called. - /** - Checks to see if object has any subscriptions for named event. + A model context does not include a primitive string or number, + which does cause the model hook to be called. - @method has - @param {String} name The name of the event - @return {Boolean} does the object have a subscription for event - */ - has: function(name) { - return hasListeners(this, name); - } - }); + Routes without dynamic segments will always execute the model hook. - __exports__["default"] = Evented; - }); -define("ember-runtime/mixins/freezable", - ["ember-metal/mixin","ember-metal/property_get","ember-metal/property_set","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-runtime - */ + ```javascript + // no dynamic segment, model hook always called + this.transitionTo('posts'); - var Mixin = __dependency1__.Mixin; - var get = __dependency2__.get; - var set = __dependency3__.set; + // model passed in, so model hook not called + thePost = store.find('post', 1); + this.transitionTo('post', thePost); - /** - The `Ember.Freezable` mixin implements some basic methods for marking an - object as frozen. Once an object is frozen it should be read only. No changes - may be made the internal state of the object. + // integer passed in, model hook is called + this.transitionTo('post', 1); + ``` - ## Enforcement - To fully support freezing in your subclass, you must include this mixin and - override any method that might alter any property on the object to instead - raise an exception. You can check the state of an object by checking the - `isFrozen` property. + This hook follows the asynchronous/promise semantics + described in the documentation for `beforeModel`. In particular, + if a promise returned from `model` fails, the error will be + handled by the `error` hook on `Ember.Route`. - Although future versions of JavaScript may support language-level freezing - object objects, that is not the case today. Even if an object is freezable, - it is still technically possible to modify the object, even though it could - break other parts of your application that do not expect a frozen object to - change. It is, therefore, very important that you always respect the - `isFrozen` property on all freezable objects. + Example - ## Example Usage + ```javascript + App.PostRoute = Ember.Route.extend({ + model: function(params) { + return this.store.find('post', params.post_id); + } + }); + ``` - The example below shows a simple object that implement the `Ember.Freezable` - protocol. + @method model + @param {Object} params the parameters extracted from the URL + @param {Transition} transition + @return {Object|Promise} the model for this route. If + a promise is returned, the transition will pause until + the promise resolves, and the resolved value of the promise + will be used as the model for this route. + */ + model: function(params, transition) { + var match, name, sawParams, value; - ```javascript - Contact = Ember.Object.extend(Ember.Freezable, { - firstName: null, - lastName: null, + var queryParams = get(this, '_qp.map'); - // swaps the names - swapNames: function() { - if (this.get('isFrozen')) throw Ember.FROZEN_ERROR; - var tmp = this.get('firstName'); - this.set('firstName', this.get('lastName')); - this.set('lastName', tmp); - return this; - } + for (var prop in params) { + if (prop === 'queryParams' || (queryParams && prop in queryParams)) { + continue; + } - }); + if (match = prop.match(/^(.*)_id$/)) { + name = match[1]; + value = params[prop]; + } + sawParams = true; + } - c = Contact.create({ firstName: "John", lastName: "Doe" }); - c.swapNames(); // returns c - c.freeze(); - c.swapNames(); // EXCEPTION - ``` + if (!name && sawParams) { return copy(params); } + else if (!name) { + if (transition.resolveIndex < 1) { return; } - ## Copying + var parentModel = transition.state.handlerInfos[transition.resolveIndex-1].context; - Usually the `Ember.Freezable` protocol is implemented in cooperation with the - `Ember.Copyable` protocol, which defines a `frozenCopy()` method that will - return a frozen object, if the object implements this method as well. + return parentModel; + } - @class Freezable - @namespace Ember - @since Ember 0.9 - */ - var Freezable = Mixin.create({ + return this.findModel(name, value); + }, /** - Set to `true` when the object is frozen. Use this property to detect - whether your object is frozen or not. + @private + @method deserialize + @param {Object} params the parameters extracted from the URL + @param {Transition} transition + @return {Object|Promise} the model for this route. - @property isFrozen - @type Boolean - */ - isFrozen: false, + Router.js hook. + */ + deserialize: function(params, transition) { + return this.model(this.paramsFor(this.routeName), transition); + }, /** - Freezes the object. Once this method has been called the object should - no longer allow any properties to be edited. - @method freeze - @return {Object} receiver + @method findModel + @param {String} type the model type + @param {Object} value the value passed to find */ - freeze: function() { - if (get(this, 'isFrozen')) return this; - set(this, 'isFrozen', true); - return this; - } + findModel: function(){ + var store = get(this, 'store'); + return store.find.apply(store, arguments); + }, - }); + /** + Store property provides a hook for data persistence libraries to inject themselves. - var FROZEN_ERROR = "Frozen object cannot be modified."; + By default, this store property provides the exact same functionality previously + in the model hook. - __exports__.Freezable = Freezable; - __exports__.FROZEN_ERROR = FROZEN_ERROR; - }); -define("ember-runtime/mixins/mutable_array", - ["ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/error","ember-metal/mixin","ember-runtime/mixins/array","ember-runtime/mixins/mutable_enumerable","ember-runtime/mixins/enumerable","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-runtime - */ + Currently, the required interface is: + `store.find(modelName, findArguments)` - // require('ember-runtime/mixins/array'); - // require('ember-runtime/mixins/mutable_enumerable'); + @method store + @param {Object} store + */ + store: computed(function(){ + var container = this.container; + var routeName = this.routeName; + var namespace = get(this, 'router.namespace'); - // .......................................................... - // CONSTANTS - // + return { + find: function(name, value) { + var modelClass = container.lookupFactory('model:' + name); - var OUT_OF_RANGE_EXCEPTION = "Index out of range"; - var EMPTY = []; + Ember.assert("You used the dynamic segment " + name + "_id in your route " + + routeName + ", but " + namespace + "." + classify(name) + + " did not exist and you did not override your route's `model` " + + "hook.", !!modelClass); - // .......................................................... - // HELPERS - // + if (!modelClass) { return; } - var get = __dependency1__.get; - var set = __dependency2__.set; - var isArray = __dependency3__.isArray; - var EmberError = __dependency4__["default"]; - var Mixin = __dependency5__.Mixin; - var required = __dependency5__.required; - var EmberArray = __dependency6__["default"]; - var MutableEnumerable = __dependency7__["default"]; - var Enumerable = __dependency8__["default"]; - /** - This mixin defines the API for modifying array-like objects. These methods - can be applied only to a collection that keeps its items in an ordered set. - It builds upon the Array mixin and adds methods to modify the array. - Concrete implementations of this class include ArrayProxy and ArrayController. + Ember.assert(classify(name) + ' has no method `find`.', typeof modelClass.find === 'function'); - It is important to use the methods in this class to modify arrays so that - changes are observable. This allows the binding system in Ember to function - correctly. + return modelClass.find(value); + } + }; + }), + /** + A hook you can implement to convert the route's model into parameters + for the URL. - Note that an Array can change even if it does not implement this mixin. - For example, one might implement a SparseArray that cannot be directly - modified, but if its underlying enumerable changes, it will change also. + ```javascript + App.Router.map(function() { + this.resource('post', { path: '/posts/:post_id' }); + }); - @class MutableArray - @namespace Ember - @uses Ember.Array - @uses Ember.MutableEnumerable - */ - var MutableArray = Mixin.create(EmberArray, MutableEnumerable, { + App.PostRoute = Ember.Route.extend({ + model: function(params) { + // the server returns `{ id: 12 }` + return Ember.$.getJSON('/posts/' + params.post_id); + }, - /** - __Required.__ You must implement this method to apply this mixin. + serialize: function(model) { + // this will make the URL `/posts/12` + return { post_id: model.id }; + } + }); + ``` - This is one of the primitives you must implement to support `Ember.Array`. - You should replace amt objects started at idx with the objects in the - passed array. You should also call `this.enumerableContentDidChange()` + The default `serialize` method will insert the model's `id` into the + route's dynamic segment (in this case, `:post_id`) if the segment contains '_id'. + If the route has multiple dynamic segments or does not contain '_id', `serialize` + will return `Ember.getProperties(model, params)` - @method replace - @param {Number} idx Starting index in the array to replace. If - idx >= length, then append to the end of the array. - @param {Number} amt Number of elements that should be removed from - the array, starting at *idx*. - @param {Array} objects An array of zero or more objects that should be - inserted into the array at *idx* + This method is called when `transitionTo` is called with a context + in order to populate the URL. + + @method serialize + @param {Object} model the route's model + @param {Array} params an Array of parameter names for the current + route (in the example, `['post_id']`. + @return {Object} the serialized parameters */ - replace: required(), + serialize: function(model, params) { + if (params.length < 1) { return; } + if (!model) { return; } - /** - Remove all elements from the array. This is useful if you - want to reuse an existing array without having to recreate it. + var name = params[0], object = {}; - ```javascript - var colors = ["red", "green", "blue"]; - color.length(); // 3 - colors.clear(); // [] - colors.length(); // 0 - ``` + if (/_id$/.test(name) && params.length === 1) { + object[name] = get(model, "id"); + } else { + object = getProperties(model, params); + } - @method clear - @return {Ember.Array} An empty Array. - */ - clear: function () { - var len = get(this, 'length'); - if (len === 0) return this; - this.replace(0, len, EMPTY); - return this; + return object; }, /** - This will use the primitive `replace()` method to insert an object at the - specified index. - - ```javascript - var colors = ["red", "green", "blue"]; - colors.insertAt(2, "yellow"); // ["red", "green", "yellow", "blue"] - colors.insertAt(5, "orange"); // Error: Index out of range - ``` + A hook you can use to setup the controller for the current route. - @method insertAt - @param {Number} idx index of insert the object at. - @param {Object} object object to insert - @return {Ember.Array} receiver - */ - insertAt: function(idx, object) { - if (idx > get(this, 'length')) throw new EmberError(OUT_OF_RANGE_EXCEPTION); - this.replace(idx, 0, [object]); - return this; - }, + This method is called with the controller for the current route and the + model supplied by the `model` hook. - /** - Remove an object at the specified index using the `replace()` primitive - method. You can pass either a single index, or a start and a length. + By default, the `setupController` hook sets the `model` property of + the controller to the `model`. - If you pass a start and length that is beyond the - length this method will throw an `OUT_OF_RANGE_EXCEPTION`. + 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`: ```javascript - var colors = ["red", "green", "blue", "yellow", "orange"]; - colors.removeAt(0); // ["green", "blue", "yellow", "orange"] - colors.removeAt(2, 2); // ["green", "blue"] - colors.removeAt(4, 2); // Error: Index out of range + App.PhotosRoute = Ember.Route.extend({ + model: function() { + return this.store.find('photo'); + }, + + setupController: function (controller, model) { + // Call _super for default behavior + this._super(controller, model); + // Implement your custom setup after + this.controllerFor('application').set('showingPhotos', true); + } + }); ``` - @method removeAt - @param {Number} start index, start of range - @param {Number} len length of passing range - @return {Ember.Array} receiver - */ - removeAt: function(start, len) { - if ('number' === typeof start) { + 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 ((start < 0) || (start >= get(this, 'length'))) { - throw new EmberError(OUT_OF_RANGE_EXCEPTION); - } + The provided controller will be one resolved based on the name + of this route. - // fast case - if (len === undefined) len = 1; - this.replace(start, len, EMPTY); - } + If no explicit controller is defined, Ember will automatically create + an appropriate controller for the model. - return 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`. - /** - Push the object onto the end of the array. Works just like `push()` but it - is KVO-compliant. + As an example, consider the router: ```javascript - var colors = ["red", "green"]; - colors.pushObject("black"); // ["red", "green", "black"] - colors.pushObject(["yellow"]); // ["red", "green", ["yellow"]] + App.Router.map(function() { + this.resource('post', { path: '/posts/:post_id' }); + }); ``` - @method pushObject - @param {*} obj object to push - @return object same object passed as a param - */ - pushObject: function(obj) { - this.insertAt(get(this, 'length'), obj); - return obj; - }, + 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. - /** - Add the objects in the passed numerable to the end of the array. Defers - notifying observers of the change until all objects are added. + Example ```javascript - var colors = ["red"]; - colors.pushObjects(["yellow", "orange"]); // ["red", "yellow", "orange"] + App.PostRoute = Ember.Route.extend({ + setupController: function(controller, model) { + controller.set('model', model); + } + }); ``` - @method pushObjects - @param {Ember.Enumerable} objects the objects to add - @return {Ember.Array} receiver + @method setupController + @param {Controller} controller instance + @param {Object} model */ - pushObjects: function(objects) { - if (!(Enumerable.detect(objects) || isArray(objects))) { - throw new TypeError("Must pass Ember.Enumerable to Ember.MutableArray#pushObjects"); + setupController: function(controller, context, transition) { + if (controller && (context !== undefined)) { + set(controller, 'model', context); } - this.replace(get(this, 'length'), 0, objects); - return this; }, /** - Pop object from array or nil if none are left. Works just like `pop()` but - it is KVO-compliant. + Returns the controller for a particular route or name. + + The controller instance must already have been created, either through entering the + associated route or using `generateController`. + + ```javascript + App.PostRoute = Ember.Route.extend({ + setupController: function(controller, post) { + this._super(controller, post); + this.controllerFor('posts').set('currentPost', post); + } + }); + ``` + + @method controllerFor + @param {String} name the name of the route or controller + @return {Ember.Controller} + */ + controllerFor: function(name, _skipAssert) { + var container = this.container; + var route = container.lookup('route:'+name); + var controller; + + if (route && route.controllerName) { + name = route.controllerName; + } - ```javascript - var colors = ["red", "green", "blue"]; - colors.popObject(); // "blue" - console.log(colors); // ["red", "green"] - ``` + controller = container.lookup('controller:' + name); - @method popObject - @return object - */ - popObject: function() { - var len = get(this, 'length'); - if (len === 0) return null; + // 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); - var ret = this.objectAt(len-1); - this.removeAt(len-1, 1); - return ret; + return controller; }, /** - Shift an object from start of array or nil if none are left. Works just - like `shift()` but it is KVO-compliant. + Generates a controller for a route. + + If the optional model is passed then the controller type is determined automatically, + e.g., an ArrayController for arrays. + + Example ```javascript - var colors = ["red", "green", "blue"]; - colors.shiftObject(); // "red" - console.log(colors); // ["green", "blue"] + App.PostRoute = Ember.Route.extend({ + setupController: function(controller, post) { + this._super(controller, post); + this.generateController('posts', post); + } + }); ``` - @method shiftObject - @return object + @method generateController + @param {String} name the name of the controller + @param {Object} model the model to infer the type of the controller (optional) */ - shiftObject: function() { - if (get(this, 'length') === 0) return null; - var ret = this.objectAt(0); - this.removeAt(0); - return ret; + generateController: function(name, model) { + var container = this.container; + + model = model || this.modelFor(name); + + return generateController(container, name, model); }, /** - Unshift an object to start of array. Works just like `unshift()` but it is - KVO-compliant. + 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. + + Example ```javascript - var colors = ["red"]; - colors.unshiftObject("yellow"); // ["yellow", "red"] - colors.unshiftObject(["black"]); // [["black"], "yellow", "red"] + App.Router.map(function() { + this.resource('post', { path: '/post/:post_id' }, function() { + this.resource('comments'); + }); + }); + + App.CommentsRoute = Ember.Route.extend({ + afterModel: function() { + this.set('post', this.modelFor('post')); + } + }); ``` - @method unshiftObject - @param {*} obj object to unshift - @return object same object passed as a param + @method modelFor + @param {String} name the name of the route + @return {Object} the model object */ - unshiftObject: function(obj) { - this.insertAt(0, obj); - return obj; + modelFor: function(name) { + var route = this.container.lookup('route:' + name); + var transition = this.router ? this.router.router.activeTransition : null; + + // 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]; + } + } + + return route && route.currentModel; }, /** - Adds the named objects to the beginning of the array. Defers notifying - observers until all objects have been added. + A hook you can use to render the template for the current route. + + 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. + + This method can be overridden to set up and render additional or + alternative templates. ```javascript - var colors = ["red"]; - colors.unshiftObjects(["black", "white"]); // ["black", "white", "red"] - colors.unshiftObjects("yellow"); // Type Error: 'undefined' is not a function + App.PostsRoute = Ember.Route.extend({ + renderTemplate: function(controller, model) { + var favController = this.controllerFor('favoritePost'); + + // Render the `favoritePost` template into + // the outlet `posts`, and display the `favoritePost` + // controller. + this.render('favoritePost', { + outlet: 'posts', + controller: favController + }); + } + }); ``` - @method unshiftObjects - @param {Ember.Enumerable} objects the objects to add - @return {Ember.Array} receiver + @method renderTemplate + @param {Object} controller the route's controller + @param {Object} model the route's model */ - unshiftObjects: function(objects) { - this.replace(0, 0, objects); - return this; + renderTemplate: function(controller, model) { + this.render(); }, /** - Reverse objects in the array. Works just like `reverse()` but it is - KVO-compliant. - - @method reverseObjects - @return {Ember.Array} receiver - */ - reverseObjects: function() { - var len = get(this, 'length'); - if (len === 0) return this; - var objects = this.toArray().reverse(); - this.replace(0, len, objects); - return this; - }, + `render` is used to render a template into a region of another template + (indicated by an `{{outlet}}`). `render` is used both during the entry + phase of routing (via the `renderTemplate` hook) and later in response to + user interaction. - /** - Replace all the the receiver's content with content of the argument. - If argument is an empty array receiver will be cleared. + For example, given the following minimal router and templates: ```javascript - var colors = ["red", "green", "blue"]; - colors.setObjects(["black", "white"]); // ["black", "white"] - colors.setObjects([]); // [] + Router.map(function() { + this.resource('photos'); + }); ``` - @method setObjects - @param {Ember.Array} objects array whose content will be used for replacing - the content of the receiver - @return {Ember.Array} receiver with the new content - */ - setObjects: function(objects) { - if (objects.length === 0) return this.clear(); - - var len = get(this, 'length'); - this.replace(0, len, objects); - return this; - }, + ```handlebars + +
    + {{outlet "anOutletName"}} +
    + ``` - // .......................................................... - // IMPLEMENT Ember.MutableEnumerable - // + ```handlebars + +

    Photos

    + ``` - /** - Remove all occurances of an object in the array. + You can render `photos.hbs` into the `"anOutletName"` outlet of + `application.hbs` by calling `render`: ```javascript - var cities = ["Chicago", "Berlin", "Lima", "Chicago"]; - cities.removeObject("Chicago"); // ["Berlin", "Lima"] - cities.removeObject("Lima"); // ["Berlin"] - cities.removeObject("Tokyo") // ["Berlin"] + // posts route + Ember.Route.extend({ + renderTemplate: function(){ + this.render('photos', { + into: 'application', + outlet: 'anOutletName' + }) + } + }); ``` - @method removeObject - @param {*} obj object to remove - @return {Ember.Array} receiver - */ - removeObject: function(obj) { - var loc = get(this, 'length') || 0; - while(--loc >= 0) { - var curObject = this.objectAt(loc); - if (curObject === obj) this.removeAt(loc); - } - return this; - }, + `render` additionally allows you to supply which `view`, `controller`, and + `model` objects should be loaded and associated with the rendered template. - /** - Push the object onto the end of the array if it is not already - present in the array. ```javascript - var cities = ["Chicago", "Berlin"]; - cities.addObject("Lima"); // ["Chicago", "Berlin", "Lima"] - cities.addObject("Berlin"); // ["Chicago", "Berlin", "Lima"] + // posts route + Ember.Route.extend({ + renderTemplate: function(controller, model){ + this.render('posts', { // the template to render, referenced by name + into: 'application', // the template to render into, referenced by name + outlet: 'anOutletName', // the outlet inside `options.template` to render into. + view: 'aViewName', // the view to use for this template, referenced by name + controller: 'someControllerName', // the controller to use for this template, referenced by name + model: model // the model to set on `options.controller`. + }) + } + }); ``` - @method addObject - @param {*} obj object to add, if not already present - @return {Ember.Array} receiver - */ - addObject: function(obj) { - if (!this.contains(obj)) this.pushObject(obj); - return this; - } - - }); - - __exports__["default"] = MutableArray; - }); -define("ember-runtime/mixins/mutable_enumerable", - ["ember-metal/enumerable_utils","ember-runtime/mixins/enumerable","ember-metal/mixin","ember-metal/property_events","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - var EnumerableUtils = __dependency1__["default"]; - var Enumerable = __dependency2__["default"]; - var Mixin = __dependency3__.Mixin; - var required = __dependency3__.required; - var beginPropertyChanges = __dependency4__.beginPropertyChanges; - var endPropertyChanges = __dependency4__.endPropertyChanges; + The string values provided for the template name, view, and controller + will eventually pass through to the resolver for lookup. See + Ember.Resolver for how these are mapped to JavaScript objects in your + application. - /** - @module ember - @submodule ember-runtime - */ + Not all options need to be passed to `render`. Default values will be used + based on the name of the route specified in the router or the Route's + `controllerName`, `viewName` and `templateName` properties. - var forEach = EnumerableUtils.forEach; + For example: - /** - This mixin defines the API for modifying generic enumerables. These methods - can be applied to an object regardless of whether it is ordered or - unordered. + ```javascript + // router + Router.map(function() { + this.route('index'); + this.resource('post', { path: '/posts/:post_id' }); + }); + ``` - Note that an Enumerable can change even if it does not implement this mixin. - For example, a MappedEnumerable cannot be directly modified but if its - underlying enumerable changes, it will change also. + ```javascript + // post route + PostRoute = App.Route.extend({ + renderTemplate: function() { + this.render(); // all defaults apply + } + }); + ``` - ## Adding Objects + The name of the `PostRoute`, defined by the router, is `post`. - To add an object to an enumerable, use the `addObject()` method. This - method will only add the object to the enumerable if the object is not - already present and is of a type supported by the enumerable. + The following equivalent default options will be applied when + the Route calls `render`: - ```javascript - set.addObject(contact); - ``` + ```javascript + // + this.render('post', { // the template name associated with 'post' Route + into: 'application', // the parent route to 'post' Route + outlet: 'main', // {{outlet}} and {{outlet 'main' are synonymous}}, + view: 'post', // the view associated with the 'post' Route + controller: 'post', // the controller associated with the 'post' Route + }) + ``` - ## Removing Objects + By default the controller's `model` will be the route's model, so it does not + need to be passed unless you wish to change which model is being used. - 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. + @method render + @param {String} name the name of the template to render + @param {Object} [options] the options + @param {String} [options.into] the template to render into, + referenced by name. Defaults to the parent template + @param {String} [options.outlet] the outlet inside `options.template` to render into. + Defaults to 'main' + @param {String} [options.controller] the controller to use for this template, + referenced by name. Defaults to the Route's paired controller + @param {String} [options.model] the model object to set on `options.controller` + Defaults to the return value of the Route's model hook + */ + render: function(_name, options) { + Ember.assert("The name in the given arguments is undefined", arguments.length > 0 ? !isNone(arguments[0]) : true); - ```javascript - set.removeObject(contact); - ``` + var namePassed = typeof _name === 'string' && !!_name; + var name; - ## Implementing In Your Own Code + if (typeof _name === 'object' && !options) { + name = this.routeName; + options = _name; + } else { + name = _name; + } - If you are implementing an object and want to support this API, just include - this mixin in your class and implement the required methods. In your unit - tests, be sure to apply the Ember.MutableEnumerableTests to your object. + var templateName; - @class MutableEnumerable - @namespace Ember - @uses Ember.Enumerable - */ - var MutableEnumerable = Mixin.create(Enumerable, { + if (name) { + name = name.replace(/\//g, '.'); + templateName = name; + } else { + name = this.routeName; + templateName = this.templateName || name; + } - /** - __Required.__ You must implement this method to apply this mixin. + var renderOptions = buildRenderOptions(this, namePassed, name, options); - Attempts to add the passed object to the receiver if the object is not - already present in the collection. If the object is present, this method - has no effect. + var LOG_VIEW_LOOKUPS = get(this.router, 'namespace.LOG_VIEW_LOOKUPS'); + var viewName = options && options.view || namePassed && name || this.viewName || name; + var view, template; - If the passed object is of a type not supported by the receiver, - then this method should raise an exception. + var ViewClass = this.container.lookupFactory('view:' + viewName); + if (ViewClass) { + view = setupView(ViewClass, renderOptions); + if (!get(view, 'template')) { + view.set('template', this.container.lookup('template:' + templateName)); + } + if (LOG_VIEW_LOOKUPS) { + Ember.Logger.info("Rendering " + renderOptions.name + " with " + view, { fullName: 'view:' + renderOptions.name }); + } + } else { + template = this.container.lookup('template:' + templateName); + if (!template) { + Ember.assert("Could not find \"" + name + "\" template or view.", arguments.length === 0 || Ember.isEmpty(arguments[0])); + if (LOG_VIEW_LOOKUPS) { + Ember.Logger.info("Could not find \"" + name + "\" template or view. Nothing will be rendered", { fullName: 'template:' + name }); + } + return; + } + var defaultView = renderOptions.into ? 'view:default' : 'view:toplevel'; + ViewClass = this.container.lookupFactory(defaultView); + view = setupView(ViewClass, renderOptions); + if (!get(view, 'template')) { + view.set('template', template); + } + if (LOG_VIEW_LOOKUPS) { + Ember.Logger.info("Rendering " + renderOptions.name + " with default view " + view, { fullName: 'view:' + renderOptions.name }); + } + } - @method addObject - @param {Object} object The object to add to the enumerable. - @return {Object} the passed object - */ - addObject: required(Function), + if (renderOptions.outlet === 'main') { this.lastRenderedTemplate = name; } + appendView(this, view, renderOptions); + }, /** - Adds each object in the passed enumerable to the receiver. + Disconnects a view that has been rendered into an outlet. - @method addObjects - @param {Ember.Enumerable} objects the objects to add. - @return {Object} receiver - */ - addObjects: function(objects) { - beginPropertyChanges(this); - forEach(objects, function(obj) { this.addObject(obj); }, this); - endPropertyChanges(this); - return this; - }, + You may pass any or all of the following options to `disconnectOutlet`: - /** - __Required.__ You must implement this method to apply this mixin. + * `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) - 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. + Example: - If the passed object is of a type not supported by the receiver, - then this method should raise an exception. + ```javascript + 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' + }); + } + } + }); + ``` - @method removeObject - @param {Object} object The object to remove from the enumerable. - @return {Object} the passed object - */ - removeObject: required(Function), + Alternatively, you can pass the `outlet` name directly as a string. + Example: - /** - Removes each object in the passed enumerable from the receiver. + ```javascript + hideModal: function(evt) { + this.disconnectOutlet('modal'); + } + ``` - @method removeObjects - @param {Ember.Enumerable} objects the objects to remove - @return {Object} receiver + @method disconnectOutlet + @param {Object|String} options the options hash or outlet name */ - removeObjects: function(objects) { - beginPropertyChanges(this); - for (var i = objects.length - 1; i >= 0; i--) { - this.removeObject(objects[i]); + disconnectOutlet: function(options) { + if (!options || typeof options === "string") { + var outletName = options; + options = {}; + options.outlet = outletName; } - endPropertyChanges(this); - return this; - } - }); - - __exports__["default"] = MutableEnumerable; - }); -define("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__) { - "use strict"; - /** - @module ember - @submodule ember-runtime - */ - var Ember = __dependency1__["default"]; - // Ember.assert + options.parentView = options.parentView ? options.parentView.replace(/\//g, '.') : parentTemplate(this); + options.outlet = options.outlet || 'main'; - 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__.isNone; + var parentView = this.router._lookupActiveView(options.parentView); + if (parentView) { parentView.disconnectOutlet(options.outlet); } + }, + willDestroy: function() { + this.teardownViews(); + }, - var slice = Array.prototype.slice; - /** - ## Overview + /** + @private - This mixin provides properties and property observing functionality, core - features of the Ember object model. + @method teardownViews + */ + teardownViews: function() { + // Tear down the top level view + if (this.teardownTopLevelView) { this.teardownTopLevelView(); } - 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. + // Tear down any outlets rendered with 'into' + var teardownOutletViews = this.teardownOutletViews || []; + forEach(teardownOutletViews, function(teardownOutletView) { + teardownOutletView(); + }); - 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. + delete this.teardownTopLevelView; + delete this.teardownOutletViews; + delete this.lastRenderedTemplate; + } + }); - 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. + + // TODO add mixin directly to `Route` class definition above, once this + // feature is merged: + Route.reopen(Evented); + - ## Using `get()` and `set()` + var defaultQPMeta = { + qps: [], + map: {}, + states: {} + }; - 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. + function parentRoute(route) { + var handlerInfo = handlerInfoFor(route, route.router.router.state.handlerInfos, -1); + return handlerInfo && handlerInfo.handler; + } - More documentation about `get` and `set` are below. + function handlerInfoFor(route, handlerInfos, _offset) { + if (!handlerInfos) { return; } - ## Observing Property Changes + var offset = _offset || 0; + var current; + for (var i=0, l=handlerInfos.length; i " + routeName, { fullName: routeName }); + } + } - @method cacheFor - @param {String} keyName - @return {Object} The cached value of the computed property, if any - */ - cacheFor: function(keyName) { - return cacheFor(this, keyName); + handler.routeName = name; + return handler; + }; }, - // intended for debugging purposes - observersForKey: function(keyName) { - return observersFor(this, keyName); - } - }); + _setupRouter: function(router, location) { + var lastURL, emberRouter = this; - __exports__["default"] = Observable; - }); -define("ember-runtime/mixins/promise_proxy", - ["ember-metal/property_get","ember-metal/property_set","ember-metal/computed","ember-metal/mixin","ember-metal/error","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { - "use strict"; - var get = __dependency1__.get; - var set = __dependency2__.set; - var computed = __dependency3__.computed; - var Mixin = __dependency4__.Mixin; - var EmberError = __dependency5__["default"]; + router.getHandler = this._getHandlerFunction(); - var not = computed.not, or = computed.or; + var doUpdateURL = function() { + location.setURL(lastURL); + }; - /** - @module ember - @submodule ember-runtime - */ + router.updateURL = function(path) { + lastURL = path; + run.once(doUpdateURL); + }; - function tap(proxy, promise) { - set(proxy, 'isFulfilled', false); - set(proxy, 'isRejected', false); + if (location.replaceURL) { + var doReplaceURL = function() { + location.replaceURL(lastURL); + }; - return promise.then(function(value) { - set(proxy, 'isFulfilled', true); - set(proxy, 'content', value); - return value; - }, function(reason) { - set(proxy, 'isRejected', true); - set(proxy, 'reason', reason); - throw reason; - }, "Ember: PromiseProxy"); - } + router.replaceURL = function(path) { + lastURL = path; + run.once(doReplaceURL); + }; + } - /** - A low level mixin making ObjectProxy, ObjectController or ArrayController's promise aware. + router.didTransition = function(infos) { + emberRouter.didTransition(infos); + }; + }, - ```javascript - var ObjectPromiseController = Ember.ObjectController.extend(Ember.PromiseProxyMixin); + _serializeQueryParams: function(targetRouteName, queryParams) { + var groupedByUrlKey = {}; - var controller = ObjectPromiseController.create({ - promise: $.getJSON('/some/remote/data.json') - }); + forEachQueryParam(this, targetRouteName, queryParams, function(key, value, qp) { + var urlKey = qp.urlKey; + if (!groupedByUrlKey[urlKey]) { + groupedByUrlKey[urlKey] = []; + } + groupedByUrlKey[urlKey].push({ + qp: qp, + value: value + }); + delete queryParams[key]; + }); - controller.then(function(json){ - // the json - }, function(reason) { - // the reason why you have no json - }); - ``` + for (var key in groupedByUrlKey) { + var qps = groupedByUrlKey[key]; + Ember.assert(fmt("You're not allowed to have more than one controller " + + "property map to the same query param key, but both " + + "`%@` and `%@` map to `%@`. You can fix this by mapping " + + "one of the controller properties to a different query " + + "param key via the `as` config option, e.g. `%@: { as: 'other-%@' }`", + [qps[0].qp.fprop, qps[1] ? qps[1].qp.fprop : "", qps[0].qp.urlKey, qps[0].qp.prop, qps[0].qp.prop]), qps.length <= 1); + var qp = qps[0].qp; + queryParams[qp.urlKey] = qp.route.serializeQueryParam(qps[0].value, qp.urlKey, qp.type); + } + }, - the controller has bindable attributes which - track the promises life cycle + _deserializeQueryParams: function(targetRouteName, queryParams) { + forEachQueryParam(this, targetRouteName, queryParams, function(key, value, qp) { + delete queryParams[key]; + queryParams[qp.prop] = qp.route.deserializeQueryParam(value, qp.urlKey, qp.type); + }); + }, - ```javascript - controller.get('isPending') //=> true - controller.get('isSettled') //=> false - controller.get('isRejected') //=> false - controller.get('isFulfilled') //=> false - ``` + _pruneDefaultQueryParamValues: function(targetRouteName, queryParams) { + var qps = this._queryParamsFor(targetRouteName); + for (var key in queryParams) { + var qp = qps.map[key]; + if (qp && qp.sdef === queryParams[key]) { + delete queryParams[key]; + } + } + }, - When the the $.getJSON completes, and the promise is fulfilled - with json, the life cycle attributes will update accordingly. + _doTransition: function(_targetRouteName, models, _queryParams) { + var targetRouteName = _targetRouteName || getActiveTargetName(this.router); + Ember.assert("The route " + targetRouteName + " was not found", targetRouteName && this.router.hasRoute(targetRouteName)); - ```javascript - controller.get('isPending') //=> false - controller.get('isSettled') //=> true - controller.get('isRejected') //=> false - controller.get('isFulfilled') //=> true - ``` + var queryParams = {}; + merge(queryParams, _queryParams); + this._prepareQueryParams(targetRouteName, models, queryParams); - As the controller is an ObjectController, and the json now its content, - all the json properties will be available directly from the controller. + var transitionArgs = routeArgs(targetRouteName, models, queryParams); + var transitionPromise = this.router.transitionTo.apply(this.router, transitionArgs); - ```javascript - // Assuming the following json: - { - firstName: 'Stefan', - lastName: 'Penner' - } + listenForTransitionErrors(transitionPromise); - // both properties will accessible on the controller - controller.get('firstName') //=> 'Stefan' - controller.get('lastName') //=> 'Penner' - ``` + return transitionPromise; + }, - If the controller is backing a template, the attributes are - bindable from within that template + _prepareQueryParams: function(targetRouteName, models, queryParams) { + this._hydrateUnsuppliedQueryParams(targetRouteName, models, queryParams); + this._serializeQueryParams(targetRouteName, queryParams); + this._pruneDefaultQueryParamValues(targetRouteName, queryParams); + }, - ```handlebars - {{#if isPending}} - loading... - {{else}} - firstName: {{firstName}} - lastName: {{lastName}} - {{/if}} - ``` - @class Ember.PromiseProxyMixin - */ - var PromiseProxyMixin = Mixin.create({ /** - If the proxied promise is rejected this will contain the reason - provided. + Returns a merged query params meta object for a given route. + Useful for asking a route what its known query params are. + */ + _queryParamsFor: function(leafRouteName) { + if (this._qpCache[leafRouteName]) { + return this._qpCache[leafRouteName]; + } - @property reason - @default null - */ - reason: null, + var map = {}, qps = []; + this._qpCache[leafRouteName] = { + map: map, + qps: qps + }; - /** - Once the proxied promise has settled this will become `false`. + var routerjs = this.router; + var recogHandlerInfos = routerjs.recognizer.handlersFor(leafRouteName); - @property isPending - @default true - */ - isPending: not('isSettled').readOnly(), + for (var i = 0, len = recogHandlerInfos.length; i < len; ++i) { + var recogHandler = recogHandlerInfos[i]; + var route = routerjs.getHandler(recogHandler.handler); + var qpMeta = get(route, '_qp'); - /** - Once the proxied promise has settled this will become `true`. + if (!qpMeta) { continue; } - @property isSettled - @default false - */ - isSettled: or('isRejected', 'isFulfilled').readOnly(), + merge(map, qpMeta.map); + qps.push.apply(qps, qpMeta.qps); + } - /** - Will become `true` if the proxied promise is rejected. + return { + qps: qps, + map: map + }; + }, - @property isRejected - @default false - */ - isRejected: false, + /* + becomeResolved: function(payload, resolvedContext) { + var params = this.serialize(resolvedContext); - /** - Will become `true` if the proxied promise is fulfilled. + if (payload) { + this.stashResolvedModel(payload, resolvedContext); + payload.params = payload.params || {}; + payload.params[this.name] = params; + } - @property isFullfilled - @default false + return this.factory('resolved', { + context: resolvedContext, + name: this.name, + handler: this.handler, + params: params + }); + }, */ - isFulfilled: false, - - /** - The promise whose fulfillment value is being proxied by this object. - This property must be specified upon creation, and should not be - changed once created. + _hydrateUnsuppliedQueryParams: function(leafRouteName, contexts, queryParams) { + var state = calculatePostTransitionState(this, leafRouteName, contexts); + var handlerInfos = state.handlerInfos; + var appCache = this._bucketCache; - Example: + stashParamNames(this, handlerInfos); - ```javascript - Ember.ObjectController.extend(Ember.PromiseProxyMixin).create({ - promise: - }); - ``` + for (var i = 0, len = handlerInfos.length; i < len; ++i) { + var route = handlerInfos[i].handler; + var qpMeta = get(route, '_qp'); + + for (var j = 0, qpLen = qpMeta.qps.length; j < qpLen; ++j) { + var qp = qpMeta.qps[j]; + var presentProp = qp.prop in queryParams && qp.prop || + qp.fprop in queryParams && qp.fprop; + + if (presentProp) { + if (presentProp !== qp.fprop) { + queryParams[qp.fprop] = queryParams[presentProp]; + delete queryParams[presentProp]; + } + } else { + var controllerProto = qp.cProto; + var cacheMeta = get(controllerProto, '_cacheMeta'); - @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"); + var cacheKey = controllerProto._calculateCacheKey(qp.ctrl, cacheMeta[qp.prop].parts, state.params); + queryParams[qp.fprop] = appCache.lookup(cacheKey, qp.prop, qp.def); + } + } } - }), - - /** - An alias to the proxied promise's `then`. + }, - See RSVP.Promise.then. + _scheduleLoadingEvent: function(transition, originRoute) { + this._cancelLoadingEvent(); + this._loadingStateTimer = run.scheduleOnce('routerTransitions', this, '_fireLoadingEvent', transition, originRoute); + }, - @method then - @param {Function} callback - @return {RSVP.Promise} - */ - then: promiseAlias('then'), + _fireLoadingEvent: function(transition, originRoute) { + if (!this.router.activeTransition) { + // Don't fire an event if we've since moved on from + // the transition that put us in a loading state. + return; + } - /** - An alias to the proxied promise's `catch`. + transition.trigger(true, 'loading', transition, originRoute); + }, - See RSVP.Promise.catch. + _cancelLoadingEvent: function () { + if (this._loadingStateTimer) { + run.cancel(this._loadingStateTimer); + } + this._loadingStateTimer = null; + } + }); - @method catch - @param {Function} callback - @return {RSVP.Promise} - @since 1.3.0 - */ - 'catch': promiseAlias('catch'), + /* + Helper function for iterating root-ward, starting + from (but not including) the provided `originRoute`. - /** - An alias to the proxied promise's `finally`. + Returns true if the last callback fired requested + to bubble upward. - See RSVP.Promise.finally. + @private + */ + function forEachRouteAbove(originRoute, transition, callback) { + var handlerInfos = transition.state.handlerInfos; + var originRouteFound = false; + var handlerInfo, route; - @method finally - @param {Function} callback - @return {RSVP.Promise} - @since 1.3.0 - */ - 'finally': promiseAlias('finally') + for (var i = handlerInfos.length - 1; i >= 0; --i) { + handlerInfo = handlerInfos[i]; + route = handlerInfo.handler; - }); + if (!originRouteFound) { + if (originRoute === route) { + originRouteFound = true; + } + continue; + } - function promiseAlias(name) { - return function () { - var promise = get(this, 'promise'); - return promise[name].apply(promise, arguments); - }; + if (callback(route, handlerInfos[i + 1].handler) !== true) { + return false; + } + } + return true; } - __exports__["default"] = PromiseProxyMixin; - }); -define("ember-runtime/mixins/sortable", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","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__, __dependency9__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-runtime - */ + // These get invoked when an action bubbles above ApplicationRoute + // and are not meant to be overridable. + var defaultActionHandlers = { - var Ember = __dependency1__["default"]; - // Ember.assert, Ember.A + willResolveModel: function(transition, originRoute) { + originRoute.router._scheduleLoadingEvent(transition, originRoute); + }, - var get = __dependency2__.get; - var set = __dependency3__.set; - var EnumerableUtils = __dependency4__["default"]; - var Mixin = __dependency5__.Mixin; - var MutableEnumerable = __dependency6__["default"]; - var compare = __dependency7__["default"]; - var addObserver = __dependency8__.addObserver; - var removeObserver = __dependency8__.removeObserver; - var computed = __dependency9__.computed; - var beforeObserver = __dependency5__.beforeObserver; - var observer = __dependency5__.observer; - //ES6TODO: should we access these directly from their package or from how thier exposed in ember-metal? + error: function(error, transition, originRoute) { + // Attempt to find an appropriate error substate to enter. + var router = originRoute.router; - var forEach = EnumerableUtils.forEach; + var tryTopLevel = forEachRouteAbove(originRoute, transition, function(route, childRoute) { + var childErrorRouteName = findChildRouteName(route, childRoute, 'error'); + if (childErrorRouteName) { + router.intermediateTransitionTo(childErrorRouteName, error); + return; + } + return true; + }); - /** - `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 - content array: + if (tryTopLevel) { + // Check for top-level error state to enter. + if (routeHasBeenDefined(originRoute.router, 'application_error')) { + router.intermediateTransitionTo('application_error', error); + return; + } + } - ```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'}, - ]; + logError(error, 'Error while processing route: ' + transition.targetName); + }, - songsController = Ember.ArrayController.create({ - content: songs, - sortProperties: ['trackNumber'], - sortAscending: true - }); + loading: function(transition, originRoute) { + // Attempt to find an appropriate loading substate to enter. + var router = originRoute.router; - songsController.get('firstObject'); // {trackNumber: 2, title: 'Back in the U.S.S.R.'} + var tryTopLevel = forEachRouteAbove(originRoute, transition, function(route, childRoute) { + var childLoadingRouteName = findChildRouteName(route, childRoute, 'loading'); - songsController.addObject({trackNumber: 1, title: 'Dear Prudence'}); - songsController.get('firstObject'); // {trackNumber: 1, title: 'Dear Prudence'} - ``` + if (childLoadingRouteName) { + router.intermediateTransitionTo(childLoadingRouteName); + return; + } - If you add or remove the properties to sort by or change the sort direction the content - sort order will be automatically updated. + // Don't bubble above pivot route. + if (transition.pivotHandler !== route) { + return true; + } + }); - ```javascript - songsController.set('sortProperties', ['title']); - songsController.get('firstObject'); // {trackNumber: 2, title: 'Back in the U.S.S.R.'} + if (tryTopLevel) { + // Check for top-level loading state to enter. + if (routeHasBeenDefined(originRoute.router, 'application_loading')) { + router.intermediateTransitionTo('application_loading'); + return; + } + } + } + }; - songsController.toggleProperty('sortAscending'); - songsController.get('firstObject'); // {trackNumber: 4, title: 'Ob-La-Di, Ob-La-Da'} - ``` + function logError(error, initialMessage) { + var errorArgs = []; - 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: + if (initialMessage) { errorArgs.push(initialMessage); } - ```javascript - songsController.get('content').get('firstObject'); // Returns the unsorted original content - songsController.get('firstObject'); // Returns the sorted content. - ``` + if (error) { + if (error.message) { errorArgs.push(error.message); } + if (error.stack) { errorArgs.push(error.stack); } - 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. + if (typeof error === "string") { errorArgs.push(error); } + } - @class SortableMixin - @namespace Ember - @uses Ember.MutableEnumerable - */ - var SortableMixin = Mixin.create(MutableEnumerable, { + Ember.Logger.error.apply(this, errorArgs); + } - /** - Specifies which properties dictate the arrangedContent's sort order. + function findChildRouteName(parentRoute, originatingChildRoute, name) { + var router = parentRoute.router; + var childName; + var targetChildRouteName = originatingChildRoute.routeName.split('.').pop(); + var namespace = parentRoute.routeName === 'application' ? '' : parentRoute.routeName + '.'; - When specifying multiple properties the sorting will use properties - from the `sortProperties` array prioritized from first to last. + + // Second, try general loading state, e.g. 'loading' + childName = namespace + name; + if (routeHasBeenDefined(router, childName)) { + return childName; + } + } - @property {Array} sortProperties - */ - sortProperties: null, + function routeHasBeenDefined(router, name) { + var container = router.container; + return router.hasRoute(name) && + (container.has('template:' + name) || container.has('route:' + name)); + } - /** - Specifies the arrangedContent's sort direction. - Sorts the content in ascending order by default. Set to `false` to - use descending order. + function triggerEvent(handlerInfos, ignoreFailure, args) { + var name = args.shift(); - @property {Boolean} sortAscending - @default true - */ - sortAscending: true, + 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."); + } - /** - 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. - 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: + var eventWasHandled = false; + var handlerInfo, handler; - ```javascript - function(x,y) { // These are assumed to be integers - if (x === y) - return 0; - return x < y ? -1 : 1; + for (var i = handlerInfos.length - 1; i >= 0; i--) { + handlerInfo = handlerInfos[i]; + handler = handlerInfo.handler; + + if (handler._actions && handler._actions[name]) { + if (handler._actions[name].apply(handler, args) === true) { + eventWasHandled = true; + } else { + return; + } } - ``` + } - @property sortFunction - @type {Function} - @default Ember.compare - */ - sortFunction: compare, + if (defaultActionHandlers[name]) { + defaultActionHandlers[name].apply(null, args); + return; + } - orderBy: function(item1, item2) { - var result = 0, - sortProperties = get(this, 'sortProperties'), - sortAscending = get(this, 'sortAscending'), - sortFunction = get(this, 'sortFunction'); + 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."); + } + } - Ember.assert("you need to define `sortProperties`", !!sortProperties); + function calculatePostTransitionState(emberRouter, leafRouteName, contexts) { + var routerjs = emberRouter.router; + var state = routerjs.applyIntent(leafRouteName, contexts); + var handlerInfos = state.handlerInfos; + var params = state.params; - 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); + for (var i = 0, len = handlerInfos.length; i < len; ++i) { + var handlerInfo = handlerInfos[i]; + if (!handlerInfo.isResolved) { + handlerInfo = handlerInfo.becomeResolved(null, handlerInfo.context); + } + params[handlerInfo.name] = handlerInfo.params; + } + return state; + } - return result; - }, + function updatePaths(router) { + var appController = router.container.lookup('controller:application'); - destroy: function() { - var content = get(this, 'content'), - sortProperties = get(this, 'sortProperties'); + 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; + } - if (content && sortProperties) { - forEach(content, function(item) { - forEach(sortProperties, function(sortProperty) { - removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); - }, this); - }, this); - } + var infos = router.router.currentHandlerInfos; + var path = EmberRouter._routePath(infos); - return this._super(); - }, + if (!('currentPath' in appController)) { + defineProperty(appController, 'currentPath'); + } - isSorted: computed.notEmpty('sortProperties'), + set(appController, 'currentPath', path); + + if (!('currentRouteName' in appController)) { + defineProperty(appController, 'currentRouteName'); + } + + set(appController, 'currentRouteName', infos[infos.length - 1].name); + } + + EmberRouter.reopenClass({ + router: null, /** - 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. - - @property arrangedContent - */ + The `Router.map` function allows you to define mappings from URLs to routes + and resources in your application. These mappings are defined within the + supplied callback function using `this.resource` and `this.route`. - arrangedContent: computed('content', 'sortProperties.@each', function(key, value) { - var content = get(this, 'content'), - isSorted = get(this, 'isSorted'), - sortProperties = get(this, 'sortProperties'), - self = this; + ```javascript + App.Router.map(function({ + this.route('about'); + this.resource('article'); + })); + ``` - 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); - } + For more detailed examples please see + [the guides](http://emberjs.com/guides/routing/defining-your-routes/). - return content; - }), + @method map + @param callback + */ + map: function(callback) { + var router = this.router; + if (!router) { + router = new Router(); - _contentWillChange: beforeObserver('content', function() { - var content = get(this, 'content'), - sortProperties = get(this, 'sortProperties'); + + router._triggerWillChangeContext = K; + router._triggerWillLeave = K; + - if (content && sortProperties) { - forEach(content, function(item) { - forEach(sortProperties, function(sortProperty) { - removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); - }, this); - }, this); + router.callbacks = []; + router.triggerEvent = triggerEvent; + this.reopenClass({ router: router }); } - this._super(); - }), + 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); + }); + }); - sortPropertiesWillChange: beforeObserver('sortProperties', function() { - this._lastSortAscending = undefined; - }), + router.callbacks.push(callback); + router.map(dsl.generate()); + return router; + }, - sortPropertiesDidChange: observer('sortProperties', function() { - this._lastSortAscending = undefined; - }), + _routePath: function(handlerInfos) { + var path = []; - sortAscendingWillChange: beforeObserver('sortAscending', function() { - this._lastSortAscending = get(this, 'sortAscending'); - }), + // 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' - sortAscendingDidChange: observer('sortAscending', function() { - if (this._lastSortAscending !== undefined && get(this, 'sortAscending') !== this._lastSortAscending) { - var arrangedContent = get(this, 'arrangedContent'); - arrangedContent.reverseObjects(); + function intersectionMatches(a1, a2) { + for (var i = 0, len = a1.length; i < len; ++i) { + if (a1[i] !== a2[i]) { + return false; + } + } + return true; } - }), - - contentArrayWillChange: function(array, idx, removedCount, addedCount) { - var isSorted = get(this, 'isSorted'); - if (isSorted) { - var arrangedContent = get(this, 'arrangedContent'); - var removedObjects = array.slice(idx, idx+removedCount); - var sortProperties = get(this, 'sortProperties'); + var name, nameParts, oldNameParts; + for (var i=1, l=handlerInfos.length; i 0) { - arrangedContent.removeObject(item); - this.insertItemSorted(item); + if (qp) { + callback(key, value, qp); } - }, + } + } - _binarySearch: function(item, low, high) { - var mid, midItem, res, arrangedContent; + __exports__["default"] = EmberRouter; + }); +enifed("ember-routing/utils", + ["ember-metal/utils","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var typeOf = __dependency1__.typeOf; - if (low === high) { - return low; - } + function routeArgs(targetRouteName, models, queryParams) { + var args = []; + if (typeOf(targetRouteName) === 'string') { + args.push('' + targetRouteName); + } + args.push.apply(args, models); + args.push({ queryParams: queryParams }); + return args; + } - arrangedContent = get(this, 'arrangedContent'); + __exports__.routeArgs = routeArgs;function getActiveTargetName(router) { + var handlerInfos = router.activeTransition ? + router.activeTransition.state.handlerInfos : + router.state.handlerInfos; + return handlerInfos[handlerInfos.length - 1].name; + } - mid = low + Math.floor((high - low) / 2); - midItem = arrangedContent.objectAt(mid); + __exports__.getActiveTargetName = getActiveTargetName;function stashParamNames(router, handlerInfos) { + if (handlerInfos._namesStashed) { return; } - res = this.orderBy(midItem, item); + // This helper exists because router.js/route-recognizer.js awkwardly + // keeps separate a handlerInfo's list of parameter names depending + // on whether a URL transition or named transition is happening. + // Hopefully we can remove this in the future. + var targetRouteName = handlerInfos[handlerInfos.length-1].name; + var recogHandlers = router.router.recognizer.handlersFor(targetRouteName); + var dynamicParent = null; - if (res < 0) { - return this._binarySearch(item, mid+1, high); - } else if (res > 0) { - return this._binarySearch(item, low, mid); + for (var i = 0, len = handlerInfos.length; i < len; ++i) { + var handlerInfo = handlerInfos[i]; + var names = recogHandlers[i].names; + + if (names.length) { + dynamicParent = handlerInfo; } - return mid; + handlerInfo._names = names; + + var route = handlerInfo.handler; + route._stashNames(handlerInfo, dynamicParent); } - }); - __exports__["default"] = SortableMixin; + handlerInfos._namesStashed = true; + } + + __exports__.stashParamNames = stashParamNames; }); -define("ember-runtime/mixins/target_action_support", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/mixin","ember-metal/computed","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { +enifed("ember-runtime", + ["ember-metal","ember-runtime/core","ember-runtime/compare","ember-runtime/copy","ember-runtime/inject","ember-runtime/system/namespace","ember-runtime/system/object","ember-runtime/system/tracked_array","ember-runtime/system/subarray","ember-runtime/system/container","ember-runtime/system/array_proxy","ember-runtime/system/object_proxy","ember-runtime/system/core_object","ember-runtime/system/each_proxy","ember-runtime/system/native_array","ember-runtime/system/set","ember-runtime/system/string","ember-runtime/system/deferred","ember-runtime/system/lazy_load","ember-runtime/mixins/array","ember-runtime/mixins/comparable","ember-runtime/mixins/copyable","ember-runtime/mixins/enumerable","ember-runtime/mixins/freezable","ember-runtime/mixins/-proxy","ember-runtime/mixins/observable","ember-runtime/mixins/action_handler","ember-runtime/mixins/deferred","ember-runtime/mixins/mutable_enumerable","ember-runtime/mixins/mutable_array","ember-runtime/mixins/target_action_support","ember-runtime/mixins/evented","ember-runtime/mixins/promise_proxy","ember-runtime/mixins/sortable","ember-runtime/computed/array_computed","ember-runtime/computed/reduce_computed","ember-runtime/computed/reduce_computed_macros","ember-runtime/controllers/array_controller","ember-runtime/controllers/object_controller","ember-runtime/controllers/controller","ember-runtime/mixins/controller","ember-runtime/system/service","ember-runtime/ext/rsvp","ember-runtime/ext/string","ember-runtime/ext/function","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__, __dependency22__, __dependency23__, __dependency24__, __dependency25__, __dependency26__, __dependency27__, __dependency28__, __dependency29__, __dependency30__, __dependency31__, __dependency32__, __dependency33__, __dependency34__, __dependency35__, __dependency36__, __dependency37__, __dependency38__, __dependency39__, __dependency40__, __dependency41__, __dependency42__, __dependency43__, __dependency44__, __dependency45__, __exports__) { "use strict"; /** + Ember Runtime + @module ember @submodule ember-runtime + @requires ember-metal */ - var Ember = __dependency1__["default"]; - // Ember.lookup, Ember.assert - - var get = __dependency2__.get; - var set = __dependency3__.set; - var typeOf = __dependency4__.typeOf; - var Mixin = __dependency5__.Mixin; - var computed = __dependency6__.computed; - - /** - `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. - See also `Ember.ViewTargetActionSupport`, which has - view-aware defaults for target and actionContext. + // BEGIN IMPORTS + var Ember = __dependency1__["default"]; + var isEqual = __dependency2__.isEqual; + var compare = __dependency3__["default"]; + var copy = __dependency4__["default"]; + var inject = __dependency5__["default"]; - @class TargetActionSupport - @namespace Ember - @extends Ember.Mixin - */ - var TargetActionSupport = Mixin.create({ - target: null, - action: null, - actionContext: null, + var Namespace = __dependency6__["default"]; + var EmberObject = __dependency7__["default"]; + var TrackedArray = __dependency8__["default"]; + var SubArray = __dependency9__["default"]; + var Container = __dependency10__["default"]; + var ArrayProxy = __dependency11__["default"]; + var ObjectProxy = __dependency12__["default"]; + var CoreObject = __dependency13__["default"]; + var EachArray = __dependency14__.EachArray; + var EachProxy = __dependency14__.EachProxy; + + var NativeArray = __dependency15__["default"]; + var Set = __dependency16__["default"]; + var EmberStringUtils = __dependency17__["default"]; + var Deferred = __dependency18__["default"]; + var onLoad = __dependency19__.onLoad; + var runLoadHooks = __dependency19__.runLoadHooks; + + var EmberArray = __dependency20__["default"]; + var Comparable = __dependency21__["default"]; + var Copyable = __dependency22__["default"]; + var Enumerable = __dependency23__["default"]; + var Freezable = __dependency24__.Freezable; + var FROZEN_ERROR = __dependency24__.FROZEN_ERROR; + var _ProxyMixin = __dependency25__["default"]; + + var Observable = __dependency26__["default"]; + var ActionHandler = __dependency27__["default"]; + var DeferredMixin = __dependency28__["default"]; + var MutableEnumerable = __dependency29__["default"]; + var MutableArray = __dependency30__["default"]; + var TargetActionSupport = __dependency31__["default"]; + var Evented = __dependency32__["default"]; + var PromiseProxyMixin = __dependency33__["default"]; + var SortableMixin = __dependency34__["default"]; + var arrayComputed = __dependency35__.arrayComputed; + var ArrayComputedProperty = __dependency35__.ArrayComputedProperty; + + var reduceComputed = __dependency36__.reduceComputed; + var ReduceComputedProperty = __dependency36__.ReduceComputedProperty; + + var sum = __dependency37__.sum; + var min = __dependency37__.min; + var max = __dependency37__.max; + var map = __dependency37__.map; + var sort = __dependency37__.sort; + var setDiff = __dependency37__.setDiff; + var mapBy = __dependency37__.mapBy; + var mapProperty = __dependency37__.mapProperty; + var filter = __dependency37__.filter; + var filterBy = __dependency37__.filterBy; + var filterProperty = __dependency37__.filterProperty; + var uniq = __dependency37__.uniq; + var union = __dependency37__.union; + var intersect = __dependency37__.intersect; + + var ArrayController = __dependency38__["default"]; + var ObjectController = __dependency39__["default"]; + var Controller = __dependency40__["default"]; + var ControllerMixin = __dependency41__["default"]; + + var Service = __dependency42__["default"]; + + var RSVP = __dependency43__["default"]; + // just for side effect of extending Ember.RSVP + // just for side effect of extending String.prototype + // just for side effect of extending Function.prototype + // END IMPORTS - targetObject: computed(function() { - var target = get(this, 'target'); + // BEGIN EXPORTS + Ember.compare = compare; + Ember.copy = copy; + Ember.isEqual = isEqual; - if (typeOf(target) === "string") { - var value = get(this, target); - if (value === undefined) { value = get(Ember.lookup, target); } - return value; - } else { - return target; - } - }).property('target'), + + Ember.inject = inject; + - actionContextObject: computed(function() { - var actionContext = get(this, 'actionContext'); + Ember.Array = EmberArray; - if (typeOf(actionContext) === "string") { - var value = get(this, actionContext); - if (value === undefined) { value = get(Ember.lookup, actionContext); } - return value; - } else { - return actionContext; - } - }).property('actionContext'), + Ember.Comparable = Comparable; + Ember.Copyable = Copyable; - /** - Send an `action` with an `actionContext` to a `target`. The action, actionContext - and target will be retrieved from properties of the object. For example: + Ember.SortableMixin = SortableMixin; - ```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 - } - }); - ``` + Ember.Freezable = Freezable; + Ember.FROZEN_ERROR = FROZEN_ERROR; - The `target`, `action`, and `actionContext` can be provided as properties of - an optional object argument to `triggerAction` as well. + Ember.DeferredMixin = DeferredMixin; - ```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 - } - }); - ``` + Ember.MutableEnumerable = MutableEnumerable; + Ember.MutableArray = MutableArray; - 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: + Ember.TargetActionSupport = TargetActionSupport; + Ember.Evented = Evented; - ```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 - } - }); - ``` + Ember.PromiseProxyMixin = PromiseProxyMixin; - @method triggerAction - @param opts {Hash} (optional, with the optional keys action, target and/or actionContext) - @return {Boolean} true if the action was sent successfully and did not return false - */ - triggerAction: function(opts) { - opts = opts || {}; - var action = opts.action || get(this, 'action'), - target = opts.target || get(this, 'targetObject'), - actionContext = opts.actionContext; + Ember.Observable = Observable; - function args(options, actionName) { - var ret = []; - if (actionName) { ret.push(actionName); } + Ember.arrayComputed = arrayComputed; + Ember.ArrayComputedProperty = ArrayComputedProperty; + Ember.reduceComputed = reduceComputed; + Ember.ReduceComputedProperty = ReduceComputedProperty; - return ret.concat(options); - } + // ES6TODO: this seems a less than ideal way/place to add properties to Ember.computed + var EmComputed = Ember.computed; - if (typeof actionContext === 'undefined') { - actionContext = get(this, 'actionContextObject') || this; - } + EmComputed.sum = sum; + EmComputed.min = min; + EmComputed.max = max; + EmComputed.map = map; + EmComputed.sort = sort; + EmComputed.setDiff = setDiff; + EmComputed.mapBy = mapBy; + EmComputed.mapProperty = mapProperty; + EmComputed.filter = filter; + EmComputed.filterBy = filterBy; + EmComputed.filterProperty = filterProperty; + EmComputed.uniq = uniq; + EmComputed.union = union; + EmComputed.intersect = intersect; + + Ember.String = EmberStringUtils; + Ember.Object = EmberObject; + Ember.TrackedArray = TrackedArray; + Ember.SubArray = SubArray; + Ember.Container = Container; + Ember.Namespace = Namespace; + Ember.Enumerable = Enumerable; + Ember.ArrayProxy = ArrayProxy; + Ember.ObjectProxy = ObjectProxy; + Ember.ActionHandler = ActionHandler; + Ember.CoreObject = CoreObject; + Ember.EachArray = EachArray; + Ember.EachProxy = EachProxy; + Ember.NativeArray = NativeArray; + // ES6TODO: Currently we must rely on the global from ember-metal/core to avoid circular deps + // Ember.A = A; + Ember.Set = Set; + Ember.Deferred = Deferred; + Ember.onLoad = onLoad; + Ember.runLoadHooks = runLoadHooks; - if (target && action) { - var ret; + Ember.ArrayController = ArrayController; + Ember.ObjectController = ObjectController; + Ember.Controller = Controller; + Ember.ControllerMixin = ControllerMixin; - if (target.send) { - ret = target.send.apply(target, args(actionContext, action)); - } else { - Ember.assert("The action '" + action + "' did not exist on " + target, typeof target[action] === 'function'); - ret = target[action].apply(target, args(actionContext)); - } + + Ember.Service = Service; + - if (ret !== false) ret = true; + Ember._ProxyMixin = _ProxyMixin; - return ret; - } else { - return false; - } - } - }); + Ember.RSVP = RSVP; + // END EXPORTS - __exports__["default"] = TargetActionSupport; + __exports__["default"] = Ember; }); -define("ember-runtime/system/application", - ["ember-runtime/system/namespace","exports"], - function(__dependency1__, __exports__) { +enifed("ember-runtime/compare", + ["ember-metal/utils","ember-runtime/mixins/comparable","exports"], + function(__dependency1__, __dependency2__, __exports__) { "use strict"; - var Namespace = __dependency1__["default"]; + var typeOf = __dependency1__.typeOf; + var Comparable = __dependency2__["default"]; + + var TYPE_ORDER = { + 'undefined': 0, + 'null': 1, + 'boolean': 2, + 'number': 3, + 'string': 4, + 'array': 5, + 'object': 6, + 'instance': 7, + 'function': 8, + 'class': 9, + 'date': 10 + }; - var Application = Namespace.extend(); - __exports__["default"] = Application; - }); -define("ember-runtime/system/array_proxy", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/computed","ember-metal/mixin","ember-metal/property_events","ember-metal/error","ember-runtime/system/object","ember-runtime/mixins/mutable_array","ember-runtime/mixins/enumerable","ember-runtime/system/string","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // Ember.K, Ember.assert - var get = __dependency2__.get; - var set = __dependency3__.set; - var isArray = __dependency4__.isArray; - var apply = __dependency4__.apply; - var computed = __dependency5__.computed; - var beforeObserver = __dependency6__.beforeObserver; - var observer = __dependency6__.observer; - var beginPropertyChanges = __dependency7__.beginPropertyChanges; - var endPropertyChanges = __dependency7__.endPropertyChanges; - var EmberError = __dependency8__["default"]; - var EmberObject = __dependency9__["default"];var MutableArray = __dependency10__["default"];var Enumerable = __dependency11__["default"]; - var fmt = __dependency12__.fmt; + // + // the spaceship operator + // + function spaceship(a, b) { + var diff = a - b; + return (diff > 0) - (diff < 0); + } /** - @module ember - @submodule ember-runtime + This will compare two javascript values of possibly different types. + It will tell you which one is greater than the other by returning: + + - -1 if the first is smaller than the second, + - 0 if both are equal, + - 1 if the first is greater than the second. + + The order is calculated based on `Ember.ORDER_DEFINITION`, if types are different. + In case they have the same type an appropriate comparison for this type is made. + + ```javascript + Ember.compare('hello', 'hello'); // 0 + Ember.compare('abc', 'dfg'); // -1 + Ember.compare(2, 1); // 1 + ``` + + @method compare + @for Ember + @param {Object} v First value to compare + @param {Object} w Second value to compare + @return {Number} -1 if v < w, 0 if v = w and 1 if v > w. */ + __exports__["default"] = function compare(v, w) { + if (v === w) { + return 0; + } - var OUT_OF_RANGE_EXCEPTION = "Index out of range"; - var EMPTY = []; - var alias = computed.alias; - var K = Ember.K; + var type1 = typeOf(v); + var type2 = typeOf(w); - /** - An ArrayProxy wraps any other object that implements `Ember.Array` and/or - `Ember.MutableArray,` forwarding all requests. This makes it very useful for - a number of binding use cases or other cases where being able to swap - out the underlying array is useful. + if (Comparable) { + if (type1 === 'instance' && Comparable.detect(v) && v.constructor.compare) { + return v.constructor.compare(v, w); + } - A simple example of usage: + if (type2 === 'instance' && Comparable.detect(w) && w.constructor.compare) { + return w.constructor.compare(w, v) * -1; + } + } - ```javascript - var pets = ['dog', 'cat', 'fish']; - var ap = Ember.ArrayProxy.create({ content: Ember.A(pets) }); + var res = spaceship(TYPE_ORDER[type1], TYPE_ORDER[type2]); - ap.get('firstObject'); // 'dog' - ap.set('content', ['amoeba', 'paramecium']); - ap.get('firstObject'); // 'amoeba' - ``` + if (res !== 0) { + return res; + } - This class can also be useful as a layer to transform the contents of - an array, as they are accessed. This can be done by overriding - `objectAtContent`: + // types are equal - so we have to check values now + switch (type1) { + case 'boolean': + case 'number': + return spaceship(v,w); - ```javascript - var pets = ['dog', 'cat', 'fish']; - var ap = Ember.ArrayProxy.create({ - content: Ember.A(pets), - objectAtContent: function(idx) { - return this.get('content').objectAt(idx).toUpperCase(); + case 'string': + return spaceship(v.localeCompare(w), 0); + + case 'array': + var vLen = v.length; + var wLen = w.length; + var len = Math.min(vLen, wLen); + + for (var i = 0; i < len; i++) { + var r = compare(v[i], w[i]); + if (r !== 0) { + return r; + } } - }); - ap.get('firstObject'); // . 'DOG' - ``` + // all elements are equal now + // shorter array should be ordered first + return spaceship(vLen, wLen); - @class ArrayProxy - @namespace Ember - @extends Ember.Object - @uses Ember.MutableArray - */ - var ArrayProxy = EmberObject.extend(MutableArray, { + case 'instance': + if (Comparable && Comparable.detect(v)) { + return v.compare(v, w); + } + return 0; - /** - The content array. Must be an object that implements `Ember.Array` and/or - `Ember.MutableArray.` + case 'date': + return spaceship(v.getTime(), w.getTime()); - @property content - @type Ember.Array - */ - content: null, + default: + return 0; + } + } + }); +enifed("ember-runtime/computed/array_computed", + ["ember-metal/core","ember-runtime/computed/reduce_computed","ember-metal/enumerable_utils","ember-metal/platform","ember-metal/observer","ember-metal/error","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + var ReduceComputedProperty = __dependency2__.ReduceComputedProperty; + var forEach = __dependency3__.forEach; + var o_create = __dependency4__.create; + var addObserver = __dependency5__.addObserver; + var EmberError = __dependency6__["default"]; - /** - The array that the proxy pretends to be. In the default `ArrayProxy` - implementation, this and `content` are the same. Subclasses of `ArrayProxy` - can override this property to provide things like sorting and filtering. + var a_slice = [].slice; - @property arrangedContent - */ - arrangedContent: alias('content'), + function ArrayComputedProperty() { + var cp = this; - /** - Should actually retrieve the object at the specified index from the - content. You can override this method in subclasses to transform the - content item to something new. + ReduceComputedProperty.apply(this, arguments); - This method will only be called if content is non-`null`. + this.func = (function(reduceFunc) { + return function (propertyName) { + if (!cp._hasInstanceMeta(this, propertyName)) { + // When we recompute an array computed property, we need already + // retrieved arrays to be updated; we can't simply empty the cache and + // hope the array is re-retrieved. + forEach(cp._dependentKeys, function(dependentKey) { + addObserver(this, dependentKey, function() { + cp.recomputeOnce.call(this, propertyName); + }); + }, this); + } - @method objectAtContent - @param {Number} idx The index to retrieve. - @return {Object} the value or undefined if none found - */ - objectAtContent: function(idx) { - return get(this, 'arrangedContent').objectAt(idx); - }, + return reduceFunc.apply(this, arguments); + }; + })(this.func); - /** - Should actually replace the specified objects on the content array. - You can override this method in subclasses to transform the content item - into something new. + return this; + } - This method will only be called if content is non-`null`. + ArrayComputedProperty.prototype = o_create(ReduceComputedProperty.prototype); - @method replaceContent - @param {Number} idx The starting index - @param {Number} amt The number of items to remove from the content. - @param {Array} objects Optional array of objects to insert or null if no - objects. - @return {void} - */ - replaceContent: function(idx, amt, objects) { - get(this, 'content').replace(idx, amt, objects); - }, + ArrayComputedProperty.prototype.initialValue = function () { + return Ember.A(); + }; - /** - Invoked when the content property is about to change. Notifies observers that the - entire array content will change. + ArrayComputedProperty.prototype.resetValue = function (array) { + array.clear(); + return array; + }; - @private - @method _contentWillChange - */ - _contentWillChange: beforeObserver('content', function() { - this._teardownContent(); - }), + // This is a stopgap to keep the reference counts correct with lazy CPs. + ArrayComputedProperty.prototype.didChange = function (obj, keyName) { + return; + }; + + /** + Creates a computed property which operates on dependent arrays and + is updated with "one at a time" semantics. When items are added or + removed from the dependent array(s) an array computed only operates + on the change instead of re-evaluating the entire array. This should + return an array, if you'd like to use "one at a time" semantics and + compute some value other then an array look at + `Ember.reduceComputed`. + + If there are more than one arguments the first arguments are + considered to be dependent property keys. The last argument is + required to be an options object. The options object can have the + following three properties. - _teardownContent: function() { - var content = get(this, 'content'); + `initialize` - An optional initialize function. Typically this will be used + to set up state on the instanceMeta object. - if (content) { - content.removeArrayObserver(this, { - willChange: 'contentArrayWillChange', - didChange: 'contentArrayDidChange' - }); - } - }, + `removedItem` - A function that is called each time an element is + removed from the array. - contentArrayWillChange: K, - contentArrayDidChange: K, + `addedItem` - A function that is called each time an element is + added to the array. - /** - Invoked when the content property changes. Notifies observers that the - entire array content has changed. - @private - @method _contentDidChange - */ - _contentDidChange: observer('content', function() { - var content = get(this, 'content'); + The `initialize` function has the following signature: - Ember.assert("Can't set ArrayProxy's content to itself", content !== this); + ```javascript + function(array, changeMeta, instanceMeta) + ``` - this._setupContent(); - }), + `array` - The initial value of the arrayComputed, an empty array. - _setupContent: function() { - var content = get(this, 'content'); + `changeMeta` - An object which contains meta information about the + computed. It contains the following properties: - if (content) { - Ember.assert(fmt('ArrayProxy expects an Array or ' + - 'Ember.ArrayProxy, but you passed %@', [typeof content]), - isArray(content) || content.isDestroyed); + - `property` the computed property + - `propertyName` the name of the property on the object - content.addArrayObserver(this, { - willChange: 'contentArrayWillChange', - didChange: 'contentArrayDidChange' - }); - } - }, + `instanceMeta` - An object that can be used to store meta + information needed for calculating your computed. For example a + unique computed might use this to store the number of times a given + element is found in the dependent array. - _arrangedContentWillChange: beforeObserver('arrangedContent', function() { - var arrangedContent = get(this, 'arrangedContent'), - len = arrangedContent ? get(arrangedContent, 'length') : 0; - this.arrangedContentArrayWillChange(this, 0, len, undefined); - this.arrangedContentWillChange(this); + The `removedItem` and `addedItem` functions both have the following signature: - this._teardownArrangedContent(arrangedContent); - }), + ```javascript + function(accumulatedValue, item, changeMeta, instanceMeta) + ``` - _arrangedContentDidChange: observer('arrangedContent', function() { - var arrangedContent = get(this, 'arrangedContent'), - len = arrangedContent ? get(arrangedContent, 'length') : 0; + `accumulatedValue` - The value returned from the last time + `removedItem` or `addedItem` was called or an empty array. - Ember.assert("Can't set ArrayProxy's content to itself", arrangedContent !== this); + `item` - the element added or removed from the array - this._setupArrangedContent(); + `changeMeta` - An object which contains meta information about the + change. It contains the following properties: - this.arrangedContentDidChange(this); - this.arrangedContentArrayDidChange(this, 0, undefined, len); - }), + - `property` the computed property + - `propertyName` the name of the property on the object + - `index` the index of the added or removed item + - `item` the added or removed item: this is exactly the same as + the second arg + - `arrayChanged` the array that triggered the change. Can be + useful when depending on multiple arrays. - _setupArrangedContent: function() { - var arrangedContent = get(this, 'arrangedContent'); + For property changes triggered on an item property change (when + depKey is something like `someArray.@each.someProperty`), + `changeMeta` will also contain the following property: - if (arrangedContent) { - Ember.assert(fmt('ArrayProxy expects an Array or ' + - 'Ember.ArrayProxy, but you passed %@', [typeof arrangedContent]), - isArray(arrangedContent) || arrangedContent.isDestroyed); + - `previousValues` an object whose keys are the properties that changed on + the item, and whose values are the item's previous values. - arrangedContent.addArrayObserver(this, { - willChange: 'arrangedContentArrayWillChange', - didChange: 'arrangedContentArrayDidChange' - }); - } - }, + `previousValues` is important Ember coalesces item property changes via + Ember.run.once. This means that by the time removedItem gets called, item has + the new values, but you may need the previous value (eg for sorting & + filtering). - _teardownArrangedContent: function() { - var arrangedContent = get(this, 'arrangedContent'); + `instanceMeta` - An object that can be used to store meta + information needed for calculating your computed. For example a + unique computed might use this to store the number of times a given + element is found in the dependent array. - if (arrangedContent) { - arrangedContent.removeArrayObserver(this, { - willChange: 'arrangedContentArrayWillChange', - didChange: 'arrangedContentArrayDidChange' - }); - } - }, + The `removedItem` and `addedItem` functions should return the accumulated + value. It is acceptable to not return anything (ie return undefined) + to invalidate the computation. This is generally not a good idea for + arrayComputed but it's used in eg max and min. - arrangedContentWillChange: K, - arrangedContentDidChange: K, + Example - objectAt: function(idx) { - return get(this, 'content') && this.objectAtContent(idx); - }, + ```javascript + Ember.computed.map = function(dependentKey, callback) { + var options = { + addedItem: function(array, item, changeMeta, instanceMeta) { + var mapped = callback(item); + array.insertAt(changeMeta.index, mapped); + return array; + }, + removedItem: function(array, item, changeMeta, instanceMeta) { + array.removeAt(changeMeta.index, 1); + return array; + } + }; - length: computed(function() { - var arrangedContent = get(this, 'arrangedContent'); - return arrangedContent ? get(arrangedContent, 'length') : 0; - // No dependencies since Enumerable notifies length of change - }), + return Ember.arrayComputed(dependentKey, options); + }; + ``` - _replace: function(idx, amt, objects) { - var content = get(this, 'content'); - Ember.assert('The content property of '+ this.constructor + ' should be set before modifying it', content); - if (content) this.replaceContent(idx, amt, objects); - return this; - }, + @method arrayComputed + @for Ember + @param {String} [dependentKeys*] + @param {Object} options + @return {Ember.ComputedProperty} + */ + function arrayComputed (options) { + var args; - replace: function() { - if (get(this, 'arrangedContent') === get(this, 'content')) { - apply(this, this._replace, arguments); - } else { - throw new EmberError("Using replace on an arranged ArrayProxy is not allowed."); - } - }, + if (arguments.length > 1) { + args = a_slice.call(arguments, 0, -1); + options = a_slice.call(arguments, -1)[0]; + } - _insertAt: function(idx, object) { - if (idx > get(this, 'content.length')) throw new EmberError(OUT_OF_RANGE_EXCEPTION); - this._replace(idx, 0, [object]); - return this; - }, + if (typeof options !== 'object') { + throw new EmberError('Array Computed Property declared without an options hash'); + } - insertAt: function(idx, object) { - if (get(this, 'arrangedContent') === get(this, 'content')) { - return this._insertAt(idx, object); - } else { - throw new EmberError("Using insertAt on an arranged ArrayProxy is not allowed."); - } - }, + var cp = new ArrayComputedProperty(options); - removeAt: function(start, len) { - if ('number' === typeof start) { - var content = get(this, 'content'), - arrangedContent = get(this, 'arrangedContent'), - indices = [], i; + if (args) { + cp.property.apply(cp, args); + } - if ((start < 0) || (start >= get(this, 'length'))) { - throw new EmberError(OUT_OF_RANGE_EXCEPTION); - } + return cp; + } - if (len === undefined) len = 1; + __exports__.arrayComputed = arrayComputed; + __exports__.ArrayComputedProperty = ArrayComputedProperty; + }); +enifed("ember-runtime/computed/reduce_computed", + ["ember-metal/core","ember-metal/property_get","ember-metal/utils","ember-metal/error","ember-metal/property_events","ember-metal/expand_properties","ember-metal/observer","ember-metal/computed","ember-metal/platform","ember-metal/enumerable_utils","ember-runtime/system/tracked_array","ember-runtime/mixins/array","ember-metal/run_loop","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // Ember.assert + var e_get = __dependency2__.get; + var guidFor = __dependency3__.guidFor; + var metaFor = __dependency3__.meta; + var EmberError = __dependency4__["default"]; + var propertyWillChange = __dependency5__.propertyWillChange; + var propertyDidChange = __dependency5__.propertyDidChange; + var expandProperties = __dependency6__["default"]; + var addObserver = __dependency7__.addObserver; + var removeObserver = __dependency7__.removeObserver; + var addBeforeObserver = __dependency7__.addBeforeObserver; + var removeBeforeObserver = __dependency7__.removeBeforeObserver; + var ComputedProperty = __dependency8__.ComputedProperty; + var cacheFor = __dependency8__.cacheFor; + var o_create = __dependency9__.create; + var forEach = __dependency10__.forEach; + var TrackedArray = __dependency11__["default"]; + var EmberArray = __dependency12__["default"]; + var run = __dependency13__["default"]; + var isArray = __dependency3__.isArray; - // Get a list of indices in original content to remove - for (i=start; i TrackedArray instances. We use + // this to lazily recompute indexes for item property observers. + this.trackedArraysByGuid = {}; - unshiftObjects: function(objects) { - this._replace(0, 0, objects); - return this; - }, + // We suspend observers to ignore replacements from `reset` when totally + // recomputing. Unfortunately we cannot properly suspend the observers + // because we only have the key; instead we make the observers no-ops + this.suspended = false; - slice: function() { - var arr = this.toArray(); - return arr.slice.apply(arr, arguments); - }, + // This is used to coalesce item changes from property observers within a + // single item. + this.changedItems = {}; + // This is used to coalesce item changes for multiple items that depend on + // some shared state. + this.changedItemCount = 0; + } - arrangedContentArrayWillChange: function(item, idx, removedCnt, addedCnt) { - this.arrayContentWillChange(idx, removedCnt, addedCnt); - }, + function ItemPropertyObserverContext (dependentArray, index, trackedArray) { + Ember.assert('Internal error: trackedArray is null or undefined', trackedArray); - arrangedContentArrayDidChange: function(item, idx, removedCnt, addedCnt) { - this.arrayContentDidChange(idx, removedCnt, addedCnt); + this.dependentArray = dependentArray; + this.index = index; + this.item = dependentArray.objectAt(index); + this.trackedArray = trackedArray; + this.beforeObserver = null; + this.observer = null; + this.destroyed = false; + } + + DependentArraysObserver.prototype = { + setValue: function (newValue) { + this.instanceMeta.setValue(newValue, true); }, - init: function() { - this._super(); - this._setupContent(); - this._setupArrangedContent(); + getValue: function () { + return this.instanceMeta.getValue(); }, - willDestroy: function() { - this._teardownArrangedContent(); - this._teardownContent(); - } - }); + setupObservers: function (dependentArray, dependentKey) { + this.dependentKeysByGuid[guidFor(dependentArray)] = dependentKey; - __exports__["default"] = ArrayProxy; - }); -define("ember-runtime/system/container", - ["ember-metal/property_set","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var set = __dependency1__["default"]; + dependentArray.addArrayObserver(this, { + willChange: 'dependentArrayWillChange', + didChange: 'dependentArrayDidChange' + }); - var Container = requireModule('container')["default"]; - Container.set = set; + if (this.cp._itemPropertyKeys[dependentKey]) { + this.setupPropertyObservers(dependentKey, this.cp._itemPropertyKeys[dependentKey]); + } + }, - __exports__["default"] = Container; - }); -define("ember-runtime/system/core_object", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/platform","ember-metal/watching","ember-metal/chains","ember-metal/events","ember-metal/mixin","ember-metal/enumerable_utils","ember-metal/error","ember-runtime/keys","ember-runtime/mixins/action_handler","ember-metal/properties","ember-metal/binding","ember-metal/computed","ember-metal/run_loop","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-runtime - */ + teardownObservers: function (dependentArray, dependentKey) { + var itemPropertyKeys = this.cp._itemPropertyKeys[dependentKey] || []; - var Ember = __dependency1__["default"]; - // Ember.ENV.MANDATORY_SETTER, Ember.assert, Ember.K, Ember.config + delete this.dependentKeysByGuid[guidFor(dependentArray)]; - // NOTE: this object should never be included directly. Instead use `Ember.Object`. - // We only define this separately so that `Ember.Set` can depend on it. - var get = __dependency2__.get; - var set = __dependency3__.set; - var guidFor = __dependency4__.guidFor; - var apply = __dependency4__.apply; - var create = __dependency5__.create; - var generateGuid = __dependency4__.generateGuid; - var GUID_KEY = __dependency4__.GUID_KEY; - var meta = __dependency4__.meta; - var META_KEY = __dependency4__.META_KEY; - var makeArray = __dependency4__.makeArray; - var rewatch = __dependency6__.rewatch; - var finishChains = __dependency7__.finishChains; - var sendEvent = __dependency8__.sendEvent; - var IS_BINDING = __dependency9__.IS_BINDING; - var Mixin = __dependency9__.Mixin; - var required = __dependency9__.required; - var EnumerableUtils = __dependency10__["default"]; - var EmberError = __dependency11__["default"]; - var platform = __dependency5__.platform; - var keys = __dependency12__["default"]; - var ActionHandler = __dependency13__["default"]; - var defineProperty = __dependency14__.defineProperty; - var Binding = __dependency15__.Binding; - var ComputedProperty = __dependency16__.ComputedProperty; - var run = __dependency17__["default"]; - var destroy = __dependency6__.destroy; - - - var o_create = create, - o_defineProperty = platform.defineProperty, - schedule = run.schedule, - applyMixin = Mixin._apply, - finishPartial = Mixin.finishPartial, - reopen = Mixin.prototype.reopen, - MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER, - indexOf = EnumerableUtils.indexOf, - K = Ember.K; + this.teardownPropertyObservers(dependentKey, itemPropertyKeys); - var undefinedDescriptor = { - configurable: true, - writable: true, - enumerable: false, - value: undefined - }; + dependentArray.removeArrayObserver(this, { + willChange: 'dependentArrayWillChange', + didChange: 'dependentArrayDidChange' + }); + }, - var nullDescriptor = { - configurable: true, - writable: true, - enumerable: false, - value: null - }; + suspendArrayObservers: function (callback, binding) { + var oldSuspended = this.suspended; + this.suspended = true; + callback.call(binding); + this.suspended = oldSuspended; + }, - function makeCtor() { + setupPropertyObservers: function (dependentKey, itemPropertyKeys) { + var dependentArray = get(this.instanceMeta.context, dependentKey); + var length = get(dependentArray, 'length'); + var observerContexts = new Array(length); - // Note: avoid accessing any properties on the object since it makes the - // method a lot faster. This is glue code so we want it to be as fast as - // possible. + this.resetTransformations(dependentKey, observerContexts); - var wasApplied = false, initMixins, initProperties; + forEach(dependentArray, function (item, index) { + var observerContext = this.createPropertyObserverContext(dependentArray, index, this.trackedArraysByGuid[dependentKey]); + observerContexts[index] = observerContext; - var Class = function() { - if (!wasApplied) { - Class.proto(); // prepare prototype... - } - o_defineProperty(this, GUID_KEY, nullDescriptor); - o_defineProperty(this, '__nextSuper', undefinedDescriptor); - var m = meta(this), proto = m.proto; - m.proto = this; - if (initMixins) { - // capture locally so we can clear the closed over variable - var mixins = initMixins; - initMixins = null; - apply(this, this.reopen, mixins); - } - if (initProperties) { - // capture locally so we can clear the closed over variable - var props = initProperties; - initProperties = null; + forEach(itemPropertyKeys, function (propertyKey) { + addBeforeObserver(item, propertyKey, this, observerContext.beforeObserver); + addObserver(item, propertyKey, this, observerContext.observer); + }, this); + }, this); + }, - var concatenatedProperties = this.concatenatedProperties; + teardownPropertyObservers: function (dependentKey, itemPropertyKeys) { + var dependentArrayObserver = this; + var trackedArray = this.trackedArraysByGuid[dependentKey]; + var beforeObserver, observer, item; - for (var i = 0, l = props.length; i < l; i++) { - var properties = props[i]; + if (!trackedArray) { return; } - Ember.assert("Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead.", !(properties instanceof Mixin)); + trackedArray.apply(function (observerContexts, offset, operation) { + if (operation === TrackedArray.DELETE) { return; } - if (typeof properties !== 'object' && properties !== undefined) { - throw new EmberError("Ember.Object.create only accepts objects."); - } + forEach(observerContexts, function (observerContext) { + observerContext.destroyed = true; + beforeObserver = observerContext.beforeObserver; + observer = observerContext.observer; + item = observerContext.item; - if (!properties) { continue; } + forEach(itemPropertyKeys, function (propertyKey) { + removeBeforeObserver(item, propertyKey, dependentArrayObserver, beforeObserver); + removeObserver(item, propertyKey, dependentArrayObserver, observer); + }); + }); + }); + }, - var keyNames = keys(properties); + createPropertyObserverContext: function (dependentArray, index, trackedArray) { + var observerContext = new ItemPropertyObserverContext(dependentArray, index, trackedArray); - for (var j = 0, ll = keyNames.length; j < ll; j++) { - var keyName = keyNames[j]; - if (!properties.hasOwnProperty(keyName)) { continue; } + this.createPropertyObserver(observerContext); - var value = properties[keyName]; + return observerContext; + }, - if (IS_BINDING.test(keyName)) { - var bindings = m.bindings; - if (!bindings) { - bindings = m.bindings = {}; - } else if (!m.hasOwnProperty('bindings')) { - bindings = m.bindings = o_create(m.bindings); - } - bindings[keyName] = value; - } + createPropertyObserver: function (observerContext) { + var dependentArrayObserver = this; - var desc = m.descs[keyName]; + observerContext.beforeObserver = function (obj, keyName) { + return dependentArrayObserver.itemPropertyWillChange(obj, keyName, observerContext.dependentArray, observerContext); + }; - Ember.assert("Ember.Object.create no longer supports defining computed properties. Define computed properties using extend() or reopen() before calling create().", !(value instanceof ComputedProperty)); - Ember.assert("Ember.Object.create no longer supports defining methods that call _super.", !(typeof value === 'function' && value.toString().indexOf('._super') !== -1)); - Ember.assert("`actions` must be provided at extend time, not at create " + - "time, when Ember.ActionHandler is used (i.e. views, " + - "controllers & routes).", !((keyName === 'actions') && ActionHandler.detect(this))); + observerContext.observer = function (obj, keyName) { + return dependentArrayObserver.itemPropertyDidChange(obj, keyName, observerContext.dependentArray, observerContext); + }; + }, - if (concatenatedProperties && indexOf(concatenatedProperties, keyName) >= 0) { - var baseValue = this[keyName]; + resetTransformations: function (dependentKey, observerContexts) { + this.trackedArraysByGuid[dependentKey] = new TrackedArray(observerContexts); + }, - if (baseValue) { - if ('function' === typeof baseValue.concat) { - value = baseValue.concat(value); - } else { - value = makeArray(baseValue).concat(value); - } - } else { - value = makeArray(value); - } - } + trackAdd: function (dependentKey, index, newItems) { + var trackedArray = this.trackedArraysByGuid[dependentKey]; - if (desc) { - desc.set(this, keyName, value); - } else { - if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) { - this.setUnknownProperty(keyName, value); - } else if (MANDATORY_SETTER) { - defineProperty(this, keyName, null, value); // setup mandatory setter - } else { - this[keyName] = value; - } - } - } - } + if (trackedArray) { + trackedArray.addItems(index, newItems); } - finishPartial(this, m); - apply(this, this.init, arguments); - m.proto = proto; - finishChains(this); - sendEvent(this, "init"); - }; + }, - Class.toString = Mixin.prototype.toString; - Class.willReopen = function() { - if (wasApplied) { - Class.PrototypeMixin = Mixin.create(Class.PrototypeMixin); + trackRemove: function (dependentKey, index, removedCount) { + var trackedArray = this.trackedArraysByGuid[dependentKey]; + + if (trackedArray) { + return trackedArray.removeItems(index, removedCount); } - wasApplied = false; - }; - Class._initMixins = function(args) { initMixins = args; }; - Class._initProperties = function(args) { initProperties = args; }; + return []; + }, - Class.proto = function() { - var superclass = Class.superclass; - if (superclass) { superclass.proto(); } + updateIndexes: function (trackedArray, array) { + var length = get(array, 'length'); + // OPTIMIZE: we could stop updating once we hit the object whose observer + // fired; ie partially apply the transformations + trackedArray.apply(function (observerContexts, offset, operation, operationIndex) { + // we don't even have observer contexts for removed items, even if we did, + // they no longer have any index in the array + if (operation === TrackedArray.DELETE) { return; } + if (operationIndex === 0 && operation === TrackedArray.RETAIN && observerContexts.length === length && offset === 0) { + // If we update many items we don't want to walk the array each time: we + // only need to update the indexes at most once per run loop. + return; + } - if (!wasApplied) { - wasApplied = true; - Class.PrototypeMixin.applyPartial(Class.prototype); - rewatch(Class.prototype); - } + forEach(observerContexts, function (context, index) { + context.index = index + offset; + }); + }); + }, - return this.prototype; - }; + dependentArrayWillChange: function (dependentArray, index, removedCount, addedCount) { + if (this.suspended) { return; } - return Class; + var removedItem = this.callbacks.removedItem; + var changeMeta; + var guid = guidFor(dependentArray); + var dependentKey = this.dependentKeysByGuid[guid]; + var itemPropertyKeys = this.cp._itemPropertyKeys[dependentKey] || []; + var length = get(dependentArray, 'length'); + var normalizedIndex = normalizeIndex(index, length, 0); + var normalizedRemoveCount = normalizeRemoveCount(normalizedIndex, length, removedCount); + var item, itemIndex, sliceIndex, observerContexts; - } + observerContexts = this.trackRemove(dependentKey, normalizedIndex, normalizedRemoveCount); - /** - @class CoreObject - @namespace Ember - */ - var CoreObject = makeCtor(); - CoreObject.toString = function() { return "Ember.CoreObject"; }; + function removeObservers(propertyKey) { + observerContexts[sliceIndex].destroyed = true; + removeBeforeObserver(item, propertyKey, this, observerContexts[sliceIndex].beforeObserver); + removeObserver(item, propertyKey, this, observerContexts[sliceIndex].observer); + } - CoreObject.PrototypeMixin = Mixin.create({ - reopen: function() { - applyMixin(this, arguments, true); - return this; + for (sliceIndex = normalizedRemoveCount - 1; sliceIndex >= 0; --sliceIndex) { + itemIndex = normalizedIndex + sliceIndex; + if (itemIndex >= length) { break; } + + item = dependentArray.objectAt(itemIndex); + + forEach(itemPropertyKeys, removeObservers, this); + + changeMeta = new ChangeMeta(dependentArray, item, itemIndex, this.instanceMeta.propertyName, this.cp, normalizedRemoveCount); + this.setValue(removedItem.call( + this.instanceMeta.context, this.getValue(), item, changeMeta, this.instanceMeta.sugarMeta)); + } + this.callbacks.flushedChanges.call(this.instanceMeta.context, this.getValue(), this.instanceMeta.sugarMeta); }, - /** - An overridable method called when objects are instantiated. By default, - does nothing unless it is overridden during class definition. + dependentArrayDidChange: function (dependentArray, index, removedCount, addedCount) { + if (this.suspended) { return; } - Example: + var addedItem = this.callbacks.addedItem; + var guid = guidFor(dependentArray); + var dependentKey = this.dependentKeysByGuid[guid]; + var observerContexts = new Array(addedCount); + var itemPropertyKeys = this.cp._itemPropertyKeys[dependentKey]; + var length = get(dependentArray, 'length'); + var normalizedIndex = normalizeIndex(index, length, addedCount); + var endIndex = normalizedIndex + addedCount; + var changeMeta, observerContext; + + forEach(dependentArray.slice(normalizedIndex, endIndex), function (item, sliceIndex) { + if (itemPropertyKeys) { + observerContext = this.createPropertyObserverContext(dependentArray, normalizedIndex + sliceIndex, + this.trackedArraysByGuid[dependentKey]); + observerContexts[sliceIndex] = observerContext; - ```javascript - App.Person = Ember.Object.extend({ - init: function() { - alert('Name is ' + this.get('name')); + forEach(itemPropertyKeys, function (propertyKey) { + addBeforeObserver(item, propertyKey, this, observerContext.beforeObserver); + addObserver(item, propertyKey, this, observerContext.observer); + }, this); } - }); - var steve = App.Person.create({ - name: "Steve" - }); + changeMeta = new ChangeMeta(dependentArray, item, normalizedIndex + sliceIndex, this.instanceMeta.propertyName, this.cp, addedCount); + this.setValue(addedItem.call( + this.instanceMeta.context, this.getValue(), item, changeMeta, this.instanceMeta.sugarMeta)); + }, this); + this.callbacks.flushedChanges.call(this.instanceMeta.context, this.getValue(), this.instanceMeta.sugarMeta); + this.trackAdd(dependentKey, normalizedIndex, observerContexts); + }, - // alerts 'Name is Steve'. - ``` + itemPropertyWillChange: function (obj, keyName, array, observerContext) { + var guid = guidFor(obj); - 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. + if (!this.changedItems[guid]) { + this.changedItems[guid] = { + array: array, + observerContext: observerContext, + obj: obj, + previousValues: {} + }; + } - @method init - */ - init: function() {}, + ++this.changedItemCount; + this.changedItems[guid].previousValues[keyName] = get(obj, keyName); + }, - /** - Defines the properties that will be concatenated from the superclass - (instead of overridden). + itemPropertyDidChange: function (obj, keyName, array, observerContext) { + if (--this.changedItemCount === 0) { + this.flushChanges(); + } + }, - 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`. + flushChanges: function () { + var changedItems = this.changedItems; + var key, c, changeMeta; - Here is some sample code showing the difference between a concatenated - property and a normal one: + for (key in changedItems) { + c = changedItems[key]; + if (c.observerContext.destroyed) { continue; } - ```javascript - App.BarView = Ember.View.extend({ - someNonConcatenatedProperty: ['bar'], - classNames: ['bar'] - }); + this.updateIndexes(c.observerContext.trackedArray, c.observerContext.dependentArray); - App.FooBarView = App.BarView.extend({ - someNonConcatenatedProperty: ['foo'], - classNames: ['foo'], - }); + changeMeta = new ChangeMeta(c.array, c.obj, c.observerContext.index, this.instanceMeta.propertyName, this.cp, changedItems.length, c.previousValues); + this.setValue( + this.callbacks.removedItem.call(this.instanceMeta.context, this.getValue(), c.obj, changeMeta, this.instanceMeta.sugarMeta)); + this.setValue( + this.callbacks.addedItem.call(this.instanceMeta.context, this.getValue(), c.obj, changeMeta, this.instanceMeta.sugarMeta)); + } - var fooBarView = App.FooBarView.create(); - fooBarView.get('someNonConcatenatedProperty'); // ['foo'] - fooBarView.get('classNames'); // ['ember-view', 'bar', 'foo'] - ``` + this.changedItems = {}; + this.callbacks.flushedChanges.call(this.instanceMeta.context, this.getValue(), this.instanceMeta.sugarMeta); + } + }; - This behavior extends to object creation as well. Continuing the - above example: + function normalizeIndex(index, length, newItemsOffset) { + if (index < 0) { + return Math.max(0, length + index); + } else if (index < length) { + return index; + } else /* index > length */ { + return Math.min(length - newItemsOffset, index); + } + } - ```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: + function normalizeRemoveCount(index, length, removedCount) { + return Math.min(removedCount, length - index); + } - ```javascript - var view = App.FooBarView.create({ - classNames: 'baz' - }) - view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz'] - ``` + function ChangeMeta(dependentArray, item, index, propertyName, property, changedCount, previousValues){ + this.arrayChanged = dependentArray; + this.index = index; + this.item = item; + this.propertyName = propertyName; + this.property = property; + this.changedCount = changedCount; - Using the `concatenatedProperties` property, we can tell to Ember that mix - the content of the properties. + if (previousValues) { + // previous values only available for item property changes + this.previousValues = previousValues; + } + } - In `Ember.View` the `classNameBindings` and `attributeBindings` properties - are also concatenated, in addition to `classNames`. + function addItems(dependentArray, callbacks, cp, propertyName, meta) { + forEach(dependentArray, function (item, index) { + meta.setValue( callbacks.addedItem.call( + this, meta.getValue(), item, new ChangeMeta(dependentArray, item, index, propertyName, cp, dependentArray.length), meta.sugarMeta)); + }, this); + callbacks.flushedChanges.call(this, meta.getValue(), meta.sugarMeta); + } - 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). + function reset(cp, propertyName) { + var hadMeta = cp._hasInstanceMeta(this, propertyName); + var meta = cp._instanceMeta(this, propertyName); - @property concatenatedProperties - @type Array - @default null - */ - concatenatedProperties: null, + if (hadMeta) { meta.setValue(cp.resetValue(meta.getValue())); } - /** - Destroyed object property flag. + if (cp.options.initialize) { + cp.options.initialize.call(this, meta.getValue(), { + property: cp, + propertyName: propertyName + }, meta.sugarMeta); + } + } - if this property is `true` the observers and bindings were already - removed by the effect of calling the `destroy()` method. + function partiallyRecomputeFor(obj, dependentKey) { + if (arrayBracketPattern.test(dependentKey)) { + return false; + } - @property isDestroyed - @default false - */ - isDestroyed: false, + var value = get(obj, dependentKey); + return EmberArray.detect(value); + } - /** - Destruction scheduled flag. The `destroy()` method has been called. + function ReduceComputedPropertyInstanceMeta(context, propertyName, initialValue) { + this.context = context; + this.propertyName = propertyName; + this.cache = metaFor(context).cache; + this.dependentArrays = {}; + this.sugarMeta = {}; + this.initialValue = initialValue; + } - The object stays intact until the end of the run loop at which point - the `isDestroyed` flag is set. + ReduceComputedPropertyInstanceMeta.prototype = { + getValue: function () { + var value = cacheGet(this.cache, this.propertyName); - @property isDestroying - @default false - */ - isDestroying: false, + if (value !== undefined) { + return value; + } else { + return this.initialValue; + } + }, - /** - Destroys an object by setting the `isDestroyed` flag and removing its - metadata, which effectively destroys observers and bindings. + setValue: function(newValue, triggerObservers) { + // This lets sugars force a recomputation, handy for very simple + // implementations of eg max. + if (newValue === cacheGet(this.cache, this.propertyName)) { + return; + } - If you try to set a property on a destroyed object, an exception will be - raised. + if (triggerObservers) { + propertyWillChange(this.context, this.propertyName); + } - Note that destruction is scheduled for the end of the run loop and does not - happen immediately. It will set an isDestroying flag immediately. + if (newValue === undefined) { + cacheRemove(this.cache, this.propertyName); + } else { + cacheSet(this.cache, this.propertyName, newValue); + } - @method destroy - @return {Ember.Object} receiver - */ - destroy: function() { - if (this.isDestroying) { return; } - this.isDestroying = true; + if (triggerObservers) { + propertyDidChange(this.context, this.propertyName); + } + } + }; - schedule('actions', this, this.willDestroy); - schedule('destroy', this, this._scheduledDestroy); - return this; - }, + /** + A computed property whose dependent keys are arrays and which is updated with + "one at a time" semantics. - /** - Override to implement teardown. + @class ReduceComputedProperty + @namespace Ember + @extends Ember.ComputedProperty + @constructor + */ - @method willDestroy - */ - willDestroy: K, + __exports__.ReduceComputedProperty = ReduceComputedProperty; + // TODO: default export - /** - Invoked by the run loop to actually destroy the object. This is - scheduled for execution by the `destroy` method. + function ReduceComputedProperty(options) { + var cp = this; - @private - @method _scheduledDestroy - */ - _scheduledDestroy: function() { - if (this.isDestroyed) { return; } - destroy(this); - this.isDestroyed = true; - }, + this.options = options; + this._dependentKeys = null; + this._cacheable = true; + // A map of dependentKey -> [itemProperty, ...] that tracks what properties of + // items in the array we must track to update this property. + this._itemPropertyKeys = {}; + this._previousItemPropertyKeys = {}; - bind: function(to, from) { - if (!(from instanceof Binding)) { from = Binding.from(from); } - from.to(to).connect(this); - return from; - }, + this.readOnly(); - /** - Returns a string representation which attempts to provide more information - than Javascript's `toString` typically does, in a generic way for all Ember - objects. + this.recomputeOnce = function(propertyName) { + // What we really want to do is coalesce by . + // We need a form of `scheduleOnce` that accepts an arbitrary token to + // coalesce by, in addition to the target and method. + run.once(this, recompute, propertyName); + }; - ```javascript - App.Person = Em.Object.extend() - person = App.Person.create() - person.toString() //=> "" - ``` + var recompute = function(propertyName) { + var meta = cp._instanceMeta(this, propertyName); + var callbacks = cp._callbacks(); - If the object's class is not defined on an Ember namespace, it will - indicate it is a subclass of the registered superclass: + reset.call(this, cp, propertyName); - ```javascript - Student = App.Person.extend() - student = Student.create() - student.toString() //=> "<(subclass of App.Person):ember1025>" - ``` + meta.dependentArraysObserver.suspendArrayObservers(function () { + forEach(cp._dependentKeys, function (dependentKey) { + Ember.assert( + 'dependent array ' + dependentKey + ' must be an `Ember.Array`. ' + + 'If you are not extending arrays, you will need to wrap native arrays with `Ember.A`', + !(isArray(get(this, dependentKey)) && !EmberArray.detect(get(this, dependentKey)))); - If the method `toStringExtension` is defined, its return value will be - included in the output. + if (!partiallyRecomputeFor(this, dependentKey)) { return; } - ```javascript - App.Teacher = App.Person.extend({ - toStringExtension: function() { - return this.get('fullName'); - } - }); - teacher = App.Teacher.create() - teacher.toString(); //=> "" - ``` + var dependentArray = get(this, dependentKey); + var previousDependentArray = meta.dependentArrays[dependentKey]; - @method toString - @return {String} string representation - */ - toString: function toString() { - var hasToStringExtension = typeof this.toStringExtension === 'function', - extension = hasToStringExtension ? ":" + this.toStringExtension() : ''; - var ret = '<'+this.constructor.toString()+':'+guidFor(this)+extension+'>'; - this.toString = makeToString(ret); - return ret; - } - }); + if (dependentArray === previousDependentArray) { + // The array may be the same, but our item property keys may have + // changed, so we set them up again. We can't easily tell if they've + // changed: the array may be the same object, but with different + // contents. + if (cp._previousItemPropertyKeys[dependentKey]) { + delete cp._previousItemPropertyKeys[dependentKey]; + meta.dependentArraysObserver.setupPropertyObservers(dependentKey, cp._itemPropertyKeys[dependentKey]); + } + } else { + meta.dependentArrays[dependentKey] = dependentArray; - CoreObject.PrototypeMixin.ownerConstructor = CoreObject; + if (previousDependentArray) { + meta.dependentArraysObserver.teardownObservers(previousDependentArray, dependentKey); + } - function makeToString(ret) { - return function() { return ret; }; - } + if (dependentArray) { + meta.dependentArraysObserver.setupObservers(dependentArray, dependentKey); + } + } + }, this); + }, this); - if (Ember.config.overridePrototypeMixin) { - Ember.config.overridePrototypeMixin(CoreObject.PrototypeMixin); - } + forEach(cp._dependentKeys, function(dependentKey) { + if (!partiallyRecomputeFor(this, dependentKey)) { return; } - CoreObject.__super__ = null; + var dependentArray = get(this, dependentKey); - var ClassMixin = Mixin.create({ + if (dependentArray) { + addItems.call(this, dependentArray, callbacks, cp, propertyName, meta); + } + }, this); + }; - ClassMixin: required(), - PrototypeMixin: required(), + this.func = function (propertyName) { + Ember.assert('Computed reduce values require at least one dependent key', cp._dependentKeys); - isClass: true, + recompute.call(this, propertyName); - isMethod: false, + return cp._instanceMeta(this, propertyName).getValue(); + }; + } - /** - Creates a new subclass. + ReduceComputedProperty.prototype = o_create(ComputedProperty.prototype); - ```javascript - App.Person = Ember.Object.extend({ - say: function(thing) { - alert(thing); - } - }); - ``` + function defaultCallback(computedValue) { + return computedValue; + } - This defines a new subclass of Ember.Object: `App.Person`. It contains one method: `say()`. + ReduceComputedProperty.prototype._callbacks = function () { + if (!this.callbacks) { + var options = this.options; - 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: + this.callbacks = { + removedItem: options.removedItem || defaultCallback, + addedItem: options.addedItem || defaultCallback, + flushedChanges: options.flushedChanges || defaultCallback + }; + } - ```javascript - App.PersonView = Ember.View.extend({ - tagName: 'li', - classNameBindings: ['isAdministrator'] - }); - ``` + return this.callbacks; + }; - When defining a subclass, you can override methods but still access the implementation of your parent class by calling the special `_super()` method: + ReduceComputedProperty.prototype._hasInstanceMeta = function (context, propertyName) { + return !!metaFor(context).cacheMeta[propertyName]; + }; - ```javascript - App.Person = Ember.Object.extend({ - say: function(thing) { - var name = this.get('name'); - alert(name + ' says: ' + thing); - } - }); + ReduceComputedProperty.prototype._instanceMeta = function (context, propertyName) { + var cacheMeta = metaFor(context).cacheMeta; + var meta = cacheMeta[propertyName]; - App.Soldier = App.Person.extend({ - say: function(thing) { - this._super(thing + ", sir!"); - }, - march: function(numberOfHours) { - alert(this.get('name') + ' marches for ' + numberOfHours + ' hours.') - } - }); + if (!meta) { + meta = cacheMeta[propertyName] = new ReduceComputedPropertyInstanceMeta(context, propertyName, this.initialValue()); + meta.dependentArraysObserver = new DependentArraysObserver(this._callbacks(), this, meta, context, propertyName, meta.sugarMeta); + } - var yehuda = App.Soldier.create({ - name: "Yehuda Katz" - }); + return meta; + }; - yehuda.say("Yes"); // alerts "Yehuda Katz says: Yes, sir!" - ``` + ReduceComputedProperty.prototype.initialValue = function () { + if (typeof this.options.initialValue === 'function') { + return this.options.initialValue(); + } + else { + return this.options.initialValue; + } + }; - 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. + ReduceComputedProperty.prototype.resetValue = function (value) { + return this.initialValue(); + }; - You can also pass `Mixin` classes to add additional properties to the subclass. + ReduceComputedProperty.prototype.itemPropertyKey = function (dependentArrayKey, itemPropertyKey) { + this._itemPropertyKeys[dependentArrayKey] = this._itemPropertyKeys[dependentArrayKey] || []; + this._itemPropertyKeys[dependentArrayKey].push(itemPropertyKey); + }; - ```javascript - App.Person = Ember.Object.extend({ - say: function(thing) { - alert(this.get('name') + ' says: ' + thing); - } - }); + ReduceComputedProperty.prototype.clearItemPropertyKeys = function (dependentArrayKey) { + if (this._itemPropertyKeys[dependentArrayKey]) { + this._previousItemPropertyKeys[dependentArrayKey] = this._itemPropertyKeys[dependentArrayKey]; + this._itemPropertyKeys[dependentArrayKey] = []; + } + }; - App.SingingMixin = Mixin.create({ - sing: function(thing){ - alert(this.get('name') + ' sings: la la la ' + thing); - } - }); + ReduceComputedProperty.prototype.property = function () { + var cp = this; + var args = a_slice.call(arguments); + var propertyArgs = {}; + var match, dependentArrayKey; - App.BroadwayStar = App.Person.extend(App.SingingMixin, { - dance: function() { - alert(this.get('name') + ' dances: tap tap tap tap '); - } - }); - ``` + forEach(args, function (dependentKey) { + if (doubleEachPropertyPattern.test(dependentKey)) { + throw new EmberError('Nested @each properties not supported: ' + dependentKey); + } else if (match = eachPropertyPattern.exec(dependentKey)) { + dependentArrayKey = match[1]; - The `App.BroadwayStar` class contains three methods: `say()`, `sing()`, and `dance()`. + var itemPropertyKeyPattern = match[2]; + var addItemPropertyKey = function (itemPropertyKey) { + cp.itemPropertyKey(dependentArrayKey, itemPropertyKey); + }; - @method extend - @static + expandProperties(itemPropertyKeyPattern, addItemPropertyKey); + propertyArgs[guidFor(dependentArrayKey)] = dependentArrayKey; + } else { + propertyArgs[guidFor(dependentKey)] = dependentKey; + } + }); - @param {Mixin} [mixins]* One or more Mixin classes - @param {Object} [arguments]* Object containing values to use within the new class - */ - extend: function() { - var Class = makeCtor(), proto; - Class.ClassMixin = Mixin.create(this.ClassMixin); - Class.PrototypeMixin = Mixin.create(this.PrototypeMixin); + var propertyArgsToArray = []; + for (var guid in propertyArgs) { + propertyArgsToArray.push(propertyArgs[guid]); + } - Class.ClassMixin.ownerConstructor = Class; - Class.PrototypeMixin.ownerConstructor = Class; + return ComputedProperty.prototype.property.apply(this, propertyArgsToArray); + }; - reopen.apply(Class.PrototypeMixin, arguments); + /** + Creates a computed property which operates on dependent arrays and + is updated with "one at a time" semantics. When items are added or + removed from the dependent array(s) a reduce computed only operates + on the change instead of re-evaluating the entire array. - Class.superclass = this; - Class.__super__ = this.prototype; + If there are more than one arguments the first arguments are + considered to be dependent property keys. The last argument is + required to be an options object. The options object can have the + following four properties: - proto = Class.prototype = o_create(this.prototype); - proto.constructor = Class; - generateGuid(proto); - meta(proto).proto = proto; // this will disable observers on prototype + `initialValue` - A value or function that will be used as the initial + value for the computed. If this property is a function the result of calling + the function will be used as the initial value. This property is required. - Class.ClassMixin.apply(Class); - return Class; - }, + `initialize` - An optional initialize function. Typically this will be used + to set up state on the instanceMeta object. - /** - Equivalent to doing `extend(arguments).create()`. - If possible use the normal `create` method instead. + `removedItem` - A function that is called each time an element is removed + from the array. - @method createWithMixins - @static - @param [arguments]* - */ - createWithMixins: function() { - var C = this; - if (arguments.length>0) { this._initMixins(arguments); } - return new C(); - }, + `addedItem` - A function that is called each time an element is added to + the array. - /** - Creates an instance of a class. Accepts either no arguments, or an object - containing values to initialize the newly instantiated object with. - ```javascript - App.Person = Ember.Object.extend({ - helloWorld: function() { - alert("Hi, my name is " + this.get('name')); - } - }); + The `initialize` function has the following signature: - var tom = App.Person.create({ - name: 'Tom Dale' - }); + ```javascript + function(initialValue, changeMeta, instanceMeta) + ``` - tom.helloWorld(); // alerts "Hi, my name is Tom Dale". - ``` + `initialValue` - The value of the `initialValue` property from the + options object. - `create` will call the `init` function if defined during - `Ember.AnyObject.extend` + `changeMeta` - An object which contains meta information about the + computed. It contains the following properties: - If no arguments are passed to `create`, it will not set values to the new - instance during initialization: + - `property` the computed property + - `propertyName` the name of the property on the object - ```javascript - var noName = App.Person.create(); - noName.helloWorld(); // alerts undefined - ``` + `instanceMeta` - An object that can be used to store meta + information needed for calculating your computed. For example a + unique computed might use this to store the number of times a given + element is found in the dependent array. - 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. - @method create - @static - @param [arguments]* - */ - create: function() { - var C = this; - if (arguments.length>0) { this._initProperties(arguments); } - return new C(); - }, + The `removedItem` and `addedItem` functions both have the following signature: - /** - Augments a constructor's prototype with additional - properties and functions: + ```javascript + function(accumulatedValue, item, changeMeta, instanceMeta) + ``` - ```javascript - MyObject = Ember.Object.extend({ - name: 'an object' - }); + `accumulatedValue` - The value returned from the last time + `removedItem` or `addedItem` was called or `initialValue`. - o = MyObject.create(); - o.get('name'); // 'an object' + `item` - the element added or removed from the array - MyObject.reopen({ - say: function(msg){ - console.log(msg); - } - }) + `changeMeta` - An object which contains meta information about the + change. It contains the following properties: - o2 = MyObject.create(); - o2.say("hello"); // logs "hello" + - `property` the computed property + - `propertyName` the name of the property on the object + - `index` the index of the added or removed item + - `item` the added or removed item: this is exactly the same as + the second arg + - `arrayChanged` the array that triggered the change. Can be + useful when depending on multiple arrays. - o.say("goodbye"); // logs "goodbye" - ``` + For property changes triggered on an item property change (when + depKey is something like `someArray.@each.someProperty`), + `changeMeta` will also contain the following property: - To add functions and properties to the constructor itself, - see `reopenClass` + - `previousValues` an object whose keys are the properties that changed on + the item, and whose values are the item's previous values. - @method reopen - */ - reopen: function() { - this.willReopen(); - apply(this.PrototypeMixin, reopen, arguments); - return this; - }, + `previousValues` is important Ember coalesces item property changes via + Ember.run.once. This means that by the time removedItem gets called, item has + the new values, but you may need the previous value (eg for sorting & + filtering). - /** - Augments a constructor's own properties and functions: + `instanceMeta` - An object that can be used to store meta + information needed for calculating your computed. For example a + unique computed might use this to store the number of times a given + element is found in the dependent array. - ```javascript - MyObject = Ember.Object.extend({ - name: 'an object' - }); + The `removedItem` and `addedItem` functions should return the accumulated + value. It is acceptable to not return anything (ie return undefined) + to invalidate the computation. This is generally not a good idea for + arrayComputed but it's used in eg max and min. - MyObject.reopenClass({ - canBuild: false - }); + Note that observers will be fired if either of these functions return a value + that differs from the accumulated value. When returning an object that + mutates in response to array changes, for example an array that maps + everything from some other array (see `Ember.computed.map`), it is usually + important that the *same* array be returned to avoid accidentally triggering observers. - MyObject.canBuild; // false - o = MyObject.create(); - ``` + Example - 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 + Ember.computed.max = function(dependentKey) { + return Ember.reduceComputed(dependentKey, { + initialValue: -Infinity, - ```javascript - App.Person = Ember.Object.extend({ - name : "", - sayHello : function(){ - alert("Hello. My name is " + this.get('name')); - } - }); + addedItem: function(accumulatedValue, item, changeMeta, instanceMeta) { + return Math.max(accumulatedValue, item); + }, - App.Person.reopenClass({ - species : "Homo sapiens", - createPerson: function(newPersonsName){ - return App.Person.create({ - name:newPersonsName - }); + removedItem: function(accumulatedValue, item, changeMeta, instanceMeta) { + if (item < accumulatedValue) { + return accumulatedValue; + } } }); + }; + ``` - var tom = App.Person.create({ - name : "Tom Dale" - }); - var yehuda = App.Person.createPerson("Yehuda Katz"); - - tom.sayHello(); // "Hello. My name is Tom Dale" - yehuda.sayHello(); // "Hello. My name is Yehuda Katz" - alert(App.Person.species); // "Homo sapiens" - ``` - - Note that `species` and `createPerson` are *not* valid on the `tom` and `yehuda` - variables. They are only valid on `App.Person`. + Dependent keys may refer to `@this` to observe changes to the object itself, + which must be array-like, rather than a property of the object. This is + mostly useful for array proxies, to ensure objects are retrieved via + `objectAtContent`. This is how you could sort items by properties defined on an item controller. - To add functions and properties to instances of - a constructor by extending the constructor's prototype - see `reopen` + Example - @method reopenClass - */ - reopenClass: function() { - apply(this.ClassMixin, reopen, arguments); - applyMixin(this, arguments, false); - return this; - }, + ```javascript + App.PeopleController = Ember.ArrayController.extend({ + itemController: 'person', - detect: function(obj) { - if ('function' !== typeof obj) { return false; } - while(obj) { - if (obj===this) { return true; } - obj = obj.superclass; - } - return false; - }, + sortedPeople: Ember.computed.sort('@this.@each.reversedName', function(personA, personB) { + // `reversedName` isn't defined on Person, but we have access to it via + // the item controller App.PersonController. If we'd used + // `content.@each.reversedName` above, we would be getting the objects + // directly and not have access to `reversedName`. + // + var reversedNameA = get(personA, 'reversedName'); + var reversedNameB = get(personB, 'reversedName'); - detectInstance: function(obj) { - return obj instanceof this; - }, + return Ember.compare(reversedNameA, reversedNameB); + }) + }); - /** - 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. + App.PersonController = Ember.ObjectController.extend({ + reversedName: function() { + return reverse(get(this, 'name')); + }.property('name') + }); + ``` - You can pass a hash of these values to a computed property like this: + Dependent keys whose values are not arrays are treated as regular + dependencies: when they change, the computed property is completely + recalculated. It is sometimes useful to have dependent arrays with similar + semantics. Dependent keys which end in `.[]` do not use "one at a time" + semantics. When an item is added or removed from such a dependency, the + computed property is completely recomputed. - ```javascript - person: function() { - var personId = this.get('personId'); - return App.Person.create({ id: personId }); - }.property().meta({ type: App.Person }) - ``` + When the computed property is completely recomputed, the `accumulatedValue` + is discarded, it starts with `initialValue` again, and each item is passed + to `addedItem` in turn. - Once you've done this, you can retrieve the values saved to the computed - property from your class like this: + Example - ```javascript - MyClass.metaForProperty('person'); - ``` + ```javascript + Ember.Object.extend({ + // When `string` is changed, `computed` is completely recomputed. + string: 'a string', - This will return the original hash that was passed to `meta()`. + // When an item is added to `array`, `addedItem` is called. + array: [], - @method metaForProperty - @param key {String} property name - */ - metaForProperty: function(key) { - var meta = this.proto()[META_KEY], - desc = meta && meta.descs[key]; + // When an item is added to `anotherArray`, `computed` is completely + // recomputed. + anotherArray: [], - Ember.assert("metaForProperty() could not find a computed property with key '"+key+"'.", !!desc && desc instanceof ComputedProperty); - return desc._meta || {}; - }, + computed: Ember.reduceComputed('string', 'array', 'anotherArray.[]', { + addedItem: addedItemCallback, + removedItem: removedItemCallback + }) + }); + ``` - /** - Iterate over each computed property for the class, passing its name - and any associated metadata (see `metaForProperty`) to the callback. + @method reduceComputed + @for Ember + @param {String} [dependentKeys*] + @param {Object} options + @return {Ember.ComputedProperty} + */ + function reduceComputed(options) { + var args; - @method eachComputedProperty - @param {Function} callback - @param {Object} binding - */ - eachComputedProperty: function(callback, binding) { - var proto = this.proto(), - descs = meta(proto).descs, - empty = {}, - property; + if (arguments.length > 1) { + args = a_slice.call(arguments, 0, -1); + options = a_slice.call(arguments, -1)[0]; + } - for (var name in descs) { - property = descs[name]; + if (typeof options !== 'object') { + throw new EmberError('Reduce Computed Property declared without an options hash'); + } - if (property instanceof ComputedProperty) { - callback.call(binding || this, name, property._meta || empty); - } - } + if (!('initialValue' in options)) { + throw new EmberError('Reduce Computed Property declared without an initial value'); } - }); + var cp = new ReduceComputedProperty(options); - ClassMixin.ownerConstructor = CoreObject; + if (args) { + cp.property.apply(cp, args); + } - if (Ember.config.overrideClassMixin) { - Ember.config.overrideClassMixin(ClassMixin); + return cp; } - CoreObject.ClassMixin = ClassMixin; - ClassMixin.apply(CoreObject); - - __exports__["default"] = CoreObject; - }); -define("ember-runtime/system/deferred", - ["ember-runtime/mixins/deferred","ember-metal/property_get","ember-runtime/system/object","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var DeferredMixin = __dependency1__["default"]; - var get = __dependency2__.get; - var EmberObject = __dependency3__["default"]; - - var Deferred = EmberObject.extend(DeferredMixin); - - Deferred.reopenClass({ - promise: function(callback, binding) { - var deferred = Deferred.create(); - callback.call(binding, deferred); - return deferred; - } - }); - - __exports__["default"] = Deferred; + __exports__.reduceComputed = reduceComputed; }); -define("ember-runtime/system/each_proxy", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","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__, __dependency13__, __exports__) { +enifed("ember-runtime/computed/reduce_computed_macros", + ["ember-metal/core","ember-metal/property_get","ember-metal/utils","ember-metal/error","ember-metal/enumerable_utils","ember-metal/run_loop","ember-metal/observer","ember-runtime/computed/array_computed","ember-runtime/computed/reduce_computed","ember-runtime/system/subarray","ember-metal/keys","ember-runtime/compare","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __exports__) { "use strict"; /** @module ember @@ -20287,26476 +26910,31781 @@ define("ember-runtime/system/each_proxy", var Ember = __dependency1__["default"]; // Ember.assert - var get = __dependency2__.get; - var set = __dependency3__.set; - var guidFor = __dependency4__.guidFor; - var EnumerableUtils = __dependency5__["default"]; - var indexOf = __dependency6__.indexOf; - var EmberArray = __dependency7__["default"]; - // ES6TODO: WAT? Circular dep? - var EmberObject = __dependency8__["default"]; - var computed = __dependency9__.computed; - var addObserver = __dependency10__.addObserver; - var addBeforeObserver = __dependency10__.addBeforeObserver; - var removeBeforeObserver = __dependency10__.removeBeforeObserver; - var removeObserver = __dependency10__.removeObserver; - var typeOf = __dependency4__.typeOf; - var watchedEvents = __dependency11__.watchedEvents; - var defineProperty = __dependency12__.defineProperty; - var beginPropertyChanges = __dependency13__.beginPropertyChanges; - var propertyDidChange = __dependency13__.propertyDidChange; - var propertyWillChange = __dependency13__.propertyWillChange; - var endPropertyChanges = __dependency13__.endPropertyChanges; - var changeProperties = __dependency13__.changeProperties; + var isArray = __dependency3__.isArray; + var guidFor = __dependency3__.guidFor; + var EmberError = __dependency4__["default"]; + var forEach = __dependency5__.forEach; + var run = __dependency6__["default"]; + var addObserver = __dependency7__.addObserver; + var arrayComputed = __dependency8__.arrayComputed; + var reduceComputed = __dependency9__.reduceComputed; + var SubArray = __dependency10__["default"]; + var keys = __dependency11__["default"]; + var compare = __dependency12__["default"]; - var forEach = EnumerableUtils.forEach; + var a_slice = [].slice; - var EachArray = EmberObject.extend(EmberArray, { + /** + A computed property that returns the sum of the value + in the dependent array. - init: function(content, keyName, owner) { - this._super(); - this._keyName = keyName; - this._owner = owner; - this._content = content; - }, + @method computed.sum + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computes the sum of all values in the dependentKey's array + @since 1.4.0 + */ - objectAt: function(idx) { - var item = this._content.objectAt(idx); - return item && get(item, this._keyName); - }, + function sum(dependentKey){ + return reduceComputed(dependentKey, { + initialValue: 0, - length: computed(function() { - var content = this._content; - return content ? get(content, 'length') : 0; - }) + addedItem: function(accumulatedValue, item, changeMeta, instanceMeta){ + return accumulatedValue + item; + }, - }); + removedItem: function(accumulatedValue, item, changeMeta, instanceMeta){ + return accumulatedValue - item; + } + }); + } - var IS_OBSERVER = /^.+:(before|change)$/; + __exports__.sum = sum;/** + A computed property that calculates the maximum value in the + dependent array. This will return `-Infinity` when the dependent + array is empty. + + ```javascript + var Person = Ember.Object.extend({ + childAges: Ember.computed.mapBy('children', 'age'), + maxChildAge: Ember.computed.max('childAges') + }); + + var lordByron = Person.create({ children: [] }); + + lordByron.get('maxChildAge'); // -Infinity + lordByron.get('children').pushObject({ + name: 'Augusta Ada Byron', age: 7 + }); + lordByron.get('maxChildAge'); // 7 + lordByron.get('children').pushObjects([{ + name: 'Allegra Byron', + age: 5 + }, { + name: 'Elizabeth Medora Leigh', + age: 8 + }]); + lordByron.get('maxChildAge'); // 8 + ``` + + @method computed.max + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computes the largest value in the dependentKey's array + */ + function max(dependentKey) { + return reduceComputed(dependentKey, { + initialValue: -Infinity, + + addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { + return Math.max(accumulatedValue, item); + }, + + removedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { + if (item < accumulatedValue) { + return accumulatedValue; + } + } + }); + } + + __exports__.max = max;/** + A computed property that calculates the minimum value in the + dependent array. This will return `Infinity` when the dependent + array is empty. + + ```javascript + var Person = Ember.Object.extend({ + childAges: Ember.computed.mapBy('children', 'age'), + minChildAge: Ember.computed.min('childAges') + }); + + var lordByron = Person.create({ children: [] }); + + lordByron.get('minChildAge'); // Infinity + lordByron.get('children').pushObject({ + name: 'Augusta Ada Byron', age: 7 + }); + lordByron.get('minChildAge'); // 7 + lordByron.get('children').pushObjects([{ + name: 'Allegra Byron', + age: 5 + }, { + name: 'Elizabeth Medora Leigh', + age: 8 + }]); + lordByron.get('minChildAge'); // 5 + ``` - function addObserverForContentKey(content, keyName, proxy, idx, loc) { - var objects = proxy._objects, guid; - if (!objects) objects = proxy._objects = {}; + @method computed.min + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computes the smallest value in the dependentKey's array + */ + function min(dependentKey) { + return reduceComputed(dependentKey, { + initialValue: Infinity, - 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'); + addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { + return Math.min(accumulatedValue, item); + }, - // 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); + removedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { + if (item > accumulatedValue) { + return accumulatedValue; + } } - } + }); } - function removeObserverForContentKey(content, keyName, proxy, idx, loc) { - var objects = proxy._objects; - if (!objects) objects = proxy._objects = {}; - var indicies, guid; + __exports__.min = min;/** + Returns an array mapped via the callback - while(--loc>=idx) { - var item = content.objectAt(loc); - if (item) { - removeBeforeObserver(item, keyName, proxy, 'contentKeyWillChange'); - removeObserver(item, keyName, proxy, 'contentKeyDidChange'); + The callback method you provide should have the following signature. + `item` is the current item in the iteration. + `index` is the integer index of the current item in the iteration. - guid = guidFor(item); - indicies = objects[guid]; - indicies[indexOf.call(indicies, loc)] = null; - } - } - } + ```javascript + function(item, index); + ``` - /** - 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. + Example - @private - @class EachProxy - @namespace Ember - @extends Ember.Object - */ - var EachProxy = EmberObject.extend({ + ```javascript + var Hamster = Ember.Object.extend({ + excitingChores: Ember.computed.map('chores', function(chore, index) { + return chore.toUpperCase() + '!'; + }) + }); - init: function(content) { - this._super(); - this._content = content; - content.addArrayObserver(this); + var hamster = Hamster.create({ + chores: ['clean', 'write more unit tests'] + }); - // in case someone is already observing some keys make sure they are - // added - forEach(watchedEvents(this), function(eventName) { - this.didAddListener(eventName); - }, this); - }, + hamster.get('excitingChores'); // ['CLEAN!', 'WRITE MORE UNIT TESTS!'] + ``` - /** - You can directly access mapped properties by simply requesting them. - The `unknownProperty` handler will generate an EachArray of each item. + @method computed.map + @for Ember + @param {String} dependentKey + @param {Function} callback + @return {Ember.ComputedProperty} an array mapped via the callback + */ + function map(dependentKey, callback) { + var options = { + addedItem: function(array, item, changeMeta, instanceMeta) { + var mapped = callback.call(this, item, changeMeta.index); + array.insertAt(changeMeta.index, mapped); + return array; + }, + removedItem: function(array, item, changeMeta, instanceMeta) { + array.removeAt(changeMeta.index, 1); + return array; + } + }; - @method unknownProperty - @param keyName {String} - @param value {*} - */ - unknownProperty: function(keyName, value) { - var ret; - ret = new EachArray(this._content, keyName, this); - defineProperty(this, keyName, null, ret); - this.beginObservingContentKey(keyName); - return ret; - }, + return arrayComputed(dependentKey, options); + } - // .......................................................... - // ARRAY CHANGES - // Invokes whenever the content array itself changes. + __exports__.map = map;/** + Returns an array mapped to the specified key. - arrayWillChange: function(content, idx, removedCnt, addedCnt) { - var keys = this._keys, key, lim; + ```javascript + var Person = Ember.Object.extend({ + childAges: Ember.computed.mapBy('children', 'age') + }); - lim = removedCnt>0 ? idx+removedCnt : -1; - beginPropertyChanges(this); + var lordByron = Person.create({ children: [] }); - for(key in keys) { - if (!keys.hasOwnProperty(key)) { continue; } + lordByron.get('childAges'); // [] + lordByron.get('children').pushObject({ name: 'Augusta Ada Byron', age: 7 }); + lordByron.get('childAges'); // [7] + lordByron.get('children').pushObjects([{ + name: 'Allegra Byron', + age: 5 + }, { + name: 'Elizabeth Medora Leigh', + age: 8 + }]); + lordByron.get('childAges'); // [7, 5, 8] + ``` - if (lim>0) { removeObserverForContentKey(content, key, this, idx, lim); } + @method computed.mapBy + @for Ember + @param {String} dependentKey + @param {String} propertyKey + @return {Ember.ComputedProperty} an array mapped to the specified key + */ + function mapBy (dependentKey, propertyKey) { + var callback = function(item) { return get(item, propertyKey); }; + return map(dependentKey + '.@each.' + propertyKey, callback); + } - propertyWillChange(this, key); - } + __exports__.mapBy = mapBy;/** + @method computed.mapProperty + @for Ember + @deprecated Use `Ember.computed.mapBy` instead + @param dependentKey + @param propertyKey + */ + var mapProperty = mapBy; + __exports__.mapProperty = mapProperty; + /** + Filters the array by the callback. - propertyWillChange(this._content, '@each'); - endPropertyChanges(this); - }, + The callback method you provide should have the following signature. + `item` is the current item in the iteration. + `index` is the integer index of the current item in the iteration. + `array` is the dependant array itself. - arrayDidChange: function(content, idx, removedCnt, addedCnt) { - var keys = this._keys, lim; + ```javascript + function(item, index, array); + ``` - lim = addedCnt>0 ? idx+addedCnt : -1; - changeProperties(function() { - for(var key in keys) { - if (!keys.hasOwnProperty(key)) { continue; } + ```javascript + var Hamster = Ember.Object.extend({ + remainingChores: Ember.computed.filter('chores', function(chore, index, array) { + return !chore.done; + }) + }); - if (lim>0) { addObserverForContentKey(content, key, this, idx, lim); } + var hamster = Hamster.create({ + chores: [ + { name: 'cook', done: true }, + { name: 'clean', done: true }, + { name: 'write more unit tests', done: false } + ] + }); - propertyDidChange(this, key); - } + hamster.get('remainingChores'); // [{name: 'write more unit tests', done: false}] + ``` - propertyDidChange(this._content, '@each'); - }, this); - }, + @method computed.filter + @for Ember + @param {String} dependentKey + @param {Function} callback + @return {Ember.ComputedProperty} the filtered array + */ + function filter(dependentKey, callback) { + var options = { + initialize: function (array, changeMeta, instanceMeta) { + instanceMeta.filteredArrayIndexes = new SubArray(); + }, - // .......................................................... - // LISTEN FOR NEW OBSERVERS AND OTHER EVENT LISTENERS - // Start monitoring keys based on who is listening... + addedItem: function (array, item, changeMeta, instanceMeta) { + var match = !!callback.call(this, item, changeMeta.index, changeMeta.arrayChanged); + var filterIndex = instanceMeta.filteredArrayIndexes.addItem(changeMeta.index, match); - didAddListener: function(eventName) { - if (IS_OBSERVER.test(eventName)) { - this.beginObservingContentKey(eventName.slice(0, -7)); - } - }, + if (match) { + array.insertAt(filterIndex, item); + } - didRemoveListener: function(eventName) { - if (IS_OBSERVER.test(eventName)) { - this.stopObservingContentKey(eventName.slice(0, -7)); - } - }, + return array; + }, - // .......................................................... - // CONTENT KEY OBSERVING - // Actual watch keys on the source content. + removedItem: function(array, item, changeMeta, instanceMeta) { + var filterIndex = instanceMeta.filteredArrayIndexes.removeItem(changeMeta.index); - beginObservingContentKey: function(keyName) { - var keys = this._keys; - if (!keys) keys = this._keys = {}; - if (!keys[keyName]) { - keys[keyName] = 1; - var content = this._content, - len = get(content, 'length'); - addObserverForContentKey(content, keyName, this, 0, len); - } else { - keys[keyName]++; - } - }, + if (filterIndex > -1) { + array.removeAt(filterIndex); + } - stopObservingContentKey: function(keyName) { - var keys = this._keys; - if (keys && (keys[keyName]>0) && (--keys[keyName]<=0)) { - var content = this._content, - len = get(content, 'length'); - removeObserverForContentKey(content, keyName, this, 0, len); + return array; } - }, + }; - contentKeyWillChange: function(obj, keyName) { - propertyWillChange(this, keyName); - }, + return arrayComputed(dependentKey, options); + } - contentKeyDidChange: function(obj, keyName) { - propertyDidChange(this, keyName); - } + __exports__.filter = filter;/** + Filters the array by the property and value - }); + ```javascript + var Hamster = Ember.Object.extend({ + remainingChores: Ember.computed.filterBy('chores', 'done', false) + }); - __exports__.EachArray = EachArray; - __exports__.EachProxy = EachProxy; - }); -define("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 */ + var hamster = Hamster.create({ + chores: [ + { name: 'cook', done: true }, + { name: 'clean', done: true }, + { name: 'write more unit tests', done: false } + ] + }); - var Ember = __dependency1__["default"]; - // Ember.ENV.EMBER_LOAD_HOOKS - var forEach = __dependency2__.forEach; - // make sure Ember.A is setup. + hamster.get('remainingChores'); // [{ name: 'write more unit tests', done: false }] + ``` - /** - @module ember - @submodule ember-runtime + @method computed.filterBy + @for Ember + @param {String} dependentKey + @param {String} propertyKey + @param {*} value + @return {Ember.ComputedProperty} the filtered array */ + function filterBy (dependentKey, propertyKey, value) { + var callback; - var loadHooks = Ember.ENV.EMBER_LOAD_HOOKS || {}; - var loaded = {}; + if (arguments.length === 2) { + callback = function(item) { + return get(item, propertyKey); + }; + } else { + callback = function(item) { + return get(item, propertyKey) === value; + }; + } + + return filter(dependentKey + '.@each.' + propertyKey, callback); + } + __exports__.filterBy = filterBy;/** + @method computed.filterProperty + @for Ember + @param dependentKey + @param propertyKey + @param value + @deprecated Use `Ember.computed.filterBy` instead + */ + var filterProperty = filterBy; + __exports__.filterProperty = filterProperty; /** - Detects when a specific package of Ember (e.g. 'Ember.Handlebars') - has fully loaded and is available for extension. + A computed property which returns a new array with all the unique + elements from one or more dependent arrays. - The provided `callback` will be called with the `name` passed - resolved from a string into the object: + Example - ``` javascript - Ember.onLoad('Ember.Handlebars' function(hbars) { - hbars.registerHelper(...); + ```javascript + var Hamster = Ember.Object.extend({ + uniqueFruits: Ember.computed.uniq('fruits') + }); + + var hamster = Hamster.create({ + fruits: [ + 'banana', + 'grape', + 'kale', + 'banana' + ] }); + + hamster.get('uniqueFruits'); // ['banana', 'grape', 'kale'] ``` - @method onLoad + @method computed.uniq @for Ember - @param name {String} name of hook - @param callback {Function} callback to be called + @param {String} propertyKey* + @return {Ember.ComputedProperty} computes a new array with all the + unique elements from the dependent array */ - function onLoad(name, callback) { - var object; - - loadHooks[name] = loadHooks[name] || Ember.A(); - loadHooks[name].pushObject(callback); + function uniq() { + var args = a_slice.call(arguments); - if (object = loaded[name]) { - callback(object); - } - }; + args.push({ + initialize: function(array, changeMeta, instanceMeta) { + instanceMeta.itemCounts = {}; + }, - /** - Called when an Ember.js package (e.g Ember.Handlebars) has finished - loading. Triggers any callbacks registered for this event. + addedItem: function(array, item, changeMeta, instanceMeta) { + var guid = guidFor(item); - @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; + if (!instanceMeta.itemCounts[guid]) { + instanceMeta.itemCounts[guid] = 1; + array.pushObject(item); + } else { + ++instanceMeta.itemCounts[guid]; + } + return array; + }, - if (typeof window === 'object' && typeof window.dispatchEvent === 'function' && typeof CustomEvent === "function") { - var event = new CustomEvent(name, {detail: object, name: name}); - window.dispatchEvent(event); - } + removedItem: function(array, item, _, instanceMeta) { + var guid = guidFor(item); + var itemCounts = instanceMeta.itemCounts; - if (loadHooks[name]) { - forEach.call(loadHooks[name], function(callback) { - callback(object); - }); - } - }; + if (--itemCounts[guid] === 0) { + array.removeObject(item); + } - __exports__.onLoad = onLoad; - __exports__.runLoadHooks = runLoadHooks; - }); -define("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 - */ + return array; + } + }); - // 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; + return arrayComputed.apply(null, args); + } - var EmberObject = __dependency6__["default"]; + __exports__.uniq = uniq;/** + Alias for [Ember.computed.uniq](/api/#method_computed_uniq). + @method computed.union + @for Ember + @param {String} propertyKey* + @return {Ember.ComputedProperty} computes a new array with all the + unique elements from the dependent array + */ + var union = uniq; + __exports__.union = union; /** - 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. + A computed property which returns a new array with all the duplicated + elements from two or more dependent arrays. - # Example Usage + Example ```javascript - MyFramework = Ember.Namespace.create({ - VERSION: '1.0.0' + var obj = Ember.Object.createWithMixins({ + adaFriends: ['Charles Babbage', 'John Hobhouse', 'William King', 'Mary Somerville'], + charlesFriends: ['William King', 'Mary Somerville', 'Ada Lovelace', 'George Peacock'], + friendsInCommon: Ember.computed.intersect('adaFriends', 'charlesFriends') }); + + obj.get('friendsInCommon'); // ['William King', 'Mary Somerville'] ``` - @class Namespace - @namespace Ember - @extends Ember.Object + @method computed.intersect + @for Ember + @param {String} propertyKey* + @return {Ember.ComputedProperty} computes a new array with all the + duplicated elements from the dependent arrays */ - var Namespace = EmberObject.extend({ - isNamespace: true, + function intersect() { + var args = a_slice.call(arguments); - init: function() { - Namespace.NAMESPACES.push(this); - Namespace.PROCESSED = false; - }, + args.push({ + initialize: function (array, changeMeta, instanceMeta) { + instanceMeta.itemCounts = {}; + }, - toString: function() { - var name = get(this, 'name'); - if (name) { return name; } + addedItem: function(array, item, changeMeta, instanceMeta) { + var itemGuid = guidFor(item); + var dependentGuid = guidFor(changeMeta.arrayChanged); + var numberOfDependentArrays = changeMeta.property._dependentKeys.length; + var itemCounts = instanceMeta.itemCounts; - findNamespaces(); - return this[NAME_KEY]; - }, + if (!itemCounts[itemGuid]) { + itemCounts[itemGuid] = {}; + } - nameClasses: function() { - processNamespace([this.toString()], this, {}); - }, + if (itemCounts[itemGuid][dependentGuid] === undefined) { + itemCounts[itemGuid][dependentGuid] = 0; + } - destroy: function() { - var namespaces = Namespace.NAMESPACES, - toString = this.toString(); + if (++itemCounts[itemGuid][dependentGuid] === 1 && + numberOfDependentArrays === keys(itemCounts[itemGuid]).length) { + array.addObject(item); + } - if (toString) { - Ember.lookup[toString] = undefined; - delete Namespace.NAMESPACES_BY_ID[toString]; - } - namespaces.splice(indexOf.call(namespaces, this), 1); - this._super(); - } - }); + return array; + }, - Namespace.reopenClass({ - NAMESPACES: [Ember], - NAMESPACES_BY_ID: {}, - PROCESSED: false, - processAll: processAllNamespaces, - byName: function(name) { - if (!Ember.BOOTED) { - processAllNamespaces(); - } + removedItem: function(array, item, changeMeta, instanceMeta) { + var itemGuid = guidFor(item); + var dependentGuid = guidFor(changeMeta.arrayChanged); + var numberOfArraysItemAppearsIn; + var itemCounts = instanceMeta.itemCounts; - return NAMESPACES_BY_ID[name]; - } - }); + if (itemCounts[itemGuid][dependentGuid] === undefined) { + itemCounts[itemGuid][dependentGuid] = 0; + } + + if (--itemCounts[itemGuid][dependentGuid] === 0) { + delete itemCounts[itemGuid][dependentGuid]; + numberOfArraysItemAppearsIn = keys(itemCounts[itemGuid]).length; + + if (numberOfArraysItemAppearsIn === 0) { + delete itemCounts[itemGuid]; + } - var NAMESPACES_BY_ID = Namespace.NAMESPACES_BY_ID; + array.removeObject(item); + } - var hasOwnProp = ({}).hasOwnProperty; + return array; + } + }); - function processNamespace(paths, root, seen) { - var idx = paths.length; + return arrayComputed.apply(null, args); + } - NAMESPACES_BY_ID[paths.join('.')] = root; + __exports__.intersect = intersect;/** + A computed property which returns a new array with all the + properties from the first dependent array that are not in the second + dependent array. - // 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]; + Example - // 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; + ```javascript + var Hamster = Ember.Object.extend({ + likes: ['banana', 'grape', 'kale'], + wants: Ember.computed.setDiff('likes', 'fruits') + }); - // 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 hamster = Hamster.create({ + fruits: [ + 'grape', + 'kale', + ] + }); - // Support nested namespaces - } else if (obj && obj.isNamespace) { - // Skip aliased namespaces - if (seen[guidFor(obj)]) { continue; } - seen[guidFor(obj)] = true; + hamster.get('wants'); // ['banana'] + ``` - // Process the child namespace - processNamespace(paths, obj, seen); - } + @method computed.setDiff + @for Ember + @param {String} setAProperty + @param {String} setBProperty + @return {Ember.ComputedProperty} computes a new array with all the + items from the first dependent array that are not in the second + dependent array + */ + function setDiff(setAProperty, setBProperty) { + if (arguments.length !== 2) { + throw new EmberError('setDiff requires exactly two dependent arrays.'); } - paths.length = idx; // cut out last item - } - - var STARTS_WITH_UPPERCASE = /^[A-Z]/; - - function findNamespaces() { - var lookup = Ember.lookup, obj, isNamespace; + return arrayComputed(setAProperty, setBProperty, { + addedItem: function (array, item, changeMeta, instanceMeta) { + var setA = get(this, setAProperty); + var setB = get(this, setBProperty); - if (Namespace.PROCESSED) { return; } + if (changeMeta.arrayChanged === setA) { + if (!setB.contains(item)) { + array.addObject(item); + } + } else { + array.removeObject(item); + } - for (var prop in lookup) { - // Only process entities that start with uppercase A-Z - if (!STARTS_WITH_UPPERCASE.test(prop)) { continue; } + return array; + }, - // Unfortunately, some versions of IE don't support window.hasOwnProperty - if (lookup.hasOwnProperty && !lookup.hasOwnProperty(prop)) { continue; } + removedItem: function (array, item, changeMeta, instanceMeta) { + var setA = get(this, setAProperty); + var setB = get(this, setBProperty); - // 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. - try { - obj = lookup[prop]; - isNamespace = obj && obj.isNamespace; - } catch (e) { - continue; - } + if (changeMeta.arrayChanged === setB) { + if (setA.contains(item)) { + array.addObject(item); + } + } else { + array.removeObject(item); + } - if (isNamespace) { - obj[NAME_KEY] = prop; + return array; } - } + }); } - var NAME_KEY = Ember.NAME_KEY = GUID_KEY + '_name'; + __exports__.setDiff = setDiff;function binarySearch(array, item, low, high) { + var mid, midItem, res, guidMid, guidItem; - function superClassString(mixin) { - var superclass = mixin.superclass; - if (superclass) { - if (superclass[NAME_KEY]) { return superclass[NAME_KEY]; } - else { return superClassString(superclass); } - } else { - return; + if (arguments.length < 4) { + high = get(array, 'length'); } - } - function classToString() { - if (!Ember.BOOTED && !this[NAME_KEY]) { - processAllNamespaces(); + if (arguments.length < 3) { + low = 0; } - var ret; - - 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); + if (low === high) { + return low; } - return ret; - } + mid = low + Math.floor((high - low) / 2); + midItem = array.objectAt(mid); - function processAllNamespaces() { - var unprocessedNamespaces = !Namespace.PROCESSED, - unprocessedMixins = Ember.anyUnprocessedMixins; + guidMid = guidFor(midItem); + guidItem = guidFor(item); - if (unprocessedNamespaces) { - findNamespaces(); - Namespace.PROCESSED = true; + if (guidMid === guidItem) { + return mid; } - if (unprocessedNamespaces || unprocessedMixins) { - var namespaces = Namespace.NAMESPACES, namespace; - for (var i=0, l=namespaces.length; i 0) { + return this.binarySearch(array, item, low, mid); + } - __exports__["default"] = Namespace; - }); -define("ember-runtime/system/native_array", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/enumerable_utils","ember-metal/mixin","ember-runtime/mixins/array","ember-runtime/mixins/mutable_array","ember-runtime/mixins/observable","ember-runtime/mixins/copyable","ember-runtime/mixins/freezable","ember-runtime/copy","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-runtime - */ + return mid; + } - var Ember = __dependency1__["default"]; - // Ember.EXTEND_PROTOTYPES - var get = __dependency2__.get; - var set = __dependency3__.set; - var EnumerableUtils = __dependency4__["default"]; - var Mixin = __dependency5__.Mixin; - var EmberArray = __dependency6__["default"]; - var MutableArray = __dependency7__["default"]; - var Observable = __dependency8__["default"]; - var Copyable = __dependency9__["default"]; - var FROZEN_ERROR = __dependency10__.FROZEN_ERROR; - var copy = __dependency11__["default"]; + /** + A computed property which returns a new array with all the + properties from the first dependent array sorted based on a property + or sort function. - var replace = EnumerableUtils._replace, - forEach = EnumerableUtils.forEach; + The callback method you provide should have the following signature: - // Add Ember.Array to Array.prototype. Remove methods with native - // implementations and supply some more optimized versions of generic methods - // because they are so common. + ```javascript + function(itemA, itemB); + ``` - /** - The NativeArray mixin contains the properties needed to to make the native - Array support Ember.MutableArray and all of its dependent APIs. Unless you - have `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Array` set to - false, this will be applied automatically. Otherwise you can apply the mixin - at anytime by calling `Ember.NativeArray.activate`. + - `itemA` the first item to compare. + - `itemB` the second item to compare. - @class NativeArray - @namespace Ember - @uses Ember.MutableArray - @uses Ember.Observable - @uses Ember.Copyable - */ - var NativeArray = Mixin.create(MutableArray, Observable, Copyable, { + This function should return negative number (e.g. `-1`) when `itemA` should come before + `itemB`. It should return positive number (e.g. `1`) when `itemA` should come after + `itemB`. If the `itemA` and `itemB` are equal this function should return `0`. - // because length is a built-in property we need to know to just get the - // original property. - get: function(key) { - if (key==='length') return this.length; - else if ('number' === typeof key) return this[key]; - else return this._super(key); - }, + Therefore, if this function is comparing some numeric values, simple `itemA - itemB` or + `itemA.get( 'foo' ) - itemB.get( 'foo' )` can be used instead of series of `if`. - objectAt: function(idx) { - return this[idx]; - }, + Example - // primitive for array support. - replace: function(idx, amt, objects) { + ```javascript + var ToDoList = Ember.Object.extend({ + // using standard ascending sort + todosSorting: ['name'], + sortedTodos: Ember.computed.sort('todos', 'todosSorting'), - if (this.isFrozen) throw FROZEN_ERROR; + // using descending sort + todosSortingDesc: ['name:desc'], + sortedTodosDesc: Ember.computed.sort('todos', 'todosSortingDesc'), - // if we replaced exactly the same number of items, then pass only the - // replaced range. Otherwise, pass the full remaining array length - // since everything has shifted - var len = objects ? get(objects, 'length') : 0; - this.arrayContentWillChange(idx, amt, len); + // using a custom sort function + priorityTodos: Ember.computed.sort('todos', function(a, b){ + if (a.priority > b.priority) { + return 1; + } else if (a.priority < b.priority) { + return -1; + } - if (len === 0) { - this.splice(idx, amt); - } else { - replace(this, idx, amt, objects); - } + return 0; + }) + }); - this.arrayContentDidChange(idx, amt, len); - return this; - }, + var todoList = ToDoList.create({todos: [ + { name: 'Unit Test', priority: 2 }, + { name: 'Documentation', priority: 3 }, + { name: 'Release', priority: 1 } + ]}); - // If you ask for an unknown property, then try to collect the value - // from member items. - unknownProperty: function(key, value) { - var ret;// = this.reducedProperty(key, value) ; - if ((value !== undefined) && ret === undefined) { - ret = this[key] = value; - } - return ret ; - }, + todoList.get('sortedTodos'); // [{ name:'Documentation', priority:3 }, { name:'Release', priority:1 }, { name:'Unit Test', priority:2 }] + todoList.get('sortedTodosDesc'); // [{ name:'Unit Test', priority:2 }, { name:'Release', priority:1 }, { name:'Documentation', priority:3 }] + todoList.get('priorityTodos'); // [{ name:'Release', priority:1 }, { name:'Unit Test', priority:2 }, { name:'Documentation', priority:3 }] + ``` - // If browser did not implement indexOf natively, then override with - // specialized version - indexOf: function(object, startAt) { - var idx, len = this.length; + @method computed.sort + @for Ember + @param {String} dependentKey + @param {String or Function} sortDefinition a dependent key to an + array of sort properties (add `:desc` to the arrays sort properties to sort descending) or a function to use when sorting + @return {Ember.ComputedProperty} computes a new sorted array based + on the sort property array or callback function + */ + function sort(itemsKey, sortDefinition) { + Ember.assert('Ember.computed.sort requires two arguments: an array key to sort and ' + + 'either a sort properties key or sort function', arguments.length === 2); - if (startAt === undefined) startAt = 0; - else startAt = (startAt < 0) ? Math.ceil(startAt) : Math.floor(startAt); - if (startAt < 0) startAt += len; + if (typeof sortDefinition === 'function') { + return customSort(itemsKey, sortDefinition); + } else { + return propertySort(itemsKey, sortDefinition); + } + } - for(idx=startAt;idx=0;idx--) { - if (this[idx] === object) return idx ; + flushedChanges: function(array, instanceMeta) { + instanceMeta.insertWaiting(); } - return -1; - }, + }); + } - copy: function(deep) { - if (deep) { - return this.map(function(item) { return copy(item, true); }); - } + function propertySort(itemsKey, sortPropertiesKey) { + return arrayComputed(itemsKey, { + initialize: function (array, changeMeta, instanceMeta) { + function setupSortProperties() { + var sortPropertyDefinitions = get(this, sortPropertiesKey); + var sortProperties = instanceMeta.sortProperties = []; + var sortPropertyAscending = instanceMeta.sortPropertyAscending = {}; + var sortProperty, idx, asc; - return this.slice(); - } - }); + Ember.assert('Cannot sort: \'' + sortPropertiesKey + '\' is not an array.', + isArray(sortPropertyDefinitions)); - // Remove any methods implemented natively so we don't override them - var ignore = ['length']; - forEach(NativeArray.keys(), function(methodName) { - if (Array.prototype[methodName]) ignore.push(methodName); - }); + changeMeta.property.clearItemPropertyKeys(itemsKey); - if (ignore.length>0) { - NativeArray = NativeArray.without.apply(NativeArray, ignore); - } + forEach(sortPropertyDefinitions, function (sortPropertyDefinition) { + if ((idx = sortPropertyDefinition.indexOf(':')) !== -1) { + sortProperty = sortPropertyDefinition.substring(0, idx); + asc = sortPropertyDefinition.substring(idx+1).toLowerCase() !== 'desc'; + } else { + sortProperty = sortPropertyDefinition; + asc = true; + } - /** - 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`. + sortProperties.push(sortProperty); + sortPropertyAscending[sortProperty] = asc; + changeMeta.property.itemPropertyKey(itemsKey, sortProperty); + }); - Example + sortPropertyDefinitions.addObserver('@each', this, updateSortPropertiesOnce); + } - ```js - var Pagination = Ember.CollectionView.extend({ - tagName: 'ul', - classNames: ['pagination'], + function updateSortPropertiesOnce() { + run.once(this, updateSortProperties, changeMeta.propertyName); + } - init: function() { - this._super(); - if (!this.get('content')) { - this.set('content', Ember.A()); + function updateSortProperties(propertyName) { + setupSortProperties.call(this); + changeMeta.property.recomputeOnce.call(this, propertyName); } - } - }); - ``` - @method A - @for Ember - @return {Ember.NativeArray} - */ - var A = function(arr) { - if (arr === undefined) { arr = []; } - return EmberArray.detect(arr) ? arr : NativeArray.apply(arr); - }; + addObserver(this, sortPropertiesKey, updateSortPropertiesOnce); + setupSortProperties.call(this); - /** - 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`. + instanceMeta.order = function (itemA, itemB) { + var sortProperty, result, asc; + var keyA = this.keyFor(itemA); + var keyB = this.keyFor(itemB); - Example + for (var i = 0; i < this.sortProperties.length; ++i) { + sortProperty = this.sortProperties[i]; - ```js - if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Array) { - Ember.NativeArray.activate(); - } - ``` + result = compare(keyA[sortProperty], keyB[sortProperty]); - @method activate - @for Ember.NativeArray - @static - @return {void} - */ - NativeArray.activate = function() { - NativeArray.apply(Array.prototype); + if (result !== 0) { + asc = this.sortPropertyAscending[sortProperty]; + return asc ? result : (-1 * result); + } + } - A = function(arr) { return arr || []; }; - }; + return 0; + }; - if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Array) { - NativeArray.activate(); - } + instanceMeta.binarySearch = binarySearch; + setupKeyCache(instanceMeta); + }, - 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; - }); -define("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 - */ + addedItem: function (array, item, changeMeta, instanceMeta) { + var index = instanceMeta.binarySearch(array, item); + array.insertAt(index, item); + return array; + }, - var CoreObject = __dependency1__["default"]; - var Observable = __dependency2__["default"]; + removedItem: function (array, item, changeMeta, instanceMeta) { + var index = instanceMeta.binarySearch(array, item); + array.removeAt(index); + instanceMeta.dropKeyFor(item); + return array; + } + }); + } - /** - `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. + function setupKeyCache(instanceMeta) { + instanceMeta.keyFor = function(item) { + var guid = guidFor(item); + if (this.keyCache[guid]) { + return this.keyCache[guid]; + } + var sortProperty; + var key = {}; + for (var i = 0; i < this.sortProperties.length; ++i) { + sortProperty = this.sortProperties[i]; + key[sortProperty] = get(item, sortProperty); + } + return this.keyCache[guid] = key; + }; - @class Object - @namespace Ember - @extends Ember.CoreObject - @uses Ember.Observable - */ - var EmberObject = CoreObject.extend(Observable); - EmberObject.toString = function() { return "Ember.Object"; }; + instanceMeta.dropKeyFor = function(item) { + var guid = guidFor(item); + this.keyCache[guid] = null; + }; - __exports__["default"] = EmberObject; + instanceMeta.keyCache = {}; + } }); -define("ember-runtime/system/object_proxy", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/observer","ember-metal/property_events","ember-metal/computed","ember-metal/properties","ember-metal/mixin","ember-runtime/system/string","ember-runtime/system/object","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { +enifed("ember-runtime/controllers/array_controller", + ["ember-metal/core","ember-metal/property_get","ember-metal/enumerable_utils","ember-runtime/system/array_proxy","ember-runtime/mixins/sortable","ember-runtime/mixins/controller","ember-metal/computed","ember-metal/error","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { "use strict"; /** @module ember @submodule ember-runtime */ + var Ember = __dependency1__["default"]; - // Ember.assert var get = __dependency2__.get; - var set = __dependency3__.set; - var meta = __dependency4__.meta; - var addObserver = __dependency5__.addObserver; - var removeObserver = __dependency5__.removeObserver; - var addBeforeObserver = __dependency5__.addBeforeObserver; - var removeBeforeObserver = __dependency5__.removeBeforeObserver; - var propertyWillChange = __dependency6__.propertyWillChange; - var propertyDidChange = __dependency6__.propertyDidChange; + var forEach = __dependency3__.forEach; + var replace = __dependency3__.replace; + var ArrayProxy = __dependency4__["default"]; + var SortableMixin = __dependency5__["default"]; + var ControllerMixin = __dependency6__["default"]; var computed = __dependency7__.computed; - var defineProperty = __dependency8__.defineProperty; - var observer = __dependency9__.observer; - var fmt = __dependency10__.fmt; - var EmberObject = __dependency11__["default"]; - - function contentPropertyWillChange(content, contentKey) { - var key = contentKey.slice(8); // remove "content." - if (key in this) { return; } // if shadowed in proxy - propertyWillChange(this, key); - } + var EmberError = __dependency8__["default"]; - function contentPropertyDidChange(content, contentKey) { - var key = contentKey.slice(8); // remove "content." - if (key in this) { return; } // if shadowed in proxy - propertyDidChange(this, key); - } /** - `Ember.ObjectProxy` forwards all properties not defined by the proxy itself - to a proxied `content` object. + `Ember.ArrayController` provides a way for you to publish a collection of + objects so that you can easily bind to the collection from a Handlebars + `#each` helper, an `Ember.CollectionView`, or other controllers. + + The advantage of using an `ArrayController` is that you only have to set up + your view bindings once; to change what's displayed, simply swap out the + `model` property on the controller. + + For example, imagine you wanted to display a list of items fetched via an XHR + request. Create an `Ember.ArrayController` and set its `model` property: ```javascript - object = Ember.Object.create({ - name: 'Foo' - }); + MyApp.listController = Ember.ArrayController.create(); - proxy = Ember.ObjectProxy.create({ - content: object + $.get('people.json', function(data) { + MyApp.listController.set('model', data); }); + ``` + + Then, create a view that binds to your new controller: + + ```handlebars + {{#each person in MyApp.listController}} + {{person.firstName}} {{person.lastName}} + {{/each}} + ``` + + Although you are binding to the controller, the behavior of this controller + is to pass through any methods or properties to the underlying array. This + capability comes from `Ember.ArrayProxy`, which this class inherits from. + + Sometimes you want to display computed properties within the body of an + `#each` helper that depend on the underlying items in `model`, but are not + present on those items. To do this, set `itemController` to the name of a + controller (probably an `ObjectController`) that will wrap each individual item. - // Access and change existing properties - proxy.get('name') // 'Foo' - proxy.set('name', 'Bar'); - object.get('name') // 'Bar' + For example: - // Create new 'description' property on `object` - proxy.set('description', 'Foo is a whizboo baz'); - object.get('description') // 'Foo is a whizboo baz' + ```handlebars + {{#each post in controller}} +
  • {{post.title}} ({{post.titleLength}} characters)
  • + {{/each}} ``` - While `content` is unset, setting a property to be delegated will throw an - Error. - ```javascript - proxy = Ember.ObjectProxy.create({ - content: null, - flag: null + App.PostsController = Ember.ArrayController.extend({ + itemController: 'post' + }); + + App.PostController = Ember.ObjectController.extend({ + // the `title` property will be proxied to the underlying post. + titleLength: function() { + return this.get('title').length; + }.property('title') }); - proxy.set('flag', true); - proxy.get('flag'); // true - proxy.get('foo'); // undefined - proxy.set('foo', 'data'); // throws Error ``` - Delegated properties can be bound to and will change when content is updated. + In some cases it is helpful to return a different `itemController` depending + on the particular item. Subclasses can do this by overriding + `lookupItemController`. - Computed properties on the proxy itself can depend on delegated properties. + For example: ```javascript - ProxyWithComputedProperty = Ember.ObjectProxy.extend({ - fullName: function () { - var firstName = this.get('firstName'), - lastName = this.get('lastName'); - if (firstName && lastName) { - return firstName + ' ' + lastName; + App.MyArrayController = Ember.ArrayController.extend({ + lookupItemController: function( object ) { + if (object.get('isSpecial')) { + return "special"; // use App.SpecialController + } else { + return "regular"; // use App.RegularController } - return firstName || lastName; - }.property('firstName', 'lastName') + } }); - - proxy = ProxyWithComputedProperty.create(); - - proxy.get('fullName'); // undefined - proxy.set('content', { - firstName: 'Tom', lastName: 'Dale' - }); // triggers property change for fullName on proxy - - proxy.get('fullName'); // 'Tom Dale' ``` - @class ObjectProxy + The itemController instances will have a `parentController` property set to + the `ArrayController` instance. + + @class ArrayController @namespace Ember - @extends Ember.Object + @extends Ember.ArrayProxy + @uses Ember.SortableMixin + @uses Ember.ControllerMixin */ - var ObjectProxy = EmberObject.extend({ + + __exports__["default"] = ArrayProxy.extend(ControllerMixin, SortableMixin, { + /** - The object whose properties will be forwarded. + The controller used to wrap items, if any. If the value is a string, it will + be used to lookup the container for the controller. As an alternative, you + can also provide a controller class as the value. - @property content - @type Ember.Object + For example: + + ```javascript + App.MyArrayController = Ember.ArrayController.extend({ + itemController: Ember.ObjectController.extend({ + //Item Controller Implementation + }) + }); + ``` + + @property itemController + @type String | Ember.Controller @default null */ - content: null, - _contentDidChange: observer('content', function() { - Ember.assert("Can't set ObjectProxy's content to itself", get(this, 'content') !== this); - }), + itemController: null, - isTruthy: computed.bool('content'), + /** + Return the name of the controller to wrap items, or `null` if items should + be returned directly. The default implementation simply returns the + `itemController` property, but subclasses can override this method to return + different controllers for different objects. - _debugContainerKey: null, + For example: - willWatchProperty: function (key) { - var contentKey = 'content.' + key; - addBeforeObserver(this, contentKey, null, contentPropertyWillChange); - addObserver(this, contentKey, null, contentPropertyDidChange); - }, + ```javascript + App.MyArrayController = Ember.ArrayController.extend({ + lookupItemController: function( object ) { + if (object.get('isSpecial')) { + return "special"; // use App.SpecialController + } else { + return "regular"; // use App.RegularController + } + } + }); + ``` - didUnwatchProperty: function (key) { - var contentKey = 'content.' + key; - removeBeforeObserver(this, contentKey, null, contentPropertyWillChange); - removeObserver(this, contentKey, null, contentPropertyDidChange); + @method lookupItemController + @param {Object} object + @return {String} + */ + lookupItemController: function(object) { + return get(this, 'itemController'); }, - unknownProperty: function (key) { - var content = get(this, 'content'); - if (content) { - return get(content, key); - } - }, + objectAtContent: function(idx) { + var length = get(this, 'length'); + var arrangedContent = get(this, 'arrangedContent'); + var object = arrangedContent && arrangedContent.objectAt(idx); + var controllerClass; - setUnknownProperty: function (key, value) { - var m = meta(this); - if (m.proto === this) { - // if marked as prototype then just defineProperty - // rather than delegate - defineProperty(this, key, null, value); - return value; + if (idx >= 0 && idx < length) { + controllerClass = this.lookupItemController(object); + + if (controllerClass) { + return this.controllerAt(idx, object, controllerClass); + } } - var content = get(this, 'content'); - Ember.assert(fmt("Cannot delegate set('%@', %@) to the 'content' property of object proxy %@: its 'content' is undefined.", [key, value, this]), content); - return set(content, key, value); - } + // When `controllerClass` is falsy, we have not opted in to using item + // controllers, so return the object directly. - }); + // When the index is out of range, we want to return the "out of range" + // value, whatever that might be. Rather than make assumptions + // (e.g. guessing `null` or `undefined`) we defer this to `arrangedContent`. + return object; + }, - __exports__["default"] = ObjectProxy; - }); -define("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 + arrangedContentDidChange: function() { + this._super(); + this._resetSubControllers(); + }, - var get = __dependency2__.get; - var set = __dependency3__.set; - var guidFor = __dependency4__.guidFor; - var isNone = __dependency5__.isNone; - 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; + arrayContentDidChange: function(idx, removedCnt, addedCnt) { + var subControllers = this._subControllers; - /** - An unordered collection of objects. + if (subControllers.length) { + var subControllersToRemove = subControllers.slice(idx, idx + removedCnt); - 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. + forEach(subControllersToRemove, function(subController) { + if (subController) { + subController.destroy(); + } + }); - All Sets are observable via the Enumerable Observer API - which works - on any enumerable object including both Sets and Arrays. + replace(subControllers, idx, removedCnt, new Array(addedCnt)); + } - ## Creating a Set + // The shadow array of subcontrollers must be updated before we trigger + // observers, otherwise observers will get the wrong subcontainer when + // calling `objectAt` + this._super(idx, removedCnt, addedCnt); + }, - 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. + init: function() { + this._super(); + this._subControllers = []; + }, - 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()`. + model: computed(function () { + return Ember.A(); + }), - ```javascript - // creates a new empty set - var foundNames = new Ember.Set(); + /** + * Flag to mark as being "virtual". Used to keep this instance + * from participating in the parentController hierarchy. + * + * @private + * @property _isVirtual + * @type Boolean + */ + _isVirtual: false, - // creates a set with four names in it. - var names = new Ember.Set(["Charles", "Tom", "Juan", "Alex"]); // :P + controllerAt: function(idx, object, controllerClass) { + var container = get(this, 'container'); + var subControllers = this._subControllers; + var fullName, subController, subControllerFactory, parentController, options; - // creates a copy of the names set. - var namesCopy = new Ember.Set(names); + if (subControllers.length > idx) { + subController = subControllers[idx]; - // same as above. - var anotherNamesCopy = names.copy(); - ``` + if (subController) { + return subController; + } + } - ## Adding/Removing Objects + if (this._isVirtual) { + parentController = get(this, 'parentController'); + } else { + parentController = this; + } - 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. + + fullName = 'controller:' + controllerClass; - 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. + if (!container.has(fullName)) { + throw new EmberError('Could not resolve itemController: "' + controllerClass + '"'); + } - NOTE: You cannot add/remove `null` or `undefined` to a set. Any attempt to do - so will be ignored. + subController = container.lookupFactory(fullName).create({ + target: parentController, + parentController: parentController, + model: object + }); + - In addition to add/remove you can also call `push()`/`pop()`. Push behaves - just like `add()` but `pop()`, unlike `remove()` will pick an arbitrary - object, remove it and return it. This is a good way to use a set as a job - queue when you don't care which order the jobs are executed in. + subControllers[idx] = subController; - ## Testing for an Object + return subController; + }, - To test for an object's presence in a set you simply call - `Ember.Set#contains()`. + _subControllers: null, - ## Observing changes + _resetSubControllers: function() { + var controller; + var subControllers = this._subControllers; - When using `Ember.Set`, you can observe the `"[]"` property to be - alerted whenever the content changes. You can also add an enumerable - observer to the set to be notified of specific objects that are added and - removed from the set. See [Ember.Enumerable](/api/classes/Ember.Enumerable.html) - for more information on enumerables. + if (subControllers.length) { + for (var i = 0, length = subControllers.length; length > i; i++) { + controller = subControllers[i]; - This is often unhelpful. If you are filtering sets of objects, for instance, - it is very inefficient to re-filter all of the items each time the set - changes. It would be better if you could just adjust the filtered set based - on what was changed on the original set. The same issue applies to merging - sets, as well. + if (controller) { + controller.destroy(); + } + } - ## Other Methods + subControllers.length = 0; + } + }, - `Ember.Set` primary implements other mixin APIs. For a complete reference - on the methods you will use with `Ember.Set`, please consult these mixins. - The most useful ones will be `Ember.Enumerable` and - `Ember.MutableEnumerable` which implement most of the common iterator - methods you are used to on Array. + willDestroy: function() { + this._resetSubControllers(); + this._super(); + } + }); + }); +enifed("ember-runtime/controllers/controller", + ["ember-metal/core","ember-runtime/system/object","ember-runtime/mixins/controller","ember-runtime/inject","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // Ember.assert + var EmberObject = __dependency2__["default"]; + var Mixin = __dependency3__["default"]; + var createInjectionHelper = __dependency4__.createInjectionHelper; - Note that you can also use the `Ember.Copyable` and `Ember.Freezable` - APIs on `Ember.Set` as well. Once a set is frozen it can no longer be - modified. The benefit of this is that when you call `frozenCopy()` on it, - Ember will avoid making copies of the set. This allows you to write - code that can know with certainty when the underlying set data will or - will not be modified. + /** + @module ember + @submodule ember-runtime + */ - @class Set + /** + @class Controller @namespace Ember - @extends Ember.CoreObject - @uses Ember.MutableEnumerable - @uses Ember.Copyable - @uses Ember.Freezable - @since Ember 0.9 + @extends Ember.Object + @uses Ember.ControllerMixin */ - var Set = CoreObject.extend(MutableEnumerable, Copyable, Freezable, - { + var Controller = EmberObject.extend(Mixin); - // .......................................................... - // IMPLEMENT ENUMERABLE APIS - // + function controllerInjectionHelper(factory) { + Ember.assert("Defining an injected controller property on a " + + "non-controller is not allowed.", Controller.detect(factory)); + } + /** - This property will change as the number of objects in the set changes. + Creates a property that lazily looks up another controller in the container. + Can only be used when defining another controller. - @property length - @type number - @default 0 - */ - length: 0, + Example: - /** - Clears the set. This is useful if you want to reuse an existing set - without having to recreate it. + ```javascript + App.PostController = Ember.Controller.extend({ + posts: Ember.inject.controller() + }); + ``` + + This example will create a `posts` property on the `post` controller that + looks up the `posts` controller in the container, making it easy to + reference other controllers. This is functionally equivalent to: ```javascript - var colors = new Ember.Set(["red", "green", "blue"]); - colors.length; // 3 - colors.clear(); - colors.length; // 0 + App.PostController = Ember.Controller.extend({ + needs: 'posts', + posts: Ember.computed.alias('controllers.posts') + }); ``` - @method clear - @return {Ember.Set} An empty Set - */ - clear: function() { - if (this.isFrozen) { throw new EmberError(FROZEN_ERROR); } + @method inject.controller + @for Ember + @param {String} name (optional) name of the controller to inject, defaults + to the property's name + @return {Ember.InjectedProperty} injection descriptor instance + */ + createInjectionHelper('controller', controllerInjectionHelper); + - var len = get(this, 'length'); - if (len === 0) { return this; } + __exports__["default"] = Controller; + }); +enifed("ember-runtime/controllers/object_controller", + ["ember-runtime/mixins/controller","ember-runtime/system/object_proxy","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var ControllerMixin = __dependency1__["default"]; + var ObjectProxy = __dependency2__["default"]; - var guid; + /** + @module ember + @submodule ember-runtime + */ - this.enumerableContentWillChange(len, 0); - propertyWillChange(this, 'firstObject'); - propertyWillChange(this, 'lastObject'); + /** + `Ember.ObjectController` is part of Ember's Controller layer. It is intended + to wrap a single object, proxying unhandled attempts to `get` and `set` to the underlying + model object, and to forward unhandled action attempts to its `target`. - for (var i=0; i < len; i++) { - guid = guidFor(this[i]); - delete this[guid]; - delete this[i]; + `Ember.ObjectController` derives this functionality from its superclass + `Ember.ObjectProxy` and the `Ember.ControllerMixin` mixin. + + @class ObjectController + @namespace Ember + @extends Ember.ObjectProxy + @uses Ember.ControllerMixin + **/ + __exports__["default"] = ObjectProxy.extend(ControllerMixin); + }); +enifed("ember-runtime/copy", + ["ember-metal/enumerable_utils","ember-metal/utils","ember-runtime/system/object","ember-runtime/mixins/copyable","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + var indexOf = __dependency1__.indexOf; + var typeOf = __dependency2__.typeOf; + var EmberObject = __dependency3__["default"]; + var Copyable = __dependency4__["default"]; + + function _copy(obj, deep, seen, copies) { + var ret, loc, key; + + // primitive data types are immutable, just return them. + if (typeof obj !== 'object' || obj === null) { + return obj; + } + + // avoid cyclical loops + if (deep && (loc = indexOf(seen, obj)) >= 0) { + return copies[loc]; + } + + Ember.assert('Cannot clone an Ember.Object that does not implement Ember.Copyable', + !(obj instanceof EmberObject) || (Copyable && Copyable.detect(obj))); + + // IMPORTANT: this specific test will detect a native array only. Any other + // object will need to implement Copyable. + if (typeOf(obj) === 'array') { + ret = obj.slice(); + + if (deep) { + loc = ret.length; + + while (--loc >= 0) { + ret[loc] = _copy(ret[loc], deep, seen, copies); + } } + } else if (Copyable && Copyable.detect(obj)) { + ret = obj.copy(deep, seen, copies); + } else if (obj instanceof Date) { + ret = new Date(obj.getTime()); + } else { + ret = {}; - set(this, 'length', 0); + for (key in obj) { + // support Null prototype + if (!Object.prototype.hasOwnProperty.call(obj, key)) { + continue; + } - propertyDidChange(this, 'firstObject'); - propertyDidChange(this, 'lastObject'); - this.enumerableContentDidChange(len, 0); + // Prevents browsers that don't respect non-enumerability from + // copying internal Ember properties + if (key.substring(0, 2) === '__') { + continue; + } - return this; - }, + ret[key] = deep ? _copy(obj[key], deep, seen, copies) : obj[key]; + } + } - /** - Returns true if the passed object is also an enumerable that contains the - same objects as the receiver. + if (deep) { + seen.push(obj); + copies.push(ret); + } - ```javascript - var colors = ["red", "green", "blue"], - same_colors = new Ember.Set(colors); + return ret; + } - same_colors.isEqual(colors); // true - same_colors.isEqual(["purple", "brown"]); // false - ``` + /** + Creates a clone of the passed object. This function can take just about + any type of object and create a clone of it, including primitive values + (which are not actually cloned because they are immutable). - @method isEqual - @param {Ember.Set} obj the other object. - @return {Boolean} - */ - isEqual: function(obj) { - // fail fast - if (!Enumerable.detect(obj)) return false; + If the passed object implements the `copy()` method, then this function + will simply call that method and return the result. Please see + `Ember.Copyable` for further details. + + @method copy + @for Ember + @param {Object} obj The object to clone + @param {Boolean} deep If true, a deep copy of the object is made + @return {Object} The cloned object + */ + __exports__["default"] = function copy(obj, deep) { + // fast paths + if ('object' !== typeof obj || obj === null) { + return obj; // can't copy primitives + } + + if (Copyable && Copyable.detect(obj)) { + return obj.copy(deep); + } + + return _copy(obj, deep, deep ? [] : null, deep ? [] : null); + } + }); +enifed("ember-runtime/core", + ["exports"], + function(__exports__) { + "use strict"; + /** + @module ember + @submodule ember-runtime + */ + + /** + Compares two objects, returning true if they are logically equal. This is + a deeper comparison than a simple triple equal. For sets it will compare the + internal objects. For any other object that implements `isEqual()` it will + respect that method. + + ```javascript + Ember.isEqual('hello', 'hello'); // true + Ember.isEqual(1, 2); // false + Ember.isEqual([4, 2], [4, 2]); // false + ``` + + @method isEqual + @for Ember + @param {Object} a first object to compare + @param {Object} b second object to compare + @return {Boolean} + */ + var isEqual = function isEqual(a, b) { + if (a && typeof a.isEqual === 'function') { + return a.isEqual(b); + } + + if (a instanceof Date && b instanceof Date) { + return a.getTime() === b.getTime(); + } + + return a === b; + }; + __exports__.isEqual = isEqual; + }); +enifed("ember-runtime/ext/function", + ["ember-metal/core","ember-metal/expand_properties","ember-metal/computed","ember-metal/mixin"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { + "use strict"; + /** + @module ember + @submodule ember-runtime + */ - var loc = get(this, 'length'); - if (get(obj, 'length') !== loc) return false; + var Ember = __dependency1__["default"]; + // Ember.EXTEND_PROTOTYPES, Ember.assert + var expandProperties = __dependency2__["default"]; + var computed = __dependency3__.computed; + var observer = __dependency4__.observer; - while(--loc >= 0) { - if (!obj.contains(this[loc])) return false; - } + var a_slice = Array.prototype.slice; + var FunctionPrototype = Function.prototype; - return true; - }, + if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) { /** - 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. + The `property` extension of Javascript's Function prototype is available + when `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Function` is + `true`, which is the default. - This is an alias for `Ember.MutableEnumerable.addObject()`. + Computed properties allow you to treat a function like a property: ```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"] - ``` + MyApp.President = Ember.Object.extend({ + firstName: '', + lastName: '', - @method add - @param {Object} obj The object to add. - @return {Ember.Set} The set itself. - */ - add: aliasMethod('addObject'), + fullName: function() { + return this.get('firstName') + ' ' + this.get('lastName'); + }.property() // Call this flag to mark the function as a property + }); - /** - 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()`. + var president = MyApp.President.create({ + firstName: 'Barack', + lastName: 'Obama' + }); - ```javascript - var colors = new Ember.Set(["red", "green", "blue"]); - colors.remove("red"); // ["blue", "green"] - colors.remove("purple"); // ["blue", "green"] - colors.remove(null); // ["blue", "green"] + president.get('fullName'); // 'Barack Obama' ``` - @method remove - @param {Object} obj The object to remove - @return {Ember.Set} The set itself. - */ - remove: aliasMethod('removeObject'), + Treating a function like a property is useful because they can work with + bindings, just like any other property. - /** - Removes the last element from the set and returns it, or `null` if it's empty. + Many computed properties have dependencies on other properties. For + example, in the above example, the `fullName` property depends on + `firstName` and `lastName` to determine its value. You can tell Ember + about these dependencies like this: ```javascript - var colors = new Ember.Set(["green", "blue"]); - colors.pop(); // "blue" - colors.pop(); // "green" - colors.pop(); // null - ``` + MyApp.President = Ember.Object.extend({ + firstName: '', + lastName: '', - @method pop - @return {Object} The removed object from the set or 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; - }, + fullName: function() { + return this.get('firstName') + ' ' + this.get('lastName'); - /** - Inserts the given object on to the end of the set. It returns - the set itself. + // Tell Ember.js that this computed property depends on firstName + // and lastName + }.property('firstName', 'lastName') + }); + ``` - This is an alias for `Ember.MutableEnumerable.addObject()`. + Make sure you list these dependencies so Ember knows when to update + bindings that connect to a computed property. Changing a dependency + will not immediately trigger an update of the computed property, but + will instead clear the cache so that it is updated when the next `get` + is called on the property. - ```javascript - var colors = new Ember.Set(); - colors.push("red"); // ["red"] - colors.push("green"); // ["red", "green"] - colors.push("blue"); // ["red", "green", "blue"] - ``` + See [Ember.ComputedProperty](/api/classes/Ember.ComputedProperty.html), [Ember.computed](/api/#method_computed). - @method push - @return {Ember.Set} The set itself. + @method property + @for Function */ - push: aliasMethod('addObject'), + FunctionPrototype.property = function () { + var ret = computed(this); + // ComputedProperty.prototype.property expands properties; no need for us to + // do so here. + return ret.property.apply(ret, arguments); + }; /** - Removes the last element from the set and returns it, or `null` if it's empty. + The `observes` extension of Javascript's Function prototype is available + when `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Function` is + true, which is the default. - This is an alias for `Ember.Set.pop()`. + You can observe property changes simply by adding the `observes` + call to the end of your method declarations in classes that you write. + For example: ```javascript - var colors = new Ember.Set(["green", "blue"]); - colors.shift(); // "blue" - colors.shift(); // "green" - colors.shift(); // null + Ember.Object.extend({ + valueObserver: function() { + // Executes whenever the "value" property changes + }.observes('value') + }); ``` - @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()` + In the future this method may become asynchronous. If you want to ensure + synchronous behavior, use `observesImmediately`. - ```javascript - var colors = new Ember.Set(); - colors.unshift("red"); // ["red"] - colors.unshift("green"); // ["red", "green"] - colors.unshift("blue"); // ["red", "green", "blue"] - ``` + See `Ember.observer`. - @method unshift - @return {Ember.Set} The set itself. + @method observes + @for Function */ - unshift: aliasMethod('push'), + FunctionPrototype.observes = function() { + var length = arguments.length; + var args = new Array(length); + for (var x = 0; x < length; x++) { + args[x] = arguments[x]; + } + return observer.apply(this, args.concat(this)); + }; /** - Adds each object in the passed enumerable to the set. + The `observesImmediately` extension of Javascript's Function prototype is + available when `Ember.EXTEND_PROTOTYPES` or + `Ember.EXTEND_PROTOTYPES.Function` is true, which is the default. - This is an alias of `Ember.MutableEnumerable.addObjects()` + You can observe property changes simply by adding the `observesImmediately` + call to the end of your method declarations in classes that you write. + For example: ```javascript - var colors = new Ember.Set(); - colors.addEach(["red", "green", "blue"]); // ["red", "green", "blue"] + Ember.Object.extend({ + valueObserver: function() { + // Executes immediately after the "value" property changes + }.observesImmediately('value') + }); ``` - @method addEach - @param {Ember.Enumerable} objects the objects to add. - @return {Ember.Set} The set itself. + In the future, `observes` may become asynchronous. In this event, + `observesImmediately` will maintain the synchronous behavior. + + See `Ember.immediateObserver`. + + @method observesImmediately + @for Function */ - addEach: aliasMethod('addObjects'), + FunctionPrototype.observesImmediately = function () { + Ember.assert('Immediate observers must observe internal properties only, ' + + 'not properties on other objects.', function checkIsInternalProperty() { + for(var i = 0, l = arguments.length; i < l; i++) { + if(arguments[i].indexOf('.') !== -1) { + return false; + } + } + return true; + }); + + // observes handles property expansion + return this.observes.apply(this, arguments); + }; /** - Removes each object in the passed enumerable to the set. + The `observesBefore` extension of Javascript's Function prototype is + available when `Ember.EXTEND_PROTOTYPES` or + `Ember.EXTEND_PROTOTYPES.Function` is true, which is the default. - This is an alias of `Ember.MutableEnumerable.removeObjects()` + You can get notified when a property change is about to happen by + by adding the `observesBefore` call to the end of your method + declarations in classes that you write. For example: ```javascript - var colors = new Ember.Set(["red", "green", "blue"]); - colors.removeEach(["red", "blue"]); // ["green"] + Ember.Object.extend({ + valueObserver: function() { + // Executes whenever the "value" property is about to change + }.observesBefore('value') + }); ``` - @method removeEach - @param {Ember.Enumerable} objects the objects to remove. - @return {Ember.Set} The set itself. - */ - removeEach: aliasMethod('removeObjects'), - - // .......................................................... - // PRIVATE ENUMERABLE SUPPORT - // - - init: function(items) { - this._super(); - if (items) this.addObjects(items); - }, - - // implement Ember.Enumerable - nextObject: function(idx) { - return this[idx]; - }, + See `Ember.beforeObserver`. - // more optimized version - firstObject: computed(function() { - return this.length > 0 ? this[0] : undefined; - }), + @method observesBefore + @for Function + */ + FunctionPrototype.observesBefore = function () { + var watched = []; + var addWatchedProperty = function (obs) { + watched.push(obs); + }; - // more optimized version - lastObject: computed(function() { - return this.length > 0 ? this[this.length-1] : undefined; - }), + for (var i = 0, l = arguments.length; i < l; ++i) { + expandProperties(arguments[i], addWatchedProperty); + } - // implements Ember.MutableEnumerable - addObject: function(obj) { - if (get(this, 'isFrozen')) throw new EmberError(FROZEN_ERROR); - if (isNone(obj)) return this; // nothing to do + this.__ember_observesBefore__ = watched; - var guid = guidFor(obj), - idx = this[guid], - len = get(this, 'length'), - added ; + return this; + }; - if (idx>=0 && idx=0 && idx=0; - }, + RSVP.onerrorDefault = function (e) { + var error; - 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; - } - return ret; - }, + if (e && e.errorThrown) { + // jqXHR provides this + error = e.errorThrown; + error.__reason_with_error_thrown__ = e; + } else { + error = e; + } - toString: function() { - var len = this.length, idx, array = []; - for(idx = 0; idx < len; idx++) { - array[idx] = this[idx]; + if (error && error.name !== 'TransitionAborted') { + if (Ember.testing) { + // ES6TODO: remove when possible + if (!Test && Ember.__loader.registry[testModuleName]) { + Test = requireModule(testModuleName)['default']; + } + + if (Test && Test.adapter) { + Test.adapter.exception(error); + Logger.error(error.stack); + } else { + throw error; + } + } else if (Ember.onerror) { + Ember.onerror(error); + } else { + Logger.error(error.stack); } - return fmt("Ember.Set<%@>", [array.join(',')]); } + }; - }); - + RSVP.on('error', RSVP.onerrorDefault); - __exports__["default"] = Set; + __exports__["default"] = RSVP; }); -define("ember-runtime/system/string", - ["ember-metal/core","ember-metal/utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { +enifed("ember-runtime/ext/string", + ["ember-metal/core","ember-runtime/system/string"], + function(__dependency1__, __dependency2__) { "use strict"; /** @module ember @submodule ember-runtime */ - var Ember = __dependency1__["default"]; - // Ember.STRINGS, Ember.FEATURES - var EmberInspect = __dependency2__.inspect; - - - var STRING_DASHERIZE_REGEXP = (/[ _]/g); - var STRING_DASHERIZE_CACHE = {}; - var STRING_DECAMELIZE_REGEXP = (/([a-z\d])([A-Z])/g); - var STRING_CAMELIZE_REGEXP = (/(\-|_|\.|\s)+(.)?/g); - var STRING_UNDERSCORE_REGEXP_1 = (/([a-z\d])([A-Z]+)/g); - var STRING_UNDERSCORE_REGEXP_2 = (/\-|\s+/g); - - function fmt(str, formats) { - // 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 = formats[argIndex]; - return (s === null) ? '(null)' : (s === undefined) ? '' : EmberInspect(s); - }) ; - } - - function loc(str, formats) { - str = Ember.STRINGS[str] || str; - return fmt(str, formats); - } - - function w(str) { - return str.split(/\s+/); - } - - function decamelize(str) { - return str.replace(STRING_DECAMELIZE_REGEXP, '$1_$2').toLowerCase(); - } - - function dasherize(str) { - var cache = STRING_DASHERIZE_CACHE, - hit = cache.hasOwnProperty(str), - ret; - - if (hit) { - return cache[str]; - } else { - ret = decamelize(str).replace(STRING_DASHERIZE_REGEXP,'-'); - cache[str] = ret; - } - - return ret; - } - - function camelize(str) { - return str.replace(STRING_CAMELIZE_REGEXP, function(match, separator, chr) { - return chr ? chr.toUpperCase() : ''; - }).replace(/^([A-Z])/, function(match, separator, chr) { - return match.toLowerCase(); - }); - } - - function classify(str) { - var parts = str.split("."), - out = []; - - for (var i=0, l=parts.length; i alpha - // > beta - // > gamma - ``` + /** + See [Ember.String.w](/api/classes/Ember.String.html#method_w). @method w - @param {String} str The string to split - @return {Array} array containing the split strings + @for String */ - w: w, + StringPrototype.w = function () { + return w(this); + }; /** - Converts a camelized string into all lower case separated by underscores. - - ```javascript - 'innerHTML'.decamelize(); // 'inner_html' - 'action_name'.decamelize(); // 'action_name' - 'css-class-name'.decamelize(); // 'css-class-name' - 'my favorite items'.decamelize(); // 'my favorite items' - ``` + See [Ember.String.loc](/api/classes/Ember.String.html#method_loc). - @method decamelize - @param {String} str The string to decamelize. - @return {String} the decamelized string. + @method loc + @for String */ - decamelize: decamelize, + StringPrototype.loc = function () { + return loc(this, arguments); + }; /** - Replaces underscores, spaces, or camelCase with dashes. - - ```javascript - 'innerHTML'.dasherize(); // 'inner-html' - 'action_name'.dasherize(); // 'action-name' - 'css-class-name'.dasherize(); // 'css-class-name' - 'my favorite items'.dasherize(); // 'my-favorite-items' - ``` + See [Ember.String.camelize](/api/classes/Ember.String.html#method_camelize). - @method dasherize - @param {String} str The string to dasherize. - @return {String} the dasherized string. + @method camelize + @for String */ - dasherize: dasherize, + StringPrototype.camelize = function () { + return camelize(this); + }; /** - Returns the lowerCamelCase form of a string. - - ```javascript - 'innerHTML'.camelize(); // 'innerHTML' - 'action_name'.camelize(); // 'actionName' - 'css-class-name'.camelize(); // 'cssClassName' - 'my favorite items'.camelize(); // 'myFavoriteItems' - 'My Favorite Items'.camelize(); // 'myFavoriteItems' - ``` + See [Ember.String.decamelize](/api/classes/Ember.String.html#method_decamelize). - @method camelize - @param {String} str The string to camelize. - @return {String} the camelized string. + @method decamelize + @for String */ - camelize: camelize, + StringPrototype.decamelize = function () { + return decamelize(this); + }; /** - Returns the UpperCamelCase form of a string. - - ```javascript - 'innerHTML'.classify(); // 'InnerHTML' - 'action_name'.classify(); // 'ActionName' - 'css-class-name'.classify(); // 'CssClassName' - 'my favorite items'.classify(); // 'MyFavoriteItems' - ``` + See [Ember.String.dasherize](/api/classes/Ember.String.html#method_dasherize). - @method classify - @param {String} str the string to classify - @return {String} the classified string + @method dasherize + @for String */ - classify: classify, + StringPrototype.dasherize = function () { + return dasherize(this); + }; /** - More general than decamelize. Returns the lower\_case\_and\_underscored - form of a string. - - ```javascript - 'innerHTML'.underscore(); // 'inner_html' - 'action_name'.underscore(); // 'action_name' - 'css-class-name'.underscore(); // 'css_class_name' - 'my favorite items'.underscore(); // 'my_favorite_items' - ``` + See [Ember.String.underscore](/api/classes/Ember.String.html#method_underscore). @method underscore - @param {String} str The string to underscore. - @return {String} the underscored string. + @for String */ - underscore: underscore, + StringPrototype.underscore = function () { + return underscore(this); + }; /** - Returns the Capitalized form of a string + See [Ember.String.classify](/api/classes/Ember.String.html#method_classify). - ```javascript - 'innerHTML'.capitalize() // 'InnerHTML' - 'action_name'.capitalize() // 'Action_name' - 'css-class-name'.capitalize() // 'Css-class-name' - 'my favorite items'.capitalize() // 'My favorite items' - ``` + @method classify + @for String + */ + StringPrototype.classify = function () { + return classify(this); + }; + + /** + See [Ember.String.capitalize](/api/classes/Ember.String.html#method_capitalize). @method capitalize - @param {String} str The string to capitalize. - @return {String} The capitalized string. + @for String */ - capitalize: capitalize - }; - - __exports__["default"] = EmberStringUtils; - __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; + StringPrototype.capitalize = function () { + return capitalize(this); + }; + } }); -define("ember-runtime/system/subarray", - ["ember-metal/property_get","ember-metal/error","ember-metal/enumerable_utils","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { +enifed("ember-runtime/inject", + ["ember-metal/core","ember-metal/enumerable_utils","ember-metal/utils","ember-metal/injected_property","ember-metal/keys","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { "use strict"; - var get = __dependency1__.get; - var EmberError = __dependency2__["default"]; - var EnumerableUtils = __dependency3__["default"]; + var Ember = __dependency1__["default"]; + // Ember.assert + var indexOf = __dependency2__.indexOf; + var meta = __dependency3__.meta; + var InjectedProperty = __dependency4__["default"]; + var keys = __dependency5__["default"]; - var RETAIN = 'r', - FILTER = 'f'; + /** + Namespace for injection helper methods. - function Operation (type, count) { - this.type = type; - this.count = count; + @class inject + @namespace Ember + */ + function inject() { + Ember.assert("Injected properties must be created through helpers, see `" + + keys(inject).join("`, `") + "`"); } + // Dictionary of injection validations by type, added to by `createInjectionHelper` + var typeValidators = {}; + /** - 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. + This method allows other Ember modules to register injection helpers for a + given container type. Helpers are exported to the `inject` namespace as the + container type itself. - @class SubArray + @private + @method createInjectionHelper @namespace Ember + @param {String} type The container type the helper will inject + @param {Function} validator A validation callback that is executed at mixin-time */ - function SubArray (length) { - if (arguments.length < 1) { length = 0; } - - if (length > 0) { - this._operations = [new Operation(RETAIN, length)]; - } else { - this._operations = []; - } - }; - - SubArray.prototype = { - /** - Track that an item was added to the tracked array. - - @method addItem + function createInjectionHelper(type, validator) { + typeValidators[type] = validator; - @param {number} index The index of the item in the tracked array. - @param {boolean} match `true` iff the item is included in the subarray. + inject[type] = function(name) { + return new InjectedProperty(type, name); + }; + } - @return {number} The index of the item in the subarray. - */ - addItem: function(index, match) { - var returnValue = -1, - itemType = match ? RETAIN : FILTER, - self = this; + __exports__.createInjectionHelper = createInjectionHelper;/** + Validation function that runs per-type validation functions once for each + injected type encountered. - this._findOperation(index, function(operation, operationIndex, rangeStart, rangeEnd, seenInSubArray) { - var newOperation, splitOperation; + @private + @method validatePropertyInjections + @namespace Ember + @param {Object} factory The factory object + */ + function validatePropertyInjections(factory) { + var proto = factory.proto(); + var descs = meta(proto).descs; + var types = []; + var key, desc, validator, i, l; - if (itemType === operation.type) { - ++operation.count; - } else if (index === rangeStart) { - // insert to the left of `operation` - self._operations.splice(operationIndex, 0, new Operation(itemType, 1)); - } else { - newOperation = new Operation(itemType, 1); - splitOperation = new Operation(operation.type, rangeEnd - index + 1); - operation.count = index - rangeStart; + for (key in descs) { + desc = descs[key]; + if (desc instanceof InjectedProperty && indexOf(types, desc.type) === -1) { + types.push(desc.type); + } + } - self._operations.splice(operationIndex + 1, 0, newOperation, splitOperation); - } + if (types.length) { + for (i = 0, l = types.length; i < l; i++) { + validator = typeValidators[types[i]]; - if (match) { - if (operation.type === RETAIN) { - returnValue = seenInSubArray + (index - rangeStart); - } else { - returnValue = seenInSubArray; - } + if (typeof validator === 'function') { + validator(factory); } + } + } - self._composeAt(operationIndex); - }, function(seenInSubArray) { - self._operations.push(new Operation(itemType, 1)); + return true; + } - if (match) { - returnValue = seenInSubArray; - } + __exports__.validatePropertyInjections = validatePropertyInjections;__exports__["default"] = inject; + }); +enifed("ember-runtime/mixins/-proxy", + ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/observer","ember-metal/property_events","ember-metal/computed","ember-metal/properties","ember-metal/mixin","ember-runtime/system/string","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-runtime + */ - self._composeAt(self._operations.length-1); - }); + var Ember = __dependency1__["default"]; + // Ember.assert + var get = __dependency2__.get; + var set = __dependency3__.set; + var meta = __dependency4__.meta; + var addObserver = __dependency5__.addObserver; + var removeObserver = __dependency5__.removeObserver; + var addBeforeObserver = __dependency5__.addBeforeObserver; + var removeBeforeObserver = __dependency5__.removeBeforeObserver; + var propertyWillChange = __dependency6__.propertyWillChange; + var propertyDidChange = __dependency6__.propertyDidChange; + var computed = __dependency7__.computed; + var defineProperty = __dependency8__.defineProperty; + var Mixin = __dependency9__.Mixin; + var observer = __dependency9__.observer; + var fmt = __dependency10__.fmt; - return returnValue; - }, + function contentPropertyWillChange(content, contentKey) { + var key = contentKey.slice(8); // remove "content." + if (key in this) { return; } // if shadowed in proxy + propertyWillChange(this, key); + } - /** - Track that an item was removed from the tracked array. + function contentPropertyDidChange(content, contentKey) { + var key = contentKey.slice(8); // remove "content." + if (key in this) { return; } // if shadowed in proxy + propertyDidChange(this, key); + } - @method removeItem + /** + `Ember.ProxyMixin` forwards all properties not defined by the proxy itself + to a proxied `content` object. See Ember.ObjectProxy for more details. - @param {number} index The index of the item in the tracked array. + @class ProxyMixin + @namespace Ember + */ + __exports__["default"] = Mixin.create({ + /** + The object whose properties will be forwarded. - @return {number} The index of the item in the subarray, or `-1` if the item - was not in the subarray. + @property content + @type Ember.Object + @default null */ - removeItem: function(index) { - var returnValue = -1, - self = this; + content: null, + _contentDidChange: observer('content', function() { + Ember.assert("Can't set Proxy's content to itself", get(this, 'content') !== this); + }), - this._findOperation(index, function (operation, operationIndex, rangeStart, rangeEnd, seenInSubArray) { - if (operation.type === RETAIN) { - returnValue = seenInSubArray + (index - rangeStart); - } + isTruthy: computed.bool('content'), - if (operation.count > 1) { - --operation.count; - } else { - self._operations.splice(operationIndex, 1); - self._composeAt(operationIndex); - } - }, function() { - throw new EmberError("Can't remove an item that has never been added."); - }); + _debugContainerKey: null, - return returnValue; + willWatchProperty: function (key) { + var contentKey = 'content.' + key; + addBeforeObserver(this, contentKey, null, contentPropertyWillChange); + addObserver(this, contentKey, null, contentPropertyDidChange); }, - - _findOperation: function (index, foundCallback, notFoundCallback) { - var operationIndex, - len, - operation, - rangeStart, - rangeEnd, - seenInSubArray = 0; - - // 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; - - if (index >= rangeStart && index <= rangeEnd) { - foundCallback(operation, operationIndex, rangeStart, rangeEnd, seenInSubArray); - return; - } else if (operation.type === RETAIN) { - seenInSubArray += operation.count; - } - } - - notFoundCallback(seenInSubArray); + didUnwatchProperty: function (key) { + var contentKey = 'content.' + key; + removeBeforeObserver(this, contentKey, null, contentPropertyWillChange); + removeObserver(this, contentKey, null, contentPropertyDidChange); }, - _composeAt: function(index) { - var op = this._operations[index], - otherOp; - - if (!op) { - // Composing out of bounds is a no-op, as when removing the last operation - // in the list. - return; - } - - if (index > 0) { - otherOp = this._operations[index-1]; - if (otherOp.type === op.type) { - op.count += otherOp.count; - this._operations.splice(index-1, 1); - --index; - } + unknownProperty: function (key) { + var content = get(this, 'content'); + if (content) { + return get(content, key); } + }, - 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); - } + setUnknownProperty: function (key, value) { + var m = meta(this); + if (m.proto === this) { + // if marked as prototype then just defineProperty + // rather than delegate + defineProperty(this, key, null, value); + return value; } - }, - toString: function () { - var str = ""; - forEach(this._operations, function (operation) { - str += " " + operation.type + ":" + operation.count; - }); - return str.substring(1); + var content = get(this, 'content'); + Ember.assert(fmt("Cannot delegate set('%@', %@) to the 'content' property of" + + " object proxy %@: its 'content' is undefined.", [key, value, this]), content); + return set(content, key, value); } - }; - __exports__["default"] = SubArray; + }); }); -define("ember-runtime/system/tracked_array", - ["ember-metal/property_get","ember-metal/enumerable_utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { +enifed("ember-runtime/mixins/action_handler", + ["ember-metal/merge","ember-metal/mixin","ember-metal/property_get","ember-metal/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; - var get = __dependency1__.get; - var EnumerableUtils = __dependency2__["default"]; - - var forEach = EnumerableUtils.forEach, - RETAIN = 'r', - INSERT = 'i', - DELETE = 'd'; - + /** + @module ember + @submodule ember-runtime + */ + var merge = __dependency1__["default"]; + var Mixin = __dependency2__.Mixin; + var get = __dependency3__.get; + var typeOf = __dependency4__.typeOf; /** - 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. + The `Ember.ActionHandler` mixin implements support for moving an `actions` + property to an `_actions` property at extend time, and adding `_actions` + to the object's mergedProperties list. - @class TrackedArray + `Ember.ActionHandler` is available on some familiar classes including + `Ember.Route`, `Ember.View`, `Ember.Component`, and controllers such as + `Ember.Controller` and `Ember.ObjectController`. + (Internally the mixin is used by `Ember.CoreView`, `Ember.ControllerMixin`, + and `Ember.Route` and available to the above classes through + inheritance.) + + @class ActionHandler @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 ActionHandler = Mixin.create({ + mergedProperties: ['_actions'], - var length = get(items, 'length'); + /** + The collection of functions, keyed by name, available on this + `ActionHandler` as action targets. - if (length) { - this._operations = [new ArrayOperation(RETAIN, length, items)]; - } else { - this._operations = []; - } - } + These functions will be invoked when a matching `{{action}}` is triggered + from within a template and the application's current route is this route. - TrackedArray.RETAIN = RETAIN; - TrackedArray.INSERT = INSERT; - TrackedArray.DELETE = DELETE; + Actions can also be invoked from other parts of your application + via `ActionHandler#send`. - TrackedArray.prototype = { + The `actions` hash will inherit action handlers from + the `actions` hash defined on extended parent classes + or mixins rather than just replace the entire hash, e.g.: - /** - Track that `newItems` were added to the tracked array at `index`. + ```js + App.CanDisplayBanner = Ember.Mixin.create({ + actions: { + displayBanner: function(msg) { + // ... + } + } + }); - @method addItems - @param index - @param newItems - */ - addItems: function (index, newItems) { - var count = get(newItems, 'length'); - if (count < 1) { return; } + App.WelcomeRoute = Ember.Route.extend(App.CanDisplayBanner, { + actions: { + playMusic: function() { + // ... + } + } + }); - var match = this._findArrayOperation(index), - arrayOperation = match.operation, - arrayOperationIndex = match.index, - arrayOperationRangeStart = match.rangeStart, - composeIndex, - splitIndex, - splitItems, - splitArrayOperation, - newArrayOperation; + // `WelcomeRoute`, when active, will be able to respond + // to both actions, since the actions hash is merged rather + // then replaced when extending mixins / parent classes. + this.send('displayBanner'); + this.send('playMusic'); + ``` - newArrayOperation = new ArrayOperation(INSERT, count, newItems); + Within a Controller, Route, View or Component's action handler, + the value of the `this` context is the Controller, Route, View or + Component object: - 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; + ```js + App.SongRoute = Ember.Route.extend({ + actions: { + myAction: function() { + this.controllerFor("song"); + this.transitionTo("other.route"); + ... + } } - } else { - // insert at end - this._operations.push(newArrayOperation); - composeIndex = arrayOperationIndex; - } - - this._composeInsert(composeIndex); - }, - - /** - Track that `count` items were removed at `index`. + }); + ``` - @method removeItems - @param index - @param count - */ - removeItems: function (index, count) { - if (count < 1) { return; } + It is also possible to call `this._super()` from within an + action handler if it overrides a handler defined on a parent + class or mixin: - var match = this._findArrayOperation(index), - arrayOperation = match.operation, - arrayOperationIndex = match.index, - arrayOperationRangeStart = match.rangeStart, - newArrayOperation, - composeIndex; + Take for example the following routes: - 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; - } + ```js + App.DebugRoute = Ember.Mixin.create({ + actions: { + debugRouteInformation: function() { + console.debug("trololo"); + } + } + }); - return this._composeDelete(composeIndex); - }, + App.AnnoyingDebugRoute = Ember.Route.extend(App.DebugRoute, { + actions: { + debugRouteInformation: function() { + // also call the debugRouteInformation of mixed in App.DebugRoute + this._super(); - /** - Apply all operations, reducing them to retain:n, for `n`, the number of - items in the array. + // show additional annoyance + window.alert(...); + } + } + }); + ``` - `callback` will be called for each operation and will be passed the following arguments: + ## Bubbling - * {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}` + By default, an action will stop bubbling once a handler defined + on the `actions` hash handles it. To continue bubbling the action, + you must return `true` from the handler: - @method apply - @param {function} callback - */ - apply: function (callback) { - var items = [], - offset = 0; + ```js + App.Router.map(function() { + this.resource("album", function() { + this.route("song"); + }); + }); - forEach(this._operations, function (arrayOperation, operationIndex) { - callback(arrayOperation.items, offset, arrayOperation.type, operationIndex); + App.AlbumRoute = Ember.Route.extend({ + actions: { + startPlaying: function() { + } + } + }); - if (arrayOperation.type !== DELETE) { - offset += arrayOperation.count; - items = items.concat(arrayOperation.items); + App.AlbumSongRoute = Ember.Route.extend({ + actions: { + startPlaying: function() { + // ... + + if (actionShouldAlsoBeTriggeredOnParentRoute) { + return true; + } + } } }); + ``` - this._operations = [new ArrayOperation(RETAIN, items.length, items)]; - }, + @property actions + @type Hash + @default null + */ /** - Return an `ArrayOperationMatch` for the operation that contains the item at `index`. - - @method _findArrayOperation + Moves `actions` to `_actions` at extend time. Note that this currently + modifies the mixin themselves, which is technically dubious but + is practically of little consequence. This may change in the future. - @param {number} index the index of the item whose operation information - should be returned. @private + @method willMergeMixin */ - _findArrayOperation: function (index) { - var arrayOperationIndex, - len, - split = false, - arrayOperation, - arrayOperationRangeStart, - arrayOperationRangeEnd; - - // 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]; + willMergeMixin: function(props) { + var hashName; - if (arrayOperation.type === DELETE) { continue; } + if (!props._actions) { + Ember.assert("'actions' should not be a function", typeof(props.actions) !== 'function'); - arrayOperationRangeEnd = arrayOperationRangeStart + arrayOperation.count - 1; + if (typeOf(props.actions) === 'object') { + hashName = 'actions'; + } else if (typeOf(props.events) === 'object') { + Ember.deprecate('Action handlers contained in an `events` object are deprecated in favor' + + ' of putting them in an `actions` object', false); + hashName = 'events'; + } - if (index === arrayOperationRangeStart) { - break; - } else if (index > arrayOperationRangeStart && index <= arrayOperationRangeEnd) { - split = true; - break; - } else { - arrayOperationRangeStart = arrayOperationRangeEnd + 1; + if (hashName) { + props._actions = merge(props._actions || {}, props[hashName]); } - } - return new ArrayOperationMatch(arrayOperation, arrayOperationIndex, split, arrayOperationRangeStart); + delete props[hashName]; + } }, - _split: function (arrayOperationIndex, splitIndex, newArrayOperation) { - var arrayOperation = this._operations[arrayOperationIndex], - splitItems = arrayOperation.items.slice(splitIndex), - splitArrayOperation = new ArrayOperation(arrayOperation.type, splitItems.length, splitItems); + /** + Triggers a named action on the `ActionHandler`. Any parameters + supplied after the `actionName` string will be passed as arguments + to the action target function. - // truncate LHS - arrayOperation.count = splitIndex; - arrayOperation.items = arrayOperation.items.slice(0, splitIndex); + If the `ActionHandler` has its `target` property set, actions may + bubble to the `target`. Bubbling happens when an `actionName` can + not be found in the `ActionHandler`'s `actions` hash or if the + action target function returns `true`. - this._operations.splice(arrayOperationIndex + 1, 0, newArrayOperation, splitArrayOperation); - }, + Example - // see SubArray for a better implementation. - _composeInsert: function (index) { - var newArrayOperation = this._operations[index], - leftArrayOperation = this._operations[index-1], // may be undefined - rightArrayOperation = this._operations[index+1], // may be undefined - leftOp = leftArrayOperation && leftArrayOperation.type, - rightOp = rightArrayOperation && rightArrayOperation.type; + ```js + App.WelcomeRoute = Ember.Route.extend({ + actions: { + playTheme: function() { + this.send('playMusic', 'theme.mp3'); + }, + playMusic: function(track) { + // ... + } + } + }); + ``` - if (leftOp === INSERT) { - // merge left - leftArrayOperation.count += newArrayOperation.count; - leftArrayOperation.items = leftArrayOperation.items.concat(newArrayOperation.items); + @method send + @param {String} actionName The action to trigger + @param {*} context a context to send with the action + */ + send: function(actionName) { + var args = [].slice.call(arguments, 1); + var target; - 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); + if (this._actions && this._actions[actionName]) { + if (this._actions[actionName].apply(this, args) === true) { + // handler returned true, so this action will bubble } else { - // only merge left - this._operations.splice(index, 1); + return; } - } else if (rightOp === INSERT) { - // merge right - newArrayOperation.count += rightArrayOperation.count; - newArrayOperation.items = newArrayOperation.items.concat(rightArrayOperation.items); - this._operations.splice(index + 1, 1); } - }, - - _composeDelete: function (index) { - var arrayOperation = this._operations[index], - deletesToGo = arrayOperation.count, - leftArrayOperation = this._operations[index-1], // may be undefined - leftOp = leftArrayOperation && leftArrayOperation.type, - nextArrayOperation, - nextOp, - nextCount, - removeNewAndNextOp = false, - removedItems = []; - if (leftOp === DELETE) { - arrayOperation = leftArrayOperation; - index -= 1; + if (target = get(this, 'target')) { + Ember.assert("The `target` for " + this + " (" + target + + ") does not have a `send` method", typeof target.send === 'function'); + target.send.apply(target, arguments); } + } + }); - for (var i = index + 1; deletesToGo > 0; ++i) { - nextArrayOperation = this._operations[i]; - nextOp = nextArrayOperation.type; - nextCount = nextArrayOperation.count; - - if (nextOp === DELETE) { - arrayOperation.count += nextCount; - continue; - } - - 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; + __exports__["default"] = ActionHandler; + }); +enifed("ember-runtime/mixins/array", + ["ember-metal/core","ember-metal/property_get","ember-metal/computed","ember-metal/is_none","ember-runtime/mixins/enumerable","ember-metal/enumerable_utils","ember-metal/mixin","ember-metal/property_events","ember-metal/events","ember-metal/watching","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-runtime + */ - // 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; + // .......................................................... + // HELPERS + // + var Ember = __dependency1__["default"]; + // ES6TODO: Ember.A - 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; - } + var get = __dependency2__.get; + var computed = __dependency3__.computed; + var cacheFor = __dependency3__.cacheFor; + var isNone = __dependency4__["default"]; + var Enumerable = __dependency5__["default"]; + var map = __dependency6__.map; + var Mixin = __dependency7__.Mixin; + var required = __dependency7__.required; + var propertyWillChange = __dependency8__.propertyWillChange; + var propertyDidChange = __dependency8__.propertyDidChange; + var addListener = __dependency9__.addListener; + var removeListener = __dependency9__.removeListener; + var sendEvent = __dependency9__.sendEvent; + var hasListeners = __dependency9__.hasListeners; + var isWatching = __dependency10__.isWatching; - if (nextOp === INSERT) { - // d:2 i:3 will result in delete going away - arrayOperation.count -= nextCount; - } - } + function arrayObserversHelper(obj, target, opts, operation, notify) { + var willChange = (opts && opts.willChange) || 'arrayWillChange'; + var didChange = (opts && opts.didChange) || 'arrayDidChange'; + var hasObservers = get(obj, 'hasArrayObservers'); - 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); - } + if (hasObservers === notify) { + propertyWillChange(obj, 'hasArrayObservers'); + } - return removedItems; - }, + operation(obj, '@array:before', target, willChange); + operation(obj, '@array:change', target, didChange); - toString: function () { - var str = ""; - forEach(this._operations, function (operation) { - str += " " + operation.type + ":" + operation.count; - }); - return str.substring(1); + if (hasObservers === notify) { + propertyDidChange(obj, 'hasArrayObservers'); } - }; - /** - Internal data structure to represent an array operation. - - @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; + return obj; } + // .......................................................... + // ARRAY + // /** - Internal data structure used to include information when looking up operations - by item index. - - @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; - } + This mixin implements Observer-friendly Array-like behavior. It is not a + concrete implementation, but it can be used up by other classes that want + to appear like arrays. - __exports__["default"] = TrackedArray; - }); -})(); + For example, ArrayProxy and ArrayController are both concrete classes that can + be instantiated to implement array-like behavior. Both of these classes use + the Array Mixin by way of the MutableArray mixin, which allows observable + changes to be made to the underlying array. -(function() { -define("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/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","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __exports__) { - "use strict"; - /** - Ember Views + Unlike `Ember.Enumerable,` this mixin defines methods specifically for + collections that provide index-ordered access to their contents. When you + are designing code that needs to accept any kind of Array-like object, you + should use these methods instead of Array primitives because these will + properly notify observers of changes to the array. - @module ember - @submodule ember-views - @requires ember-runtime - @main ember-views - */ + Although these methods are efficient, they do add a layer of indirection to + your application so it is a good idea to use them only when you need the + flexibility of using both true JavaScript arrays and "virtual" arrays such + as controllers and collections. - // BEGIN EXPORTS - Ember.$ = __dependency2__["default"]; + You can use the methods defined in this module to access and modify array + contents in a KVO-friendly way. You can also be notified whenever the + membership of an array changes by using `.observes('myArray.[]')`. - Ember.ViewTargetActionSupport = __dependency12__["default"]; - Ember.RenderBuffer = __dependency4__["default"]; + To support `Ember.Array` in your own class, you must override two + primitives to use it: `replace()` and `objectAt()`. - var ViewUtils = Ember.ViewUtils = {}; - ViewUtils.setInnerHTML = __dependency3__.setInnerHTML; - ViewUtils.isSimpleClick = __dependency3__.isSimpleClick; - - Ember.CoreView = __dependency7__.CoreView; - Ember.View = __dependency7__.View; - Ember.View.states = __dependency6__.states; - Ember.View.cloneStates = __dependency6__.cloneStates; - - Ember._ViewCollection = __dependency7__.ViewCollection; - Ember.ContainerView = __dependency8__["default"]; - Ember.CollectionView = __dependency9__["default"]; - Ember.Component = __dependency10__["default"]; - Ember.EventDispatcher = __dependency11__["default"]; - // END EXPORTS + Note that the Ember.Array mixin also incorporates the `Ember.Enumerable` + mixin. All `Ember.Array`-like objects are also enumerable. - __exports__["default"] = Ember; - }); -define("ember-views/mixins/component_template_deprecation", - ["ember-metal/core","ember-metal/property_get","ember-metal/mixin","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // Ember.deprecate - var get = __dependency2__.get; - var Mixin = __dependency3__.Mixin; + @class Array + @namespace Ember + @uses Ember.Enumerable + @since Ember 0.9.0 + */ + __exports__["default"] = Mixin.create(Enumerable, { - /** - The ComponentTemplateDeprecation mixin is used to provide a useful - deprecation warning when using either `template` or `templateName` with - a component. The `template` and `templateName` properties specified at - extend time are moved to `layout` and `layoutName` respectively. + /** + Your array must support the `length` property. Your replace methods should + set this property whenever it changes. - `Ember.ComponentTemplateDeprecation` is used internally by Ember in - `Ember.Component`. + @property {Number} length + */ + length: required(), - @class ComponentTemplateDeprecation - @namespace Ember - */ - var ComponentTemplateDeprecation = Mixin.create({ /** - @private + Returns the object at the given `index`. If the given `index` is negative + or is greater or equal than the array length, returns `undefined`. - Moves `templateName` to `layoutName` and `template` to `layout` at extend - time if a layout is not also specified. + This is one of the primitives you must implement to support `Ember.Array`. + If your object supports retrieving the value of an array item using `get()` + (i.e. `myArray.get(0)`), then you do not need to implement this method + yourself. - Note that this currently modifies the mixin themselves, which is technically - dubious but is practically of little consequence. This may change in the - future. + ```javascript + var arr = ['a', 'b', 'c', 'd']; - @method willMergeMixin - @since 1.4.0 + arr.objectAt(0); // 'a' + arr.objectAt(3); // 'd' + arr.objectAt(-1); // undefined + arr.objectAt(4); // undefined + arr.objectAt(5); // undefined + ``` + + @method objectAt + @param {Number} idx The index of the item to return. + @return {*} item at index or undefined */ - willMergeMixin: function(props) { - // must call _super here to ensure that the ActionHandler - // mixin is setup properly (moves actions -> _actions) - // - // Calling super is only OK here since we KNOW that - // there is another Mixin loaded first. - this._super.apply(this, arguments); + objectAt: function(idx) { + if (idx < 0 || idx >= get(this, 'length')) { + return undefined; + } - var deprecatedProperty, replacementProperty, - layoutSpecified = (props.layoutName || props.layout || get(this, 'layoutName')); + return get(this, idx); + }, - if (props.templateName && !layoutSpecified) { - deprecatedProperty = 'templateName'; - replacementProperty = 'layoutName'; + /** + This returns the objects at the specified indexes, using `objectAt`. - props.layoutName = props.templateName; - delete props['templateName']; - } + ```javascript + var arr = ['a', 'b', 'c', 'd']; - if (props.template && !layoutSpecified) { - deprecatedProperty = 'template'; - replacementProperty = 'layout'; + arr.objectsAt([0, 1, 2]); // ['a', 'b', 'c'] + arr.objectsAt([2, 3, 4]); // ['c', 'd', undefined] + ``` - props.layout = props.template; - delete props['template']; - } + @method objectsAt + @param {Array} indexes An array of indexes of items to return. + @return {Array} + */ + objectsAt: function(indexes) { + var self = this; - if (deprecatedProperty) { - Ember.deprecate('Do not specify ' + deprecatedProperty + ' on a Component, use ' + replacementProperty + ' instead.', false); - } - } - }); + return map(indexes, function(idx) { + return self.objectAt(idx); + }); + }, - __exports__["default"] = ComponentTemplateDeprecation; - }); -define("ember-views/mixins/view_target_action_support", - ["ember-metal/mixin","ember-runtime/mixins/target_action_support","ember-metal/computed","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var Mixin = __dependency1__.Mixin; - var TargetActionSupport = __dependency2__["default"]; + // overrides Ember.Enumerable version + nextObject: function(idx) { + return this.objectAt(idx); + }, - // ES6TODO: computed should have its own export path so you can do import {defaultTo} from computed - var computed = __dependency3__.computed; - var alias = computed.alias; + /** + This is the handler for the special array content property. If you get + this property, it will return this. If you set this property to a new + array, it will replace the current content. - /** - `Ember.ViewTargetActionSupport` is a mixin that can be included in a - view class to add a `triggerAction` method with semantics similar to - the Handlebars `{{action}}` helper. It provides intelligent defaults - for the action's target: the view's controller; and the context that is - sent with the action: the view's context. + This property overrides the default property defined in `Ember.Enumerable`. - Note: 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 custom View subclasses. + @property [] + @return this + */ + '[]': computed(function(key, value) { + if (value !== undefined) { + this.replace(0, get(this, 'length'), value); + } - For example: + return this; + }), - ```javascript - App.SaveButtonView = Ember.View.extend(Ember.ViewTargetActionSupport, { - action: 'save', - click: function() { - this.triggerAction(); // Sends the `save` action, along with the current context - // to the current controller - } - }); - ``` + firstObject: computed(function() { + return this.objectAt(0); + }), - The `action` can be provided as properties of an optional object argument - to `triggerAction` as well. + lastObject: computed(function() { + return this.objectAt(get(this, 'length') - 1); + }), - ```javascript - App.SaveButtonView = Ember.View.extend(Ember.ViewTargetActionSupport, { - click: function() { - this.triggerAction({ - action: 'save' - }); // Sends the `save` action, along with the current context - // to the current controller - } - }); - ``` + // optimized version from Enumerable + contains: function(obj) { + return this.indexOf(obj) >= 0; + }, - @class ViewTargetActionSupport - @namespace Ember - @extends Ember.TargetActionSupport - */ - var ViewTargetActionSupport = Mixin.create(TargetActionSupport, { - /** - @property target - */ - target: alias('controller'), + // Add any extra methods to Ember.Array that are native to the built-in Array. /** - @property actionContext - */ - actionContext: alias('context') - }); + Returns a new array that is a slice of the receiver. This implementation + uses the observable array methods to retrieve the objects for the new + slice. - __exports__["default"] = ViewTargetActionSupport; - }); -define("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/views/view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-views - */ - var Ember = __dependency1__["default"]; - // Ember.assert + ```javascript + var arr = ['red', 'green', 'blue']; + + arr.slice(0); // ['red', 'green', 'blue'] + arr.slice(0, 2); // ['red', 'green'] + arr.slice(1, 100); // ['green', 'blue'] + ``` + + @method slice + @param {Integer} beginIndex (Optional) index to begin slicing from. + @param {Integer} endIndex (Optional) index to end the slice at (but not included). + @return {Array} New array with specified slice + */ + slice: function(beginIndex, endIndex) { + var ret = Ember.A(); + var length = get(this, 'length'); - var get = __dependency2__.get; - var set = __dependency3__.set; - var isNone = __dependency4__.isNone; - var run = __dependency5__["default"]; - var typeOf = __dependency6__.typeOf; - var fmt = __dependency7__.fmt; - var EmberObject = __dependency8__["default"]; - var jQuery = __dependency9__["default"]; - var View = __dependency10__.View; + if (isNone(beginIndex)) { + beginIndex = 0; + } - var ActionHelper; + if (isNone(endIndex) || (endIndex > length)) { + endIndex = length; + } - //ES6TODO: - // find a better way to do Ember.View.views without global state + if (beginIndex < 0) { + beginIndex = length + beginIndex; + } - /** - `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. + if (endIndex < 0) { + endIndex = length + endIndex; + } - @class EventDispatcher - @namespace Ember - @private - @extends Ember.Object - */ - var EventDispatcher = EmberObject.extend({ + while (beginIndex < endIndex) { + ret[ret.length] = this.objectAt(beginIndex++); + } + + return ret; + }, /** - 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. + Returns the index of the given object's first occurrence. + If no `startAt` argument is given, the starting location to + search is 0. If it's negative, will count backward from + the end of the array. Returns -1 if no match is found. - This set will be modified by `setup` to also include any events added at that time. + ```javascript + var arr = ['a', 'b', 'c', 'd', 'a']; + + arr.indexOf('a'); // 0 + arr.indexOf('z'); // -1 + arr.indexOf('a', 2); // 4 + arr.indexOf('a', -1); // 4 + arr.indexOf('b', 3); // -1 + arr.indexOf('a', 100); // -1 + ``` - @property events - @type Object + @method indexOf + @param {Object} object the item to search for + @param {Number} startAt optional starting location to search, default 0 + @return {Number} index or -1 if not found */ - 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' - }, + indexOf: function(object, startAt) { + var len = get(this, 'length'); + var idx; - /** - The root DOM element to which event listeners should be attached. Event - listeners will be attached to the document unless this is overridden. + if (startAt === undefined) { + startAt = 0; + } - Can be specified as a DOMElement or a selector string. + if (startAt < 0) { + startAt += len; + } - The default body is a string since this may be evaluated before document.body - exists in the DOM. + for (idx = startAt; idx < len; idx++) { + if (this.objectAt(idx) === object) { + return idx; + } + } - @private - @property rootElement - @type DOMElement - @default 'body' - */ - rootElement: 'body', + return -1; + }, /** - Sets up event listeners for standard browser events. + Returns the index of the given object's last occurrence. + If no `startAt` argument is given, the search starts from + the last position. If it's negative, will count backward + from the end of the array. Returns -1 if no match is found. - 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 + var arr = ['a', 'b', 'c', 'd', 'a']; + + arr.lastIndexOf('a'); // 4 + arr.lastIndexOf('z'); // -1 + arr.lastIndexOf('a', 2); // 0 + arr.lastIndexOf('a', -1); // 4 + arr.lastIndexOf('b', 3); // 1 + arr.lastIndexOf('a', 100); // 4 + ``` - @private - @method setup - @param addedEvents {Hash} + @method lastIndexOf + @param {Object} object the item to search for + @param {Number} startAt optional starting location to search, default 0 + @return {Number} index or -1 if not found */ - setup: function(addedEvents, rootElement) { - var event, events = get(this, 'events'); + lastIndexOf: function(object, startAt) { + var len = get(this, 'length'); + var idx; - jQuery.extend(events, addedEvents || {}); + if (startAt === undefined || startAt >= len) { + startAt = len-1; + } + if (startAt < 0) { + startAt += len; + } - if (!isNone(rootElement)) { - set(this, 'rootElement', rootElement); + for (idx = startAt; idx >= 0; idx--) { + if (this.objectAt(idx) === object) { + return idx; + } } - rootElement = jQuery(get(this, 'rootElement')); + return -1; + }, - 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); + // .......................................................... + // ARRAY OBSERVERS + // - rootElement.addClass('ember-application'); + /** + Adds an array observer to the receiving array. The array observer object + normally must implement two methods: - 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')); + * `arrayWillChange(observedObj, start, removeCount, addCount)` - This method will be + called just before the array is modified. + * `arrayDidChange(observedObj, start, removeCount, addCount)` - This method will be + called just after the array is modified. - for (event in events) { - if (events.hasOwnProperty(event)) { - this.setupHandler(rootElement, event, events[event]); - } - } + Both callbacks will be passed the observed object, starting index of the + change as well a a count of the items to be removed and added. You can use + these callbacks to optionally inspect the array during the change, clear + caches, or do any other bookkeeping necessary. + + In addition to passing a target, you can also include an options hash + which you can use to override the method names that will be invoked on the + target. + + @method addArrayObserver + @param {Object} target The observer object. + @param {Hash} opts Optional hash of configuration options including + `willChange` and `didChange` option. + @return {Ember.Array} receiver + */ + + addArrayObserver: function(target, opts) { + return arrayObserversHelper(this, target, opts, addListener, false); }, /** - Registers an event listener on the document. If the given event is - triggered, the provided event handler will be triggered on the target view. - - 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. + Removes an array observer from the object if the observer is current + registered. Calling this method multiple times with the same object will + have no effect. - For example, to have the `mouseDown` method called on the target view when - a `mousedown` event is received from the browser, do the following: + @method removeArrayObserver + @param {Object} target The object observing the array. + @param {Hash} opts Optional hash of configuration options including + `willChange` and `didChange` option. + @return {Ember.Array} receiver + */ + removeArrayObserver: function(target, opts) { + return arrayObserversHelper(this, target, opts, removeListener, true); + }, - ```javascript - setupHandler('mousedown', 'mouseDown'); - ``` + /** + Becomes true whenever the array currently has observers watching changes + on the array. - @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 + @property {Boolean} hasArrayObservers */ - setupHandler: function(rootElement, event, eventName) { - var self = this; + hasArrayObservers: computed(function() { + return hasListeners(this, '@array:change') || hasListeners(this, '@array:before'); + }), - rootElement.on(event + '.ember', '.ember-view', function(evt, triggeringManager) { - var view = View.views[this.id], - result = true, manager = null; + /** + If you are implementing an object that supports `Ember.Array`, call this + method just before the array content changes to notify any observers and + invalidate any related properties. Pass the starting index of the change + as well as a delta of the amounts to change. - manager = self._findNearestEventManager(view, eventName); + @method arrayContentWillChange + @param {Number} startIdx The starting index in the array that will change. + @param {Number} removeAmt The number of items that will be removed. If you + pass `null` assumes 0 + @param {Number} addAmt The number of items that will be added. If you + pass `null` assumes 0. + @return {Ember.Array} receiver + */ + arrayContentWillChange: function(startIdx, removeAmt, addAmt) { + var removing, lim; - if (manager && manager !== triggeringManager) { - result = self._dispatchEvent(manager, evt, eventName, view); - } else if (view) { - result = self._bubbleEvent(view, evt, eventName); - } else { - evt.stopPropagation(); + // if no args are passed assume everything changes + if (startIdx === undefined) { + startIdx = 0; + removeAmt = addAmt = -1; + } else { + if (removeAmt === undefined) { + removeAmt = -1; } - return result; - }); + if (addAmt === undefined) { + addAmt = -1; + } + } - rootElement.on(event + '.ember', '[data-ember-action]', function(evt) { - //ES6TODO: Needed for ActionHelper (generally not available in ember-views test suite) - if (!ActionHelper) { ActionHelper = requireModule("ember-routing/helpers/action")["ActionHelper"]; }; + // Make sure the @each proxy is set up if anyone is observing @each + if (isWatching(this, '@each')) { + get(this, '@each'); + } - var actionId = jQuery(evt.currentTarget).attr('data-ember-action'), - action = ActionHelper.registeredActions[actionId]; + sendEvent(this, '@array:before', [this, startIdx, removeAmt, addAmt]); - // 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); + if (startIdx >= 0 && removeAmt >= 0 && get(this, 'hasEnumerableObservers')) { + removing = []; + lim = startIdx + removeAmt; + + for (var idx = startIdx; idx < lim; idx++) { + removing.push(this.objectAt(idx)); } - }); - }, + } else { + removing = removeAmt; + } - _findNearestEventManager: function(view, eventName) { - var manager = null; + this.enumerableContentWillChange(removing, addAmt); - while (view) { - manager = get(view, 'eventManager'); - if (manager && manager[eventName]) { break; } + return this; + }, - view = get(view, 'parentView'); - } + /** + If you are implementing an object that supports `Ember.Array`, call this + method just after the array content changes to notify any observers and + invalidate any related properties. Pass the starting index of the change + as well as a delta of the amounts to change. - return manager; - }, + @method arrayContentDidChange + @param {Number} startIdx The starting index in the array that did change. + @param {Number} removeAmt The number of items that were removed. If you + pass `null` assumes 0 + @param {Number} addAmt The number of items that were added. If you + pass `null` assumes 0. + @return {Ember.Array} receiver + */ + arrayContentDidChange: function(startIdx, removeAmt, addAmt) { + var adding, lim; - _dispatchEvent: function(object, evt, eventName, view) { - var result = true; + // if no args are passed assume everything changes + if (startIdx === undefined) { + startIdx = 0; + removeAmt = addAmt = -1; + } else { + if (removeAmt === undefined) { + removeAmt = -1; + } - 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); + if (addAmt === undefined) { + addAmt = -1; + } } - return result; - }, - - _bubbleEvent: function(view, evt, eventName) { - return run(view, view.handleEvent, eventName, evt); - }, + if (startIdx >= 0 && addAmt >= 0 && get(this, 'hasEnumerableObservers')) { + adding = []; + lim = startIdx + addAmt; - destroy: function() { - var rootElement = get(this, 'rootElement'); - jQuery(rootElement).off('.ember', '**').removeClass('ember-application'); - return this._super(); - } - }); + for (var idx = startIdx; idx < lim; idx++) { + adding.push(this.objectAt(idx)); + } + } else { + adding = addAmt; + } - __exports__["default"] = EventDispatcher; - }); -define("ember-views/system/ext", - ["ember-metal/run_loop"], - function(__dependency1__) { - "use strict"; - /** - @module ember - @submodule ember-views - */ + this.enumerableContentDidChange(removeAmt, adding); + sendEvent(this, '@array:change', [this, startIdx, removeAmt, addAmt]); - var run = __dependency1__["default"]; + var length = get(this, 'length'); + var cachedFirst = cacheFor(this, 'firstObject'); + var cachedLast = cacheFor(this, 'lastObject'); - // 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. - var queues = run.queues; - run._addQueue('render', 'actions'); - run._addQueue('afterRender', 'render'); - }); -define("ember-views/system/jquery", - ["ember-metal/core","ember-runtime/system/string","ember-metal/enumerable_utils","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // Ember.assert - var w = __dependency2__.w; + if (this.objectAt(0) !== cachedFirst) { + propertyWillChange(this, 'firstObject'); + propertyDidChange(this, 'firstObject'); + } - // ES6TODO: the functions on EnumerableUtils need their own exports - var EnumerableUtils = __dependency3__["default"]; - var forEach = EnumerableUtils.forEach; + if (this.objectAt(length-1) !== cachedLast) { + propertyWillChange(this, 'lastObject'); + propertyDidChange(this, 'lastObject'); + } - /** - Ember Views + return this; + }, - @module ember - @submodule ember-views - @requires ember-runtime - @main ember-views - */ + // .......................................................... + // ENUMERATED PROPERTIES + // - var jQuery = (Ember.imports && Ember.imports.jQuery) || (this && this.jQuery); - if (!jQuery && typeof require === 'function') { - jQuery = require('jquery'); - } + /** + Returns a special object that can be used to observe individual properties + on the array. Just get an equivalent property on this object and it will + return an enumerable that maps automatically to the named key on the + member objects. - 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)); + If you merely want to watch for any items being added or removed to the array, + use the `[]` property instead of `@each`. - /** - @module ember - @submodule ember-views - */ - if (jQuery) { - // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#dndevents - var dragEvents = w('dragstart drag dragenter dragleave dragover drop dragend'); + @property @each + */ + '@each': computed(function() { + if (!this.__each) { + // ES6TODO: GRRRRR + var EachProxy = requireModule('ember-runtime/system/each_proxy')['EachProxy']; - // 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'] }; - }); - } + this.__each = new EachProxy(this); + } - __exports__["default"] = jQuery; + return this.__each; + }) + }); }); -define("ember-views/system/render_buffer", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-views/system/utils","ember-views/system/jquery","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { +enifed("ember-runtime/mixins/comparable", + ["ember-metal/mixin","exports"], + function(__dependency1__, __exports__) { "use strict"; + var Mixin = __dependency1__.Mixin; + var required = __dependency1__.required; + /** @module ember - @submodule ember-views + @submodule ember-runtime */ - var Ember = __dependency1__["default"]; - // jQuery - - var get = __dependency2__.get; - var set = __dependency3__.set; - var setInnerHTML = __dependency4__.setInnerHTML; - var jQuery = __dependency5__["default"]; - - function ClassSet() { - this.seen = {}; - this.list = []; - }; - + /** + Implements some standard methods for comparing objects. Add this mixin to + any class you create that can compare its instances. - ClassSet.prototype = { - add: function(string) { - if (string in this.seen) { return; } - this.seen[string] = true; + You should implement the `compare()` method. - this.list.push(string); - }, + @class Comparable + @namespace Ember + @since Ember 0.9 + */ + __exports__["default"] = Mixin.create({ - toDOM: function() { - return this.list.join(" "); - } - }; + /** + Override to return the result of the comparison of the two parameters. The + compare method should return: - var BAD_TAG_NAME_TEST_REGEXP = /[^a-zA-Z0-9\-]/; - var BAD_TAG_NAME_REPLACE_REGEXP = /[^a-zA-Z0-9\-]/g; + - `-1` if `a < b` + - `0` if `a == b` + - `1` if `a > b` - function stripTagName(tagName) { - if (!tagName) { - return tagName; - } + Default implementation raises an exception. - if (!BAD_TAG_NAME_TEST_REGEXP.test(tagName)) { - return tagName; - } + @method compare + @param a {Object} the first object to compare + @param b {Object} the second object to compare + @return {Integer} the result of the comparison + */ + compare: required(Function) + }); + }); +enifed("ember-runtime/mixins/controller", + ["ember-metal/mixin","ember-metal/computed","ember-runtime/mixins/action_handler","ember-runtime/mixins/controller_content_model_alias_deprecation","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + var Mixin = __dependency1__.Mixin; + var computed = __dependency2__.computed; + var ActionHandler = __dependency3__["default"]; + var ControllerContentModelAliasDeprecation = __dependency4__["default"]; - return tagName.replace(BAD_TAG_NAME_REPLACE_REGEXP, ''); - } + /** + `Ember.ControllerMixin` provides a standard interface for all classes that + compose Ember's controller layer: `Ember.Controller`, + `Ember.ArrayController`, and `Ember.ObjectController`. - var BAD_CHARS_REGEXP = /&(?!\w+;)|[<>"'`]/g; - var POSSIBLE_CHARS_REGEXP = /[&<>"'`]/; + @class ControllerMixin + @namespace Ember + @uses Ember.ActionHandler + */ + __exports__["default"] = Mixin.create(ActionHandler, ControllerContentModelAliasDeprecation, { + /* ducktype as a controller */ + isController: true, - function escapeAttribute(value) { - // Stolen shamelessly from Handlebars + /** + The object to which actions from the view should be sent. - var escape = { - "<": "<", - ">": ">", - '"': """, - "'": "'", - "`": "`" - }; + For example, when a Handlebars template uses the `{{action}}` helper, + it will attempt to send the action to the view's controller's `target`. - var escapeChar = function(chr) { - return escape[chr] || "&"; - }; + By default, the value of the target property is set to the router, and + is injected when a controller is instantiated. This injection is defined + in Ember.Application#buildContainer, and is applied as part of the + applications initialization process. It can also be set after a controller + has been instantiated, for instance when using the render helper in a + template, or when a controller is used as an `itemController`. In most + cases the `target` property will automatically be set to the logical + consumer of actions for the controller. - var string = value.toString(); + @property target + @default null + */ + target: null, - if(!POSSIBLE_CHARS_REGEXP.test(string)) { return string; } - return string.replace(BAD_CHARS_REGEXP, escapeChar); - } + container: null, - // IE 6/7 have bugs around setting names on inputs during creation. - // From http://msdn.microsoft.com/en-us/library/ie/ms536389(v=vs.85).aspx: - // "To include the NAME attribute at run time on objects created with the createElement method, use the eTag." - var canSetNameOnInputs = (function() { - var div = document.createElement('div'), - el = document.createElement('input'); + parentController: null, - el.setAttribute('name', 'foo'); - div.appendChild(el); + store: null, - return !!div.innerHTML.match('foo'); - })(); + /** + The controller's current model. When retrieving or modifying a controller's + model, this property should be used instead of the `content` property. - /** - `Ember.RenderBuffer` gathers information regarding the view and generates the - final representation. `Ember.RenderBuffer` will generate HTML which can be pushed - to the DOM. + @property model + @public + */ + model: null, - ```javascript - var buffer = Ember.RenderBuffer('div'); - ``` + /** + @private + */ + content: computed.alias('model') - @class RenderBuffer - @namespace Ember - @constructor - @param {String} tagName tag name (such as 'div' or 'p') used for the buffer - */ - var RenderBuffer = function(tagName) { - return new _RenderBuffer(tagName); - }; + }); + }); +enifed("ember-runtime/mixins/controller_content_model_alias_deprecation", + ["ember-metal/core","ember-metal/mixin","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // Ember.deprecate + var Mixin = __dependency2__.Mixin; - var _RenderBuffer = function(tagName) { - this.tagNames = [tagName || null]; - this.buffer = ""; - }; + /** + The ControllerContentModelAliasDeprecation mixin is used to provide a useful + deprecation warning when specifying `content` directly on a `Ember.Controller` + (without also specifying `model`). - _RenderBuffer.prototype = { + Ember versions prior to 1.7 used `model` as an alias of `content`, but due to + much confusion this alias was reversed (so `content` is now an alias of `model). - // The root view's element - _element: null, + This change reduces many caveats with model/content, and also sets a + simple ground rule: Never set a controllers content, rather always set + its model and ember will do the right thing. - _hasElement: true, - /** - An internal set used to de-dupe class names when `addClass()` is - used. After each call to `addClass()`, the `classes` property - will be updated. + `Ember.ControllerContentModelAliasDeprecation` is used internally by Ember in + `Ember.Controller`. + @class ControllerContentModelAliasDeprecation + @namespace Ember + @private + @since 1.7.0 + */ + __exports__["default"] = Mixin.create({ + /** @private - @property elementClasses - @type Array - @default null - */ - elementClasses: null, - /** - Array of class names which will be applied in the class attribute. + Moves `content` to `model` at extend time if a `model` is not also specified. - You can use `setClasses()` to set this property directly. If you - use `addClass()`, it will be maintained for you. + Note that this currently modifies the mixin themselves, which is technically + dubious but is practically of little consequence. This may change in the + future. - @property classes - @type Array - @default null + @method willMergeMixin + @since 1.4.0 */ - classes: null, + willMergeMixin: function(props) { + // Calling super is only OK here since we KNOW that + // there is another Mixin loaded first. + this._super.apply(this, arguments); - /** - The id in of the element, to be applied in the id attribute. + var modelSpecified = !!props.model; - You should not set this property yourself, rather, you should use - the `id()` method of `Ember.RenderBuffer`. + if (props.content && !modelSpecified) { + props.model = props.content; + delete props['content']; - @property elementId - @type String - @default null - */ - elementId: null, + Ember.deprecate('Do not specify `content` on a Controller, use `model` instead.', false); + } + } + }); + }); +enifed("ember-runtime/mixins/copyable", + ["ember-metal/property_get","ember-metal/mixin","ember-runtime/mixins/freezable","ember-runtime/system/string","ember-metal/error","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-runtime + */ - /** - A hash keyed on the name of the attribute and whose value will be - applied to that attribute. For example, if you wanted to apply a - `data-view="Foo.bar"` property to an element, you would set the - elementAttributes hash to `{'data-view':'Foo.bar'}`. - You should not maintain this hash yourself, rather, you should use - the `attr()` method of `Ember.RenderBuffer`. + var get = __dependency1__.get; + var required = __dependency2__.required; + var Freezable = __dependency3__.Freezable; + var Mixin = __dependency2__.Mixin; + var fmt = __dependency4__.fmt; + var EmberError = __dependency5__["default"]; - @property elementAttributes - @type Hash - @default {} - */ - elementAttributes: null, - /** - A hash keyed on the name of the properties and whose value will be - applied to that property. For example, if you wanted to apply a - `checked=true` property to an element, you would set the - elementProperties hash to `{'checked':true}`. + /** + Implements some standard methods for copying an object. Add this mixin to + any object you create that can create a copy of itself. This mixin is + added automatically to the built-in array. - You should not maintain this hash yourself, rather, you should use - the `prop()` method of `Ember.RenderBuffer`. + You should generally implement the `copy()` method to return a copy of the + receiver. - @property elementProperties - @type Hash - @default {} - */ - elementProperties: null, + Note that `frozenCopy()` will only work if you also implement + `Ember.Freezable`. + @class Copyable + @namespace Ember + @since Ember 0.9 + */ + __exports__["default"] = Mixin.create({ /** - The tagname of the element an instance of `Ember.RenderBuffer` represents. - - Usually, this gets set as the first parameter to `Ember.RenderBuffer`. For - example, if you wanted to create a `p` tag, then you would call - - ```javascript - Ember.RenderBuffer('p') - ``` + Override to return a copy of the receiver. Default implementation raises + an exception. - @property elementTag - @type String - @default null + @method copy + @param {Boolean} deep if `true`, a deep copy of the object should be made + @return {Object} copy of receiver */ - elementTag: null, + copy: required(Function), /** - A hash keyed on the name of the style attribute and whose value will - be applied to that attribute. For example, if you wanted to apply a - `background-color:black;` style to an element, you would set the - elementStyle hash to `{'background-color':'black'}`. + If the object implements `Ember.Freezable`, then this will return a new + copy if the object is not frozen and the receiver if the object is frozen. - You should not maintain this hash yourself, rather, you should use - the `style()` method of `Ember.RenderBuffer`. + Raises an exception if you try to call this method on a object that does + not support freezing. - @property elementStyle - @type Hash - @default {} + You should use this method whenever you want a copy of a freezable object + since a freezable object can simply return itself without actually + consuming more memory. + + @method frozenCopy + @return {Object} copy of receiver or receiver */ - elementStyle: null, + frozenCopy: function() { + if (Freezable && Freezable.detect(this)) { + return get(this, 'isFrozen') ? this : this.copy().freeze(); + } else { + throw new EmberError(fmt("%@ does not support freezing", [this])); + } + } + }); + }); +enifed("ember-runtime/mixins/deferred", + ["ember-metal/core","ember-metal/property_get","ember-metal/mixin","ember-metal/computed","ember-runtime/ext/rsvp","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // Ember.FEATURES, Ember.Test + var get = __dependency2__.get; + var Mixin = __dependency3__.Mixin; + var computed = __dependency4__.computed; + var RSVP = __dependency5__["default"]; - /** - Nested `RenderBuffers` will set this to their parent `RenderBuffer` - instance. + /** + @module ember + @submodule ember-runtime + */ - @property parentBuffer - @type Ember._RenderBuffer - */ - parentBuffer: null, + /** + @class Deferred + @namespace Ember + */ + __exports__["default"] = Mixin.create({ /** - Adds a string of HTML to the `RenderBuffer`. + Add handlers to be called when the Deferred object is resolved or rejected. - @method push - @param {String} string HTML to push into the buffer - @chainable + @method then + @param {Function} resolve a callback function to be called when done + @param {Function} reject a callback function to be called when failed */ - push: function(string) { - this.buffer += string; - return this; + then: function(resolve, reject, label) { + var deferred, promise, entity; + + entity = this; + deferred = get(this, '_deferred'); + promise = deferred.promise; + + function fulfillmentHandler(fulfillment) { + if (fulfillment === promise) { + return resolve(entity); + } else { + return resolve(fulfillment); + } + } + + return promise.then(resolve && fulfillmentHandler, reject, label); }, /** - Adds a class to the buffer, which will be rendered to the class attribute. + Resolve a Deferred object and call any `doneCallbacks` with the given args. - @method addClass - @param {String} className Class name to add to the buffer - @chainable + @method resolve */ - addClass: function(className) { - // lazily create elementClasses - this.elementClasses = (this.elementClasses || new ClassSet()); - this.elementClasses.add(className); - this.classes = this.elementClasses.list; + resolve: function(value) { + var deferred, promise; - return this; - }, + deferred = get(this, '_deferred'); + promise = deferred.promise; - setClasses: function(classNames) { - this.elementClasses = null; - var len = classNames.length, i; - for (i = 0; i < len; i++) { - this.addClass(classNames[i]); + if (value === this) { + deferred.resolve(promise); + } else { + deferred.resolve(value); } }, /** - Sets the elementID to be used for the element. + Reject a Deferred object and call any `failCallbacks` with the given args. - @method id - @param {String} id - @chainable + @method reject */ - id: function(id) { - this.elementId = id; - return this; + reject: function(value) { + get(this, '_deferred').reject(value); }, - // duck type attribute functionality like jQuery so a render buffer - // can be used like a jQuery object in attribute binding scenarios. + _deferred: computed(function() { + Ember.deprecate('Usage of Ember.DeferredMixin or Ember.Deferred is deprecated.', this._suppressDeferredDeprecation); - /** - Adds an attribute which will be rendered to the element. + return RSVP.defer('Ember: DeferredMixin - ' + this); + }) + }); + }); +enifed("ember-runtime/mixins/enumerable", + ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/mixin","ember-metal/enumerable_utils","ember-metal/computed","ember-metal/property_events","ember-metal/events","ember-runtime/compare","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-runtime + */ - @method attr - @param {String} name The name of the attribute - @param {String} value The value to add to the attribute - @chainable - @return {Ember.RenderBuffer|String} this or the current attribute value - */ - attr: function(name, value) { - var attributes = this.elementAttributes = (this.elementAttributes || {}); + // .......................................................... + // HELPERS + // - if (arguments.length === 1) { - return attributes[name]; - } else { - attributes[name] = value; - } + var Ember = __dependency1__["default"]; + var get = __dependency2__.get; + var set = __dependency3__.set; + var apply = __dependency4__.apply; + var Mixin = __dependency5__.Mixin; + var required = __dependency5__.required; + var aliasMethod = __dependency5__.aliasMethod; + var indexOf = __dependency6__.indexOf; + var computed = __dependency7__.computed; + var propertyWillChange = __dependency8__.propertyWillChange; + var propertyDidChange = __dependency8__.propertyDidChange; + var addListener = __dependency9__.addListener; + var removeListener = __dependency9__.removeListener; + var sendEvent = __dependency9__.sendEvent; + var hasListeners = __dependency9__.hasListeners; + var compare = __dependency10__["default"]; - return this; - }, + var a_slice = Array.prototype.slice; - /** - Remove an attribute from the list of attributes to render. + var contexts = []; - @method removeAttr - @param {String} name The name of the attribute - @chainable - */ - removeAttr: function(name) { - var attributes = this.elementAttributes; - if (attributes) { delete attributes[name]; } + function popCtx() { + return contexts.length === 0 ? {} : contexts.pop(); + } - return this; - }, + function pushCtx(ctx) { + contexts.push(ctx); + return null; + } - /** - Adds a property which will be rendered to the element. + function iter(key, value) { + var valueProvided = arguments.length === 2; - @method prop - @param {String} name The name of the property - @param {String} value The value to add to the property - @chainable - @return {Ember.RenderBuffer|String} this or the current property value - */ - prop: function(name, value) { - var properties = this.elementProperties = (this.elementProperties || {}); + function i(item) { + var cur = get(item, key); + return valueProvided ? value === cur : !!cur; + } - if (arguments.length === 1) { - return properties[name]; - } else { - properties[name] = value; - } + return i; + } - return this; - }, + /** + This mixin defines the common interface implemented by enumerable objects + in Ember. Most of these methods follow the standard Array iteration + API defined up to JavaScript 1.8 (excluding language-specific features that + cannot be emulated in older versions of JavaScript). - /** - Remove an property from the list of properties to render. + This mixin is applied automatically to the Array class on page load, so you + can use any of these methods on simple arrays. If Array already implements + one of these methods, the mixin will not override them. - @method removeProp - @param {String} name The name of the property - @chainable - */ - removeProp: function(name) { - var properties = this.elementProperties; - if (properties) { delete properties[name]; } + ## Writing Your Own Enumerable - return this; - }, + To make your own custom class enumerable, you need two items: + + 1. You must have a length property. This property should change whenever + the number of items in your enumerable object changes. If you use this + with an `Ember.Object` subclass, you should be sure to change the length + property using `set().` + + 2. You must implement `nextObject().` See documentation. + + Once you have these two methods implemented, apply the `Ember.Enumerable` mixin + to your class and you will be able to enumerate the contents of your object + like any other collection. + + ## Using Ember Enumeration with Other Libraries + + Many other libraries provide some kind of iterator or enumeration like + facility. This is often where the most common API conflicts occur. + Ember's API is designed to be as friendly as possible with other + libraries by implementing only methods that mostly correspond to the + JavaScript 1.8 API. + + @class Enumerable + @namespace Ember + @since Ember 0.9 + */ + __exports__["default"] = Mixin.create({ /** - Adds a style to the style attribute which will be rendered to the element. + Implement this method to make your class enumerable. - @method style - @param {String} name Name of the style - @param {String} value - @chainable - */ - style: function(name, value) { - this.elementStyle = (this.elementStyle || {}); + This method will be call repeatedly during enumeration. The index value + will always begin with 0 and increment monotonically. You don't have to + rely on the index value to determine what object to return, but you should + always check the value and start from the beginning when you see the + requested index is 0. - this.elementStyle[name] = value; - return this; - }, + The `previousObject` is the object that was returned from the last call + to `nextObject` for the current iteration. This is a useful way to + manage iteration if you are tracing a linked list, for example. - begin: function(tagName) { - this.tagNames.push(tagName || null); - return this; - }, + Finally the context parameter will always contain a hash you can use as + a "scratchpad" to maintain any other state you need in order to iterate + properly. The context object is reused and is not reset between + iterations so make sure you setup the context with a fresh state whenever + the index parameter is 0. - pushOpeningTag: function() { - var tagName = this.currentTagName(); - if (!tagName) { return; } + Generally iterators will continue to call `nextObject` until the index + reaches the your current length-1. If you run out of data before this + time for some reason, you should simply return undefined. - if (this._hasElement && !this._element && this.buffer.length === 0) { - this._element = this.generateElement(); - return; - } + The default implementation of this method simply looks up the index. + This works great on any Array-like objects. + + @method nextObject + @param {Number} index the current index of the iteration + @param {Object} previousObject the value returned by the last call to + `nextObject`. + @param {Object} context a context object you can use to maintain state. + @return {Object} the next object in the iteration or undefined + */ + nextObject: required(Function), - var buffer = this.buffer, - id = this.elementId, - classes = this.classes, - attrs = this.elementAttributes, - props = this.elementProperties, - style = this.elementStyle, - attr, prop; + /** + Helper method returns the first object from a collection. This is usually + used by bindings and other parts of the framework to extract a single + object if the enumerable contains only one item. - buffer += '<' + stripTagName(tagName); + If you override this method, you should implement it so that it will + always return the same value each time it is called. If your enumerable + contains only one object, this method should always return that object. + If your enumerable is empty, this method should return `undefined`. - if (id) { - buffer += ' id="' + escapeAttribute(id) + '"'; - this.elementId = null; - } - if (classes) { - buffer += ' class="' + escapeAttribute(classes.join(' ')) + '"'; - this.classes = null; - this.elementClasses = null; + ```javascript + var arr = ['a', 'b', 'c']; + arr.get('firstObject'); // 'a' + + var arr = []; + arr.get('firstObject'); // undefined + ``` + + @property firstObject + @return {Object} the object or undefined + */ + firstObject: computed('[]', function() { + if (get(this, 'length') === 0) { + return undefined; } - if (style) { - buffer += ' style="'; + // handle generic enumerables + var context = popCtx(); + var ret = this.nextObject(0, null, context); - for (prop in style) { - if (style.hasOwnProperty(prop)) { - buffer += prop + ':' + escapeAttribute(style[prop]) + ';'; - } - } + pushCtx(context); - buffer += '"'; + return ret; + }), - this.elementStyle = null; - } + /** + Helper method returns the last object from a collection. If your enumerable + contains only one object, this method should always return that object. + If your enumerable is empty, this method should return `undefined`. - if (attrs) { - for (attr in attrs) { - if (attrs.hasOwnProperty(attr)) { - buffer += ' ' + attr + '="' + escapeAttribute(attrs[attr]) + '"'; - } - } + ```javascript + var arr = ['a', 'b', 'c']; + arr.get('lastObject'); // 'c' - this.elementAttributes = null; - } + var arr = []; + arr.get('lastObject'); // undefined + ``` - if (props) { - for (prop in props) { - if (props.hasOwnProperty(prop)) { - var value = props[prop]; - if (value || typeof(value) === 'number') { - if (value === true) { - buffer += ' ' + prop + '="' + prop + '"'; - } else { - buffer += ' ' + prop + '="' + escapeAttribute(props[prop]) + '"'; - } - } - } - } + @property lastObject + @return {Object} the last object or undefined + */ + lastObject: computed('[]', function() { + var len = get(this, 'length'); - this.elementProperties = null; + if (len === 0) { + return undefined; } - buffer += '>'; - this.buffer = buffer; - }, + var context = popCtx(); + var idx = 0; + var last = null; + var cur; - pushClosingTag: function() { - var tagName = this.tagNames.pop(); - if (tagName) { this.buffer += ''; } - }, + do { + last = cur; + cur = this.nextObject(idx++, last, context); + } while (cur !== undefined); - currentTagName: function() { - return this.tagNames[this.tagNames.length-1]; - }, + pushCtx(context); - generateElement: function() { - var tagName = this.tagNames.pop(), // pop since we don't need to close - id = this.elementId, - classes = this.classes, - attrs = this.elementAttributes, - props = this.elementProperties, - style = this.elementStyle, - styleBuffer = '', attr, prop, tagString; + return last; + }), - if (attrs && attrs.name && !canSetNameOnInputs) { - // IE allows passing a tag to createElement. See note on `canSetNameOnInputs` above as well. - tagString = '<'+stripTagName(tagName)+' name="'+escapeAttribute(attrs.name)+'">'; - } else { - tagString = tagName; - } + /** + Returns `true` if the passed object can be found in the receiver. The + default version will iterate through the enumerable until the object + is found. You may want to override this with a more efficient version. - var element = document.createElement(tagString), - $element = jQuery(element); + ```javascript + var arr = ['a', 'b', 'c']; - if (id) { - $element.attr('id', id); - this.elementId = null; - } - if (classes) { - $element.attr('class', classes.join(' ')); - this.classes = null; - this.elementClasses = null; - } + arr.contains('a'); // true + arr.contains('z'); // false + ``` + + @method contains + @param {Object} obj The object to search for. + @return {Boolean} `true` if object is found in enumerable. + */ + contains: function(obj) { + var found = this.find(function(item) { + return item === obj; + }); + + return found !== undefined; + }, + + /** + Iterates through the enumerable, calling the passed function on each + item. This method corresponds to the `forEach()` method defined in + JavaScript 1.6. + + The callback method you provide should have the following signature (all + parameters are optional): - if (style) { - for (prop in style) { - if (style.hasOwnProperty(prop)) { - styleBuffer += (prop + ':' + style[prop] + ';'); - } - } + ```javascript + function(item, index, enumerable); + ``` + + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. - $element.attr('style', styleBuffer); + Note that in addition to a callback, you can also pass an optional target + object that will be set as `this` on the context. This is a good way + to give your iterator function access to the current object. - this.elementStyle = null; + @method forEach + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @return {Object} receiver + */ + forEach: function(callback, target) { + if (typeof callback !== 'function') { + throw new TypeError(); } - if (attrs) { - for (attr in attrs) { - if (attrs.hasOwnProperty(attr)) { - $element.attr(attr, attrs[attr]); - } - } + var context = popCtx(); + var len = get(this, 'length'); + var last = null; - this.elementAttributes = null; + if (target === undefined) { + target = null; } - if (props) { - for (prop in props) { - if (props.hasOwnProperty(prop)) { - $element.prop(prop, props[prop]); - } - } - - this.elementProperties = null; + for(var idx = 0; idx < len; idx++) { + var next = this.nextObject(idx, last, context) ; + callback.call(target, next, idx, this); + last = next ; } - return element; + last = null ; + context = pushCtx(context); + + return this ; }, /** - @method element - @return {DOMElement} The element corresponding to the generated HTML - of this buffer - */ - element: function() { - var html = this.innerString(); - - if (html) { - this._element = setInnerHTML(this._element, html); - } + Alias for `mapBy` - return this._element; + @method getEach + @param {String} key name of the property + @return {Array} The mapped array. + */ + getEach: function(key) { + return this.mapBy(key); }, /** - Generates the HTML content for this buffer. + Sets the value on the named property for each member. This is more + efficient than using other methods defined on this helper. If the object + implements Ember.Observable, the value will be changed to `set(),` otherwise + it will be set directly. `null` objects are skipped. - @method string - @return {String} The generated HTML + @method setEach + @param {String} key The key to set + @param {Object} value The object to set + @return {Object} receiver */ - string: function() { - if (this._hasElement && this._element) { - // Firefox versions < 11 do not have support for element.outerHTML. - var thisElement = this.element(), outerHTML = thisElement.outerHTML; - if (typeof outerHTML === 'undefined') { - return jQuery('
    ').append(thisElement).html(); - } - return outerHTML; - } else { - return this.innerString(); - } + setEach: function(key, value) { + return this.forEach(function(item) { + set(item, key, value); + }); }, - innerString: function() { - return this.buffer; - } - }; + /** + Maps all of the items in the enumeration to another value, returning + a new array. This method corresponds to `map()` defined in JavaScript 1.6. - __exports__["default"] = RenderBuffer; - }); -define("ember-views/system/utils", - ["ember-metal/core","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // Ember.assert + The callback method you provide should have the following signature (all + parameters are optional): - /** - @module ember - @submodule ember-views - */ + ```javascript + function(item, index, enumerable); + ``` - /* BEGIN METAMORPH HELPERS */ + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. - // 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 ­ + It should return the mapped value. - var needsShy = typeof document !== 'undefined' && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "
    "; - testEl.firstChild.innerHTML = ""; - return testEl.firstChild.innerHTML === ''; - })(); + Note that in addition to a callback, you can also pass an optional target + object that will be set as `this` on the context. This is a good way + to give your iterator function access to the current object. - // 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'; - })(); + @method map + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @return {Array} The mapped array. + */ + map: function(callback, target) { + var ret = Ember.A(); - // Use this to find children by ID instead of using jQuery - var findChildById = function(element, id) { - if (element.getAttribute('id') === id) { return element; } + this.forEach(function(x, idx, i) { + ret[idx] = callback.call(target, x, idx,i); + }); - 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; - } + It should return `true` to include the item in the results, `false` + otherwise. - innerHTMLTags[tagName] = canSet; + Note that in addition to a callback, you can also pass an optional target + object that will be set as `this` on the context. This is a good way + to give your iterator function access to the current object. - return canSet; - }; + @method filter + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @return {Array} A filtered array. + */ + filter: function(callback, target) { + var ret = Ember.A(); - var setInnerHTML = function(element, html) { - var tagName = element.tagName; + this.forEach(function(x, idx, i) { + if (callback.call(target, x, idx, i)) { + ret.push(x); + } + }); - 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); + return ret ; + }, - var startTag = outerHTML.match(new RegExp("<"+tagName+"([^>]*)>", 'i'))[0], - endTag = ''; + /** + Returns an array with all of the items in the enumeration where the passed + function returns false for. This method is the inverse of filter(). - var wrapper = document.createElement('div'); - setInnerHTMLWithoutFix(wrapper, startTag + html + endTag); - element = wrapper.firstChild; - while (element.tagName !== tagName) { - element = element.nextSibling; - } - } + The callback method you provide should have the following signature (all + parameters are optional): - return element; - }; + ```javascript + function(item, index, enumerable); + ``` - function isSimpleClick(event) { - var modifier = event.shiftKey || event.metaKey || event.altKey || event.ctrlKey, - secondaryClick = event.which > 1; // IE9 may return undefined + - *item* is the current item in the iteration. + - *index* is the current index in the iteration + - *enumerable* is the enumerable object itself. - return !modifier && !secondaryClick; - } + It should return the a falsey value to include the item in the results. - __exports__.setInnerHTML = setInnerHTML; - __exports__.isSimpleClick = isSimpleClick; - }); -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"; + Note that in addition to a callback, you can also pass an optional target + object that will be set as "this" on the context. This is a good way + to give your iterator function access to the current object. - /** - @module ember - @submodule ember-views - */ + @method reject + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @return {Array} A rejected array. + */ + reject: function(callback, target) { + return this.filter(function() { + return !(apply(target, callback, arguments)); + }); + }, - 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"]; + /** + Returns an array with just the items with the matched property. You + can pass an optional second argument with the target value. Otherwise + this will match any property that evaluates to `true`. - /** - `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. + @method filterBy + @param {String} key the property to test + @param {*} [value] optional value to test against. + @return {Array} filtered array + */ + filterBy: function(key, value) { + return this.filter(apply(this, iter, arguments)); + }, - ## Setting content + /** + Returns an array with just the items with the matched property. You + can pass an optional second argument with the target value. Otherwise + this will match any property that evaluates to `true`. - The managed collection of objects is referenced as the `Ember.CollectionView` - instance's `content` property. + @method filterProperty + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Array} filtered array + @deprecated Use `filterBy` instead + */ + filterProperty: aliasMethod('filterBy'), - ```javascript - someItemsView = Ember.CollectionView.create({ - content: ['A', 'B','C'] - }) - ``` + /** + Returns an array with the items that do not have truthy values for + key. You can pass an optional second argument with the target value. Otherwise + this will match any property that evaluates to false. - The view for each item in the collection will have its `content` property set - to the item. + @method rejectBy + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Array} rejected array + */ + rejectBy: function(key, value) { + var exactValue = function(item) { + return get(item, key) === value; + }; - ## Specifying itemViewClass + var hasValue = function(item) { + return !!get(item, key); + }; - 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. + var use = (arguments.length === 2 ? exactValue : hasValue); - Given an empty `` and the following code: + return this.reject(use); + }, - ```javascript - someItemsView = Ember.CollectionView.create({ - classNames: ['a-collection'], - content: ['A','B','C'], - itemViewClass: Ember.View.extend({ - template: Ember.Handlebars.compile("the letter: {{view.content}}") - }) - }); + /** + Returns an array with the items that do not have truthy values for + key. You can pass an optional second argument with the target value. Otherwise + this will match any property that evaluates to false. - someItemsView.appendTo('body'); - ``` + @method rejectProperty + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Array} rejected array + @deprecated Use `rejectBy` instead + */ + rejectProperty: aliasMethod('rejectBy'), - Will result in the following HTML structure + /** + Returns the first item in the array for which the callback returns true. + This method works similar to the `filter()` method defined in JavaScript 1.6 + except that it will stop working on the array once a match is found. - ```html -
    -
    the letter: A
    -
    the letter: B
    -
    the letter: C
    -
    - ``` + The callback method you provide should have the following signature (all + parameters are optional): - ## Automatic matching of parent/child tagNames + ```javascript + function(item, index, enumerable); + ``` - 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. + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. - Given an empty `` and the following code: + It should return the `true` to include the item in the results, `false` + otherwise. - ```javascript - anUnorderedListView = Ember.CollectionView.create({ - tagName: 'ul', - content: ['A','B','C'], - itemViewClass: Ember.View.extend({ - template: Ember.Handlebars.compile("the letter: {{view.content}}") - }) - }); + Note that in addition to a callback, you can also pass an optional target + object that will be set as `this` on the context. This is a good way + to give your iterator function access to the current object. - anUnorderedListView.appendTo('body'); - ``` + @method find + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @return {Object} Found item or `undefined`. + */ + find: function(callback, target) { + var len = get(this, 'length'); - Will result in the following HTML structure + if (target === undefined) { + target = null; + } - ```html -
      -
    • the letter: A
    • -
    • the letter: B
    • -
    • the letter: C
    • -
    - ``` + var context = popCtx(); + var found = false; + var last = null; + var next, ret; - Additional `tagName` pairs can be provided by adding to - `Ember.CollectionView.CONTAINER_MAP ` + for(var idx = 0; idx < len && !found; idx++) { + next = this.nextObject(idx, last, context); - ```javascript - Ember.CollectionView.CONTAINER_MAP['article'] = 'section' - ``` + if (found = callback.call(target, next, idx, this)) { + ret = next; + } - ## Programmatic creation of child views + last = next; + } - For cases where additional customization beyond the use of a single - `itemViewClass` or `tagName` matching is required CollectionView's - `createChildView` method can be overidden: + next = last = null; + context = pushCtx(context); - ```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); - } - }); - ``` + return ret; + }, - ## Empty View + /** + Returns the first item with a property matching the passed value. You + can pass an optional second argument with the target value. Otherwise + this will match any property that evaluates to `true`. - 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. + This method works much like the more generic `find()` method. - ```javascript - aListWithNothing = Ember.CollectionView.create({ - classNames: ['nothing'] - content: null, - emptyView: Ember.View.extend({ - template: Ember.Handlebars.compile("The collection is empty") - }) - }); + @method findBy + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Object} found item or `undefined` + */ + findBy: function(key, value) { + return this.find(apply(this, iter, arguments)); + }, - aListWithNothing.appendTo('body'); - ``` + /** + Returns the first item with a property matching the passed value. You + can pass an optional second argument with the target value. Otherwise + this will match any property that evaluates to `true`. - Will result in the following HTML structure + This method works much like the more generic `find()` method. - ```html -
    -
    - The collection is empty -
    -
    - ``` + @method findProperty + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Object} found item or `undefined` + @deprecated Use `findBy` instead + */ + findProperty: aliasMethod('findBy'), - ## Adding and Removing items + /** + Returns `true` if the passed function returns true for every item in the + enumeration. This corresponds with the `every()` method in JavaScript 1.6. - 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. + The callback method you provide should have the following signature (all + parameters are optional): + ```javascript + function(item, index, enumerable); + ``` - @class CollectionView - @namespace Ember - @extends Ember.ContainerView - @since Ember 0.9 - */ - var CollectionView = ContainerView.extend({ + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. - /** - A list of items to be displayed by the `Ember.CollectionView`. + It should return the `true` or `false`. - @property content - @type Ember.Array - @default null - */ - content: null, + Note that in addition to a callback, you can also pass an optional target + object that will be set as `this` on the context. This is a good way + to give your iterator function access to the current object. - /** - This provides metadata about what kind of empty view class this - collection would like if it is being instantiated from another - system (like Handlebars) + Example Usage: - @private - @property emptyViewClass + ```javascript + if (people.every(isEngineer)) { + Paychecks.addBigBonus(); + } + ``` + + @method every + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @return {Boolean} */ - emptyViewClass: View, + every: function(callback, target) { + return !this.find(function(x, idx, i) { + return !callback.call(target, x, idx, i); + }); + }, /** - An optional view to display if content is set to an empty array. - - @property emptyView - @type Ember.View - @default null + @method everyBy + @param {String} key the property to test + @param {String} [value] optional value to test against. + @deprecated Use `isEvery` instead + @return {Boolean} */ - emptyView: null, + everyBy: aliasMethod('isEvery'), /** - @property itemViewClass - @type Ember.View - @default Ember.View + @method everyProperty + @param {String} key the property to test + @param {String} [value] optional value to test against. + @deprecated Use `isEvery` instead + @return {Boolean} */ - itemViewClass: View, + everyProperty: aliasMethod('isEvery'), /** - Setup a CollectionView + Returns `true` if the passed property resolves to `true` for all items in + the enumerable. This method is often simpler/faster than using a callback. - @method init + @method isEvery + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Boolean} + @since 1.3.0 */ - init: function() { - var ret = this._super(); - this._contentDidChange(); - return ret; + isEvery: function(key, value) { + return this.every(apply(this, iter, arguments)); }, /** - Invoked when the content property is about to change. Notifies observers that the - entire array content will change. - - @private - @method _contentWillChange - */ - _contentWillChange: beforeObserver('content', function() { - var content = this.get('content'); - - if (content) { content.removeArrayObserver(this); } - var len = content ? get(content, 'length') : 0; - this.arrayWillChange(content, 0, len); - }), + Returns `true` if the passed function returns true for any item in the + enumeration. This corresponds with the `some()` method in JavaScript 1.6. - /** - 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. + The callback method you provide should have the following signature (all + parameters are optional): - @private - @method _contentDidChange - */ - _contentDidChange: observer('content', function() { - var content = get(this, 'content'); + ```javascript + function(item, index, enumerable); + ``` - if (content) { - this._assertArrayLike(content); - content.addArrayObserver(this); - } + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. - var len = content ? get(content, 'length') : 0; - this.arrayDidChange(content, 0, null, len); - }), + It should return the `true` to include the item in the results, `false` + otherwise. - /** - Ensure that the content implements Ember.Array + Note that in addition to a callback, you can also pass an optional target + object that will be set as `this` on the context. This is a good way + to give your iterator function access to the current object. - @private - @method _assertArrayLike - */ - _assertArrayLike: function(content) { - Ember.assert(fmt("an Ember.CollectionView's content must implement Ember.Array. You passed %@", [content]), EmberArray.detect(content)); - }, + Usage Example: - /** - Removes the content and content observers. + ```javascript + if (people.any(isManager)) { + Paychecks.addBiggerBonus(); + } + ``` - @method destroy + @method any + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @return {Boolean} `true` if the passed function returns `true` for any item */ - destroy: function() { - if (!this._super()) { return; } + any: function(callback, target) { + var len = get(this, 'length'); + var context = popCtx(); + var found = false; + var last = null; + var next, idx; - var content = get(this, 'content'); - if (content) { content.removeArrayObserver(this); } + if (target === undefined) { + target = null; + } - if (this._createdEmptyView) { - this._createdEmptyView.destroy(); + for (idx = 0; idx < len && !found; idx++) { + next = this.nextObject(idx, last, context); + found = callback.call(target, next, idx, this); + last = next; } - return this; + next = last = null; + context = pushCtx(context); + return found; }, /** - Called when a mutation to the underlying content array will occur. + Returns `true` if the passed function returns true for any item in the + enumeration. This corresponds with the `some()` method in JavaScript 1.6. - This method will remove any views that are no longer in the underlying - content array. + The callback method you provide should have the following signature (all + parameters are optional): - Invokes whenever the content array itself will change. + ```javascript + function(item, index, enumerable); + ``` - @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(); - } + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. - // 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; + It should return the `true` to include the item in the results, `false` + otherwise. - len = this._childViews.length; + Note that in addition to a callback, you can also pass an optional target + object that will be set as `this` on the context. This is a good way + to give your iterator function access to the current object. - var removingAll = removedCount === len; + Usage Example: - if (removingAll) { - this.currentState.empty(this); - this.invokeRecursively(function(view) { - view.removedFromDOM = true; - }, false); + ```javascript + if (people.some(isManager)) { + Paychecks.addBiggerBonus(); } + ``` - for (idx = start + removedCount - 1; idx >= start; idx--) { - childView = childViews[idx]; - childView.destroy(); - } - }, + @method some + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @return {Boolean} `true` if the passed function returns `true` for any item + @deprecated Use `any` instead + */ + some: aliasMethod('any'), /** - Called when a mutation to the underlying content array occurs. - - 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 passed property resolves to `true` for any item in + the enumerable. This method is often simpler/faster than using a callback. - This array observer is added in `contentDidChange`. + @method isAny + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Boolean} `true` if the passed function returns `true` for any item + @since 1.3.0 + */ + isAny: function(key, value) { + return this.any(apply(this, iter, arguments)); + }, - @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 + /** + @method anyBy + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Boolean} `true` if the passed function returns `true` for any item + @deprecated Use `isAny` instead */ - arrayDidChange: function(content, start, removed, added) { - var addedViews = [], view, item, idx, len, itemViewClass, - emptyView; + anyBy: aliasMethod('isAny'), - len = content ? get(content, 'length') : 0; + /** + @method someProperty + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Boolean} `true` if the passed function returns `true` for any item + @deprecated Use `isAny` instead + */ + someProperty: aliasMethod('isAny'), - if (len) { - itemViewClass = get(this, 'itemViewClass'); + /** + This will combine the values of the enumerator into a single value. It + is a useful way to collect a summary value from an enumeration. This + corresponds to the `reduce()` method defined in JavaScript 1.8. - if ('string' === typeof itemViewClass && isGlobalPath(itemViewClass)) { - itemViewClass = get(itemViewClass) || itemViewClass; - } + The callback method you provide should have the following signature (all + parameters are optional): - Ember.assert(fmt("itemViewClass must be a subclass of Ember.View, not %@", - [itemViewClass]), - 'string' === typeof itemViewClass || View.detect(itemViewClass)); + ```javascript + function(previousValue, item, index, enumerable); + ``` - for (idx = start; idx < start+added; idx++) { - item = content.objectAt(idx); + - `previousValue` is the value returned by the last call to the iterator. + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. - view = this.createChildView(itemViewClass, { - content: item, - contentIndex: idx - }); + Return the new cumulative value. - addedViews.push(view); - } - } else { - emptyView = get(this, 'emptyView'); + In addition to the callback you can also pass an `initialValue`. An error + will be raised if you do not pass an initial value and the enumerator is + empty. - if (!emptyView) { return; } + Note that unlike the other methods, this method does not allow you to + pass a target object to set as this for the callback. It's part of the + spec. Sorry. - if ('string' === typeof emptyView && isGlobalPath(emptyView)) { - emptyView = get(emptyView) || emptyView; - } + @method reduce + @param {Function} callback The callback to execute + @param {Object} initialValue Initial value for the reduce + @param {String} reducerProperty internal use only. + @return {Object} The reduced value. + */ + reduce: function(callback, initialValue, reducerProperty) { + if (typeof callback !== 'function') { + throw new TypeError(); + } - emptyView = this.createChildView(emptyView); - addedViews.push(emptyView); - set(this, 'emptyView', emptyView); + var ret = initialValue; - if (CoreView.detect(emptyView)) { - this._createdEmptyView = emptyView; - } - } + this.forEach(function(item, i) { + ret = callback(ret, item, i, this, reducerProperty); + }, this); - this.replace(start, 0, addedViews); + return ret; }, /** - 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. - - The tag name for the view will be set to the tagName of the viewClass - passed in. + Invokes the named method on every object in the receiver that + implements it. This method corresponds to the implementation in + Prototype 1.6. - @method createChildView - @param {Class} viewClass - @param {Hash} [attrs] Attributes to add - @return {Ember.View} new instance + @method invoke + @param {String} methodName the name of the method + @param {Object...} args optional arguments to pass as well. + @return {Array} return values from calling invoke. */ - createChildView: function(view, attrs) { - view = this._super(view, attrs); - - var itemTagName = get(view, 'tagName'); + invoke: function(methodName) { + var ret = Ember.A(); + var args; - if (itemTagName === null || itemTagName === undefined) { - itemTagName = CollectionView.CONTAINER_MAP[get(this, 'tagName')]; - set(view, 'tagName', itemTagName); + if (arguments.length > 1) { + args = a_slice.call(arguments, 1); } - 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; - }); -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 Ember = __dependency1__["default"]; - // Ember.assert, Ember.Handlebars + this.forEach(function(x, idx) { + var method = x && x[methodName]; - var ComponentTemplateDeprecation = __dependency2__["default"]; - var TargetActionSupport = __dependency3__["default"]; - var View = __dependency4__.View;var get = __dependency5__.get; - var set = __dependency6__.set; - var isNone = __dependency7__.isNone; + if ('function' === typeof method) { + ret[idx] = args ? apply(x, method, args) : x[methodName](); + } + }, this); - var computed = __dependency8__.computed; + return ret; + }, - var a_slice = Array.prototype.slice; + /** + Simply converts the enumerable into a genuine array. The order is not + guaranteed. Corresponds to the method implemented by Prototype. - /** - @module ember - @submodule ember-views - */ + @method toArray + @return {Array} the enumerable as an array. + */ + toArray: function() { + var ret = Ember.A(); - /** - 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. + this.forEach(function(o, idx) { + ret[idx] = o; + }); - 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. + return ret; + }, - ```handlebars - {{app-profile person=currentUser}} - ``` + /** + Returns a copy of the array with all `null` and `undefined` elements removed. - ```handlebars - -

    {{person.title}}

    - -

    {{person.signature}}

    - ``` + ```javascript + var arr = ['a', null, 'c', undefined]; + arr.compact(); // ['a', 'c'] + ``` - 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: + @method compact + @return {Array} the array without null and undefined elements. + */ + compact: function() { + return this.filter(function(value) { + return value != null; + }); + }, - ```handlebars - {{#app-profile person=currentUser}} -

    Admin mode

    - {{! Executed in the controller's context. }} - {{/app-profile}} - ``` + /** + Returns a new enumerable that excludes the passed value. The default + implementation returns an array regardless of the receiver type unless + the receiver does not contain the value. - ```handlebars - -

    {{person.title}}

    - {{! Executed in the components context. }} - {{yield}} {{! block contents }} - ``` + ```javascript + var arr = ['a', 'b', 'a', 'c']; + arr.without('a'); // ['b', 'c'] + ``` - 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`. + @method without + @param {Object} value + @return {Ember.Enumerable} + */ + without: function(value) { + if (!this.contains(value)) { + return this; // nothing to do + } - For example, you could implement the action - `hello` for the `app-profile` component: + var ret = Ember.A(); - ```javascript - App.AppProfileComponent = Ember.Component.extend({ - actions: { - hello: function(name) { - console.log("Hello", name); + this.forEach(function(k) { + if (k !== value) { + ret[ret.length] = k; } - } - }); - ``` - - And then use it in the component's template: + }); - ```handlebars - + return ret; + }, -

    {{person.title}}

    - {{yield}} + /** + Returns a new enumerable that contains only unique values. The default + implementation returns an array regardless of the receiver type. - - ``` + ```javascript + var arr = ['a', 'a', 'b', 'b']; + arr.uniq(); // ['a', 'b'] + ``` - 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. + This only works on primitive data types, e.g. Strings, Numbers, etc. - @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] + '}}'; - } - }), + @method uniq + @return {Ember.Enumerable} + */ + uniq: function() { + var ret = Ember.A(); - init: function() { - this._super(); - set(this, 'context', this); - set(this, 'controller', this); - }, + this.forEach(function(k) { + if (indexOf(ret, k) < 0) { + ret.push(k); + } + }); - defaultLayout: function(context, options){ - Ember.Handlebars.helpers['yield'].call(context, options); + return ret; }, /** - A components template property is set by passing a block - during its invocation. It is executed within the parent context. + This property will trigger anytime the enumerable's content changes. + You can observe this property to be notified of changes to the enumerables + content. - Example: + For plain enumerables, this property is read only. `Array` overrides + this method. - ```handlebars - {{#my-component}} - // something that is run in the context - // of the parent context - {{/my-component}} - ``` + @property [] + @type Array + @return this + */ + '[]': computed(function(key, value) { + return this; + }), - Specifying a template directly to a component is deprecated without - also specifying the layout property. + // .......................................................... + // ENUMERABLE OBSERVERS + // - @deprecated - @property template + /** + Registers an enumerable observer. Must implement `Ember.EnumerableObserver` + mixin. + + @method addEnumerableObserver + @param {Object} target + @param {Hash} [opts] + @return this */ - template: computed(function(key, value) { - if (value !== undefined) { return value; } + addEnumerableObserver: function(target, opts) { + var willChange = (opts && opts.willChange) || 'enumerableWillChange'; + var didChange = (opts && opts.didChange) || 'enumerableDidChange'; + var hasObservers = get(this, 'hasEnumerableObservers'); + + if (!hasObservers) { + propertyWillChange(this, 'hasEnumerableObservers'); + } - var templateName = get(this, 'templateName'), - template = this.templateForName(templateName, 'template'); + addListener(this, '@enumerable:before', target, willChange); + addListener(this, '@enumerable:change', target, didChange); - Ember.assert("You specified the templateName " + templateName + " for " + this + ", but it did not exist.", !templateName || template); + if (!hasObservers) { + propertyDidChange(this, 'hasEnumerableObservers'); + } - return template || get(this, 'defaultTemplate'); - }).property('templateName'), + return this; + }, /** - Specifying a components `templateName` is deprecated without also - providing the `layout` or `layoutName` properties. + Removes a registered enumerable observer. - @deprecated - @property templateName + @method removeEnumerableObserver + @param {Object} target + @param {Hash} [opts] + @return this */ - templateName: null, - - // during render, isolate keywords - cloneKeywords: function() { - return { - view: this, - controller: this - }; - }, + removeEnumerableObserver: function(target, opts) { + var willChange = (opts && opts.willChange) || 'enumerableWillChange'; + var didChange = (opts && opts.didChange) || 'enumerableDidChange'; + var hasObservers = get(this, 'hasEnumerableObservers'); - _yield: function(context, options) { - var view = options.data.view, - parentView = this._parentView, - template = get(this, 'template'); + if (hasObservers) { + propertyWillChange(this, 'hasEnumerableObservers'); + } - if (template) { - Ember.assert("A Component must have a parent view in order to yield.", parentView); + removeListener(this, '@enumerable:before', target, willChange); + removeListener(this, '@enumerable:change', target, didChange); - view.appendChild(View, { - isVirtual: true, - tagName: '', - _contextView: parentView, - template: template, - context: get(parentView, 'context'), - controller: get(parentView, 'controller'), - templateData: { keywords: parentView.cloneKeywords() } - }); + if (hasObservers) { + propertyDidChange(this, 'hasEnumerableObservers'); } + + return this; }, /** - If the component is currently inserted into the DOM of a parent view, this - property will point to the controller of the parent view. + Becomes true whenever the array currently has observers watching changes + on the array. - @property targetObject - @type Ember.Controller - @default null + @property hasEnumerableObservers + @type Boolean */ - targetObject: computed(function(key) { - var parentView = get(this, '_parentView'); - return parentView ? get(parentView, 'controller') : null; - }).property('_parentView'), + hasEnumerableObservers: computed(function() { + return hasListeners(this, '@enumerable:change') || hasListeners(this, '@enumerable:before'); + }), - /** - Triggers a named action on the controller context where the component is used if - this controller has registered for notifications of the action. - 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: + /** + Invoke this method just before the contents of your enumerable will + change. You can either omit the parameters completely or pass the objects + to be removed or added if available or just a count. + @method enumerableContentWillChange + @param {Ember.Enumerable|Number} removing An enumerable of the objects to + be removed or the number of items to be removed. + @param {Ember.Enumerable|Number} adding An enumerable of the objects to be + added or the number of items to be added. + @chainable + */ + enumerableContentWillChange: function(removing, adding) { + var removeCnt, addCnt, hasDelta; - ```javascript - App.PlayButtonComponent = Ember.Component.extend({ - click: function(){ - if (this.get('isPlaying')) { - this.sendAction('play'); - } else { - this.sendAction('stop'); - } - } - }); - ``` + if ('number' === typeof removing) { + removeCnt = removing; + } else if (removing) { + removeCnt = get(removing, 'length'); + } else { + removeCnt = removing = -1; + } - When used inside a template these component actions are configured to - trigger actions in the outer application context: + if ('number' === typeof adding) { + addCnt = adding; + } else if (adding) { + addCnt = get(adding,'length'); + } else { + addCnt = adding = -1; + } - ```handlebars - {{! application.hbs }} - {{play-button play="musicStarted" stop="musicStopped"}} - ``` + hasDelta = addCnt < 0 || removeCnt < 0 || addCnt - removeCnt !== 0; - 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: + if (removing === -1) { + removing = null; + } + if (adding === -1) { + adding = null; + } - ```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 - } - } - }); - ``` + propertyWillChange(this, '[]'); - If no action name is passed to `sendAction` a default name of "action" - is assumed. + if (hasDelta) { + propertyWillChange(this, 'length'); + } - ```javascript - App.NextButtonComponent = Ember.Component.extend({ - click: function(){ - this.sendAction(); - } - }); - ``` + sendEvent(this, '@enumerable:before', [this, removing, adding]); - ```handlebars - {{! application.hbs }} - {{next-button action="playNextSongInAlbum"}} - ``` + return this; + }, - ```javascript - App.ApplicationController = Ember.Controller.extend({ - actions: { - playNextSongInAlbum: function(){ - ... - } - } - }); - ``` + /** + Invoke this method when the contents of your enumerable has changed. + This will notify any observers watching for content changes. If you are + implementing an ordered enumerable (such as an array), also pass the + start and end values where the content changed so that it can be used to + notify range observers. - @method sendAction - @param [action] {String} the action to trigger - @param [context] {*} a context to send with the action + @method enumerableContentDidChange + @param {Ember.Enumerable|Number} removing An enumerable of the objects to + be removed or the number of items to be removed. + @param {Ember.Enumerable|Number} adding An enumerable of the objects to + be added or the number of items to be added. + @chainable */ - sendAction: function(action) { - var actionName, - contexts = a_slice.call(arguments, 1); + enumerableContentDidChange: function(removing, adding) { + var removeCnt, addCnt, hasDelta; - // 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'); + if ('number' === typeof removing) { + removeCnt = removing; + } else if (removing) { + removeCnt = get(removing, 'length'); } 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'); + removeCnt = removing = -1; } - // If no action name for that action could be found, just abort. - if (actionName === undefined) { return; } - - this.triggerAction({ - action: actionName, - actionContext: contexts - }); - } - }); - - __exports__["default"] = Component; - }); -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 + if ('number' === typeof adding) { + addCnt = adding; + } else if (adding) { + addCnt = get(adding, 'length'); + } else { + addCnt = adding = -1; + } - var merge = __dependency2__["default"]; - var MutableArray = __dependency3__["default"]; - var get = __dependency4__.get; - var set = __dependency5__.set; + hasDelta = addCnt < 0 || removeCnt < 0 || addCnt - removeCnt !== 0; - var View = __dependency6__.View; - var ViewCollection = __dependency6__.ViewCollection; - var cloneStates = __dependency7__.cloneStates; - var EmberViewStates = __dependency7__.states; + if (removing === -1) { + removing = null; + } - var EmberError = __dependency8__["default"]; + if (adding === -1) { + adding = null; + } - // ES6TODO: functions on EnumerableUtils should get their own export - var EnumerableUtils = __dependency9__["default"]; - var forEach = EnumerableUtils.forEach; + sendEvent(this, '@enumerable:change', [this, removing, adding]); - 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; + if (hasDelta) { + propertyDidChange(this, 'length'); + } - /** - @module ember - @submodule ember-views - */ + propertyDidChange(this, '[]'); - var states = cloneStates(EmberViewStates); + return this ; + }, - /** - A `ContainerView` is an `Ember.View` subclass that implements `Ember.MutableArray` - allowing programmatic management of its child views. + /** + Converts the enumerable into an array and sorts by the keys + specified in the argument. - ## Setting Initial Child Views + You may provide multiple arguments to sort by multiple properties. - 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`: + @method sortBy + @param {String} property name(s) to sort on + @return {Array} The sorted array. + @since 1.2.0 + */ + sortBy: function() { + var sortKeys = arguments; - ```javascript - aContainer = Ember.ContainerView.create({ - childViews: [Ember.View.create(), Ember.View.create()] - }); - ``` + return this.toArray().sort(function(a, b) { + for(var i = 0; i < sortKeys.length; i++) { + var key = sortKeys[i]; + var propA = get(a, key); + var propB = get(b, key); + // return 1 or -1 else continue to the next sortKey + var compareValue = compare(propA, propB); - You can also provide a list of property names whose values are instances of - `Ember.View`: + if (compareValue) { + return compareValue; + } + } + return 0; + }); + } + }); + }); +enifed("ember-runtime/mixins/evented", + ["ember-metal/mixin","ember-metal/events","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var Mixin = __dependency1__.Mixin; + var addListener = __dependency2__.addListener; + var removeListener = __dependency2__.removeListener; + var hasListeners = __dependency2__.hasListeners; + var sendEvent = __dependency2__.sendEvent; - ```javascript - aContainer = Ember.ContainerView.create({ - childViews: ['aView', 'bView', 'cView'], - aView: Ember.View.create(), - bView: Ember.View.create(), - cView: Ember.View.create() - }); - ``` + /** + @module ember + @submodule ember-runtime + */ - The two strategies can be combined: + /** + This mixin allows for Ember objects to subscribe to and emit events. ```javascript - aContainer = Ember.ContainerView.create({ - childViews: ['aView', Ember.View.create()], - aView: Ember.View.create() + App.Person = Ember.Object.extend(Ember.Evented, { + greet: function() { + // ... + this.trigger('greet'); + } }); - ``` - - 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. - - ## Adding and Removing Child Views - - The container view implements `Ember.MutableArray` allowing programmatic management of its child views. - - To remove a view, pass that view into a `removeObject` call on the container view. - Given an empty `` the following code + var person = App.Person.create(); - ```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") - }) + person.on('greet', function() { + console.log('Our person has greeted'); }); - aContainer.appendTo('body'); - ``` - - Results in the HTML - - ```html -
    -
    A
    -
    B
    -
    - ``` - - Removing a view - - ```javascript - aContainer.toArray(); // [aContainer.aView, aContainer.bView] - aContainer.removeObject(aContainer.get('bView')); - aContainer.toArray(); // [aContainer.aView] - ``` - - Will result in the following HTML + person.greet(); - ```html -
    -
    A
    -
    + // outputs: 'Our person has greeted' ``` - Similarly, adding a child view is accomplished by adding `Ember.View` instances to the - container view. - - Given an empty `` the following code + You can also chain multiple event subscriptions: ```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") - }) - }); - - aContainer.appendTo('body'); + person.on('greet', function() { + console.log('Our person has greeted'); + }).one('greet', function() { + console.log('Offer one-time special'); + }).off('event', this, forgetThis); ``` - Results in the HTML - - ```html -
    -
    A
    -
    B
    -
    - ``` + @class Evented + @namespace Ember + */ + __exports__["default"] = Mixin.create({ - Adding a view + /** + Subscribes to a named event with given function. - ```javascript - AnotherViewClass = Ember.View.extend({ - template: Ember.Handlebars.compile("Another view") - }); + ```javascript + person.on('didLoad', function() { + // fired once the person has loaded + }); + ``` - aContainer.toArray(); // [aContainer.aView, aContainer.bView] - aContainer.pushObject(AnotherViewClass.create()); - aContainer.toArray(); // [aContainer.aView, aContainer.bView, ] - ``` + An optional target can be passed in as the 2nd argument that will + be set as the "this" for the callback. This is a good way to give your + function access to the object triggering the event. When the target + parameter is used the callback becomes the third argument. - Will result in the following HTML + @method on + @param {String} name The name of the event + @param {Object} [target] The "this" binding for the callback + @param {Function} method The callback to execute + @return this + */ + on: function(name, target, method) { + addListener(this, name, target, method); + return this; + }, - ```html -
    -
    A
    -
    B
    -
    Another view
    -
    - ``` + /** + Subscribes a function to a named event and then cancels the subscription + after the first time the event is triggered. It is good to use ``one`` when + you only care about the first time an event has taken place. - ## Templates and Layout + This function takes an optional 2nd argument that will become the "this" + value for the callback. If this argument is passed then the 3rd argument + becomes the function. - 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. + @method one + @param {String} name The name of the event + @param {Object} [target] The "this" binding for the callback + @param {Function} method The callback to execute + @return this + */ + one: function(name, target, method) { + if (!method) { + method = target; + target = null; + } - @class ContainerView - @namespace Ember - @extends Ember.View - */ - var ContainerView = View.extend(MutableArray, { - states: states, + addListener(this, name, target, method, true); + return this; + }, - init: function() { - this._super(); + /** + Triggers a named event for the object. Any additional arguments + will be passed as parameters to the functions that are subscribed to the + event. - var childViews = get(this, 'childViews'); + ```javascript + person.on('didEat', function(food) { + console.log('person ate some ' + food); + }); - // redefine view's childViews property that was obliterated - defineProperty(this, 'childViews', View.childViewsProperty); + person.trigger('didEat', 'broccoli'); - var _childViews = this._childViews; + // outputs: person ate some broccoli + ``` + @method trigger + @param {String} name The name of the event + @param {Object...} args Optional arguments to pass on + */ + trigger: function(name) { + var length = arguments.length; + var args = new Array(length - 1); - forEach(childViews, function(viewName, idx) { - var view; + for (var i = 1; i < length; i++) { + args[i - 1] = arguments[i]; + } - if ('string' === typeof viewName) { - view = get(this, viewName); - view = this.createChildView(view); - set(this, viewName, view); - } else { - view = this.createChildView(viewName); - } + sendEvent(this, name, args); + }, - _childViews[idx] = view; - }, this); + /** + Cancels subscription for given name, target, and method. - var currentView = get(this, 'currentView'); - if (currentView) { - if (!_childViews.length) { _childViews = this._childViews = this._childViews.slice(); } - _childViews.push(this.createChildView(currentView)); - } + @method off + @param {String} name The name of the event + @param {Object} target The target of the subscription + @param {Function} method The function of the subscription + @return this + */ + off: function(name, target, method) { + removeListener(this, name, target, method); + return this; }, - 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; })); + /** + Checks to see if object has any subscriptions for named event. - this.arrayContentWillChange(idx, removedCount, addedCount); - this.childViewsWillChange(this._childViews, idx, removedCount); + @method has + @param {String} name The name of the event + @return {Boolean} does the object have a subscription for event + */ + has: function(name) { + return hasListeners(this, name); + } + }); + }); +enifed("ember-runtime/mixins/freezable", + ["ember-metal/mixin","ember-metal/property_get","ember-metal/property_set","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-runtime + */ - 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); - } + var Mixin = __dependency1__.Mixin; + var get = __dependency2__.get; + var set = __dependency3__.set; + + /** + The `Ember.Freezable` mixin implements some basic methods for marking an + object as frozen. Once an object is frozen it should be read only. No changes + may be made the internal state of the object. - this.arrayContentDidChange(idx, removedCount, addedCount); - this.childViewsDidChange(this._childViews, idx, removedCount, addedCount); + ## Enforcement - return this; - }, + To fully support freezing in your subclass, you must include this mixin and + override any method that might alter any property on the object to instead + raise an exception. You can check the state of an object by checking the + `isFrozen` property. - objectAt: function(idx) { - return this._childViews[idx]; - }, + Although future versions of JavaScript may support language-level freezing + object objects, that is not the case today. Even if an object is freezable, + it is still technically possible to modify the object, even though it could + break other parts of your application that do not expect a frozen object to + change. It is, therefore, very important that you always respect the + `isFrozen` property on all freezable objects. - length: computed(function () { - return this._childViews.length; - }).volatile(), + ## Example Usage - /** - Instructs each child view to render to the passed render buffer. + The example below shows a simple object that implement the `Ember.Freezable` + protocol. - @private - @method render - @param {Ember.RenderBuffer} buffer the buffer to render to - */ - render: function(buffer) { - this.forEachChildView(function(view) { - view.renderToBuffer(buffer); - }); - }, + ```javascript + Contact = Ember.Object.extend(Ember.Freezable, { + firstName: null, + lastName: null, - instrumentName: 'container', + // swaps the names + swapNames: function() { + if (this.get('isFrozen')) throw Ember.FROZEN_ERROR; + var tmp = this.get('firstName'); + this.set('firstName', this.get('lastName')); + this.set('lastName', tmp); + return this; + } - /** - When a child view is removed, destroy its element so that - it is removed from the DOM. + }); - The array observer that triggers this action is set up in the - `renderToBuffer` method. + c = Contact.create({ firstName: "John", lastName: "Doe" }); + c.swapNames(); // returns c + c.freeze(); + c.swapNames(); // EXCEPTION + ``` - @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'); + ## Copying - 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); - } - }, + Usually the `Ember.Freezable` protocol is implemented in cooperation with the + `Ember.Copyable` protocol, which defines a `frozenCopy()` method that will + return a frozen object, if the object implements this method as well. - removeChild: function(child) { - this.removeObject(child); - return this; - }, + @class Freezable + @namespace Ember + @since Ember 0.9 + */ + var Freezable = Mixin.create({ /** - When a child view is added, make sure the DOM gets updated appropriately. - - 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. + Set to `true` when the object is frozen. Use this property to detect + whether your object is frozen or not. - @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 + @property isFrozen + @type Boolean */ - 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'); - }, - - initializeViews: function(views, parentView, templateData) { - forEach(views, function(view) { - set(view, '_parentView', parentView); + isFrozen: false, - if (!view.container && parentView) { - set(view, 'container', parentView.container); - } + /** + Freezes the object. Once this method has been called the object should + no longer allow any properties to be edited. - if (!get(view, 'templateData')) { - set(view, 'templateData', templateData); - } - }); - }, + @method freeze + @return {Object} receiver + */ + freeze: function() { + if (get(this, 'isFrozen')) return this; + set(this, 'isFrozen', true); + return this; + } - currentView: null, + }); + __exports__.Freezable = Freezable; + var FROZEN_ERROR = "Frozen object cannot be modified."; + __exports__.FROZEN_ERROR = FROZEN_ERROR; + }); +enifed("ember-runtime/mixins/mutable_array", + ["ember-metal/property_get","ember-metal/utils","ember-metal/error","ember-metal/mixin","ember-runtime/mixins/array","ember-runtime/mixins/mutable_enumerable","ember-runtime/mixins/enumerable","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-runtime + */ - _currentViewWillChange: beforeObserver('currentView', function() { - var currentView = get(this, 'currentView'); - if (currentView) { - currentView.destroy(); - } - }), - _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); - } - }), + // require('ember-runtime/mixins/array'); + // require('ember-runtime/mixins/mutable_enumerable'); - _ensureChildrenAreInDOM: function () { - this.currentState.ensureChildrenAreInDOM(this); - } - }); + // .......................................................... + // CONSTANTS + // - merge(states._default, { - childViewsWillChange: Ember.K, - childViewsDidChange: Ember.K, - ensureChildrenAreInDOM: Ember.K - }); + var OUT_OF_RANGE_EXCEPTION = "Index out of range"; + var EMPTY = []; - merge(states.inBuffer, { - childViewsDidChange: function(parentView, views, start, added) { - throw new EmberError('You cannot modify child views while in the inBuffer state'); - } - }); + // .......................................................... + // HELPERS + // - merge(states.hasElement, { - childViewsWillChange: function(view, views, start, removed) { - for (var i=start; i= length, then append to the end of the array. + @param {Number} amt Number of elements that should be removed from + the array, starting at *idx*. + @param {Array} objects An array of zero or more objects that should be + inserted into the array at *idx* + */ + replace: required(), - if (previous) { - previous.domManager.after(previous, buffer.string()); - } else { - view.domManager.prepend(view, buffer.string()); - } + /** + Remove all elements from the array. This is useful if you + want to reuse an existing array without having to recreate it. - viewCollection.forEach(function(v) { - v.transitionTo('inDOM'); - v.propertyDidChange('element'); - v.triggerRecursively('didInsertElement'); - }); - } + ```javascript + var colors = ["red", "green", "blue"]; + color.length(); // 3 + colors.clear(); // [] + colors.length(); // 0 + ``` + @method clear + @return {Ember.Array} An empty Array. + */ + clear: function () { + var len = get(this, 'length'); + if (len === 0) return this; + this.replace(0, len, EMPTY); + return this; + }, - __exports__["default"] = ContainerView; - }); -define("ember-views/views/states", - ["ember-metal/platform","ember-metal/merge","ember-views/views/states/default","ember-views/views/states/pre_render","ember-views/views/states/in_buffer","ember-views/views/states/has_element","ember-views/views/states/in_dom","ember-views/views/states/destroying","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { - "use strict"; - var create = __dependency1__.create; - var merge = __dependency2__["default"]; - var _default = __dependency3__["default"]; - var preRender = __dependency4__["default"]; - var inBuffer = __dependency5__["default"]; - var hasElement = __dependency6__["default"]; - var inDOM = __dependency7__["default"]; - var destroying = __dependency8__["default"]; + /** + This will use the primitive `replace()` method to insert an object at the + specified index. - function cloneStates(from) { - var into = {}; + ```javascript + var colors = ["red", "green", "blue"]; + colors.insertAt(2, "yellow"); // ["red", "green", "yellow", "blue"] + colors.insertAt(5, "orange"); // Error: Index out of range + ``` - into._default = {}; - into.preRender = create(into._default); - into.destroying = create(into._default); - into.inBuffer = create(into._default); - into.hasElement = create(into._default); - into.inDOM = create(into.hasElement); + @method insertAt + @param {Number} idx index of insert the object at. + @param {Object} object object to insert + @return {Ember.Array} receiver + */ + insertAt: function(idx, object) { + if (idx > get(this, 'length')) throw new EmberError(OUT_OF_RANGE_EXCEPTION); + this.replace(idx, 0, [object]); + return this; + }, - for (var stateName in from) { - if (!from.hasOwnProperty(stateName)) { continue; } - merge(into[stateName], from[stateName]); - } + /** + Remove an object at the specified index using the `replace()` primitive + method. You can pass either a single index, or a start and a length. - return into; - }; + If you pass a start and length that is beyond the + length this method will throw an `OUT_OF_RANGE_EXCEPTION`. - var states = { - _default: _default, - preRender: preRender, - inDOM: inDOM, - inBuffer: inBuffer, - hasElement: hasElement, - destroying: destroying - }; + ```javascript + var colors = ["red", "green", "blue", "yellow", "orange"]; + colors.removeAt(0); // ["green", "blue", "yellow", "orange"] + colors.removeAt(2, 2); // ["green", "blue"] + colors.removeAt(4, 2); // Error: Index out of range + ``` - __exports__.cloneStates = cloneStates; - __exports__.states = states; - }); -define("ember-views/views/states/default", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-metal/error","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // Ember.K - var get = __dependency2__.get; - var set = __dependency3__.set; - var run = __dependency4__["default"]; - var EmberError = __dependency5__["default"]; + @method removeAt + @param {Number} start index, start of range + @param {Number} len length of passing range + @return {Ember.Array} receiver + */ + removeAt: function(start, len) { + if ('number' === typeof start) { - /** - @module ember - @submodule ember-views - */ - var _default = { - // appendChild is only legal while rendering the buffer. - appendChild: function() { - throw new EmberError("You can't use appendChild outside of the rendering process"); - }, + if ((start < 0) || (start >= get(this, 'length'))) { + throw new EmberError(OUT_OF_RANGE_EXCEPTION); + } - $: function() { - return undefined; - }, + // fast case + if (len === undefined) len = 1; + this.replace(start, len, EMPTY); + } - getElement: function() { - return null; + return this; }, - // Handle events from `Ember.EventDispatcher` - handleEvent: function() { - return true; // continue event propagation - }, + /** + Push the object onto the end of the array. Works just like `push()` but it + is KVO-compliant. - destroyElement: function(view) { - set(view, 'element', null); - if (view._scheduledInsert) { - run.cancel(view._scheduledInsert); - view._scheduledInsert = null; - } - return view; - }, + ```javascript + var colors = ["red", "green"]; + colors.pushObject("black"); // ["red", "green", "black"] + colors.pushObject(["yellow"]); // ["red", "green", ["yellow"]] + ``` - renderToBufferIfNeeded: function () { - return false; + @method pushObject + @param {*} obj object to push + @return object same object passed as a param + */ + pushObject: function(obj) { + this.insertAt(get(this, 'length'), obj); + return obj; }, - rerender: Ember.K, - invokeObserver: Ember.K - }; + /** + Add the objects in the passed numerable to the end of the array. Defers + notifying observers of the change until all objects are added. - __exports__["default"] = _default; - }); -define("ember-views/views/states/destroying", - ["ember-metal/merge","ember-metal/platform","ember-runtime/system/string","ember-views/views/states/default","ember-metal/error","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { - "use strict"; - var merge = __dependency1__["default"]; - var create = __dependency2__.create; - var fmt = __dependency3__.fmt; - var _default = __dependency4__["default"]; - var EmberError = __dependency5__["default"]; - /** - @module ember - @submodule ember-views - */ + ```javascript + var colors = ["red"]; + colors.pushObjects(["yellow", "orange"]); // ["red", "yellow", "orange"] + ``` - var destroyingError = "You can't call %@ on a view being destroyed"; + @method pushObjects + @param {Ember.Enumerable} objects the objects to add + @return {Ember.Array} receiver + */ + pushObjects: function(objects) { + if (!(Enumerable.detect(objects) || isArray(objects))) { + throw new TypeError("Must pass Ember.Enumerable to Ember.MutableArray#pushObjects"); + } + this.replace(get(this, 'length'), 0, objects); + return this; + }, - var destroying = create(_default); + /** + Pop object from array or nil if none are left. Works just like `pop()` but + it is KVO-compliant. - merge(destroying, { - appendChild: function() { - throw new EmberError(fmt(destroyingError, ['appendChild'])); - }, - rerender: function() { - throw new EmberError(fmt(destroyingError, ['rerender'])); - }, - destroyElement: function() { - throw new EmberError(fmt(destroyingError, ['destroyElement'])); - }, - empty: function() { - throw new EmberError(fmt(destroyingError, ['empty'])); - }, + ```javascript + var colors = ["red", "green", "blue"]; + colors.popObject(); // "blue" + console.log(colors); // ["red", "green"] + ``` - setElement: function() { - throw new EmberError(fmt(destroyingError, ["set('element', ...)"])); - }, + @method popObject + @return object + */ + popObject: function() { + var len = get(this, 'length'); + if (len === 0) return null; - renderToBufferIfNeeded: function() { - return false; + var ret = this.objectAt(len-1); + this.removeAt(len-1, 1); + return ret; }, - // Since element insertion is scheduled, don't do anything if - // the view has been destroyed between scheduling and execution - insertElement: Ember.K - }); + /** + Shift an object from start of array or nil if none are left. Works just + like `shift()` but it is KVO-compliant. - __exports__["default"] = destroying; - }); -define("ember-views/views/states/has_element", - ["ember-views/views/states/default","ember-metal/run_loop","ember-metal/merge","ember-metal/platform","ember-views/system/jquery","ember-metal/error","ember-metal/property_get","ember-metal/property_set","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { - "use strict"; - var _default = __dependency1__["default"]; - var run = __dependency2__["default"]; - var merge = __dependency3__["default"]; - var create = __dependency4__.create; - var jQuery = __dependency5__["default"]; - var EmberError = __dependency6__["default"]; + ```javascript + var colors = ["red", "green", "blue"]; + colors.shiftObject(); // "red" + console.log(colors); // ["green", "blue"] + ``` - /** - @module ember - @submodule ember-views - */ + @method shiftObject + @return object + */ + shiftObject: function() { + if (get(this, 'length') === 0) return null; + var ret = this.objectAt(0); + this.removeAt(0); + return ret; + }, - var get = __dependency7__.get; - var set = __dependency8__.set; + /** + Unshift an object to start of array. Works just like `unshift()` but it is + KVO-compliant. - var hasElement = create(_default); + ```javascript + var colors = ["red"]; + colors.unshiftObject("yellow"); // ["yellow", "red"] + colors.unshiftObject(["black"]); // [["black"], "yellow", "red"] + ``` - merge(hasElement, { - $: function(view, sel) { - var elem = get(view, 'element'); - return sel ? jQuery(sel, elem) : jQuery(elem); + @method unshiftObject + @param {*} obj object to unshift + @return object same object passed as a param + */ + unshiftObject: function(obj) { + this.insertAt(0, obj); + return obj; }, - getElement: function(view) { - var parent = get(view, 'parentView'); - if (parent) { parent = get(parent, 'element'); } - if (parent) { return view.findElementInParentElement(parent); } - return jQuery("#" + get(view, 'elementId'))[0]; + /** + Adds the named objects to the beginning of the array. Defers notifying + observers until all objects have been added. + + ```javascript + var colors = ["red"]; + colors.unshiftObjects(["black", "white"]); // ["black", "white", "red"] + colors.unshiftObjects("yellow"); // Type Error: 'undefined' is not a function + ``` + + @method unshiftObjects + @param {Ember.Enumerable} objects the objects to add + @return {Ember.Array} receiver + */ + unshiftObjects: function(objects) { + this.replace(0, 0, objects); + return this; }, - setElement: function(view, value) { - if (value === null) { - view.transitionTo('preRender'); - } else { - throw new EmberError("You cannot set an element to a non-null value when the element is already in the DOM."); - } + /** + Reverse objects in the array. Works just like `reverse()` but it is + KVO-compliant. - return value; + @method reverseObjects + @return {Ember.Array} receiver + */ + reverseObjects: function() { + var len = get(this, 'length'); + if (len === 0) return this; + var objects = this.toArray().reverse(); + this.replace(0, len, objects); + return this; }, - // once the view has been inserted into the DOM, rerendering is - // deferred to allow bindings to synchronize. - rerender: function(view) { - view.triggerRecursively('willClearRender'); + /** + Replace all the receiver's content with content of the argument. + If argument is an empty array receiver will be cleared. - view.clearRenderedChildren(); + ```javascript + var colors = ["red", "green", "blue"]; + colors.setObjects(["black", "white"]); // ["black", "white"] + colors.setObjects([]); // [] + ``` - view.domManager.replace(view); - return view; + @method setObjects + @param {Ember.Array} objects array whose content will be used for replacing + the content of the receiver + @return {Ember.Array} receiver with the new content + */ + setObjects: function(objects) { + if (objects.length === 0) return this.clear(); + + var len = get(this, 'length'); + this.replace(0, len, objects); + return this; }, - // once the view is already in the DOM, destroying it removes it - // from the DOM, nukes its element, and puts it back into the - // preRender state if inDOM. + // .......................................................... + // IMPLEMENT Ember.MutableEnumerable + // - destroyElement: function(view) { - view._notifyWillDestroyElement(); - view.domManager.remove(view); - set(view, 'element', null); - if (view._scheduledInsert) { - run.cancel(view._scheduledInsert); - view._scheduledInsert = null; - } - return view; - }, + /** + Remove all occurrences of an object in the array. - empty: function(view) { - var _childViews = view._childViews, len, idx; - if (_childViews) { - len = _childViews.length; - for (idx = 0; idx < len; idx++) { - _childViews[idx]._notifyWillDestroyElement(); - } - } - view.domManager.empty(view); - }, + ```javascript + var cities = ["Chicago", "Berlin", "Lima", "Chicago"]; + cities.removeObject("Chicago"); // ["Berlin", "Lima"] + cities.removeObject("Lima"); // ["Berlin"] + cities.removeObject("Tokyo") // ["Berlin"] + ``` - // Handle events from `Ember.EventDispatcher` - handleEvent: function(view, eventName, evt) { - if (view.has(eventName)) { - // Handler should be able to re-dispatch events, so we don't - // preventDefault or stopPropagation. - return view.trigger(eventName, evt); - } else { - return true; // continue event propagation + @method removeObject + @param {*} obj object to remove + @return {Ember.Array} receiver + */ + removeObject: function(obj) { + var loc = get(this, 'length') || 0; + while(--loc >= 0) { + var curObject = this.objectAt(loc); + if (curObject === obj) this.removeAt(loc); } + return this; }, - invokeObserver: function(target, observer) { - observer.call(target); + /** + Push the object onto the end of the array if it is not already + present in the array. + + ```javascript + var cities = ["Chicago", "Berlin"]; + cities.addObject("Lima"); // ["Chicago", "Berlin", "Lima"] + cities.addObject("Berlin"); // ["Chicago", "Berlin", "Lima"] + ``` + + @method addObject + @param {*} obj object to add, if not already present + @return {Ember.Array} receiver + */ + addObject: function(obj) { + if (!this.contains(obj)) this.pushObject(obj); + return this; } - }); - __exports__["default"] = hasElement; + }); }); -define("ember-views/views/states/in_buffer", - ["ember-views/views/states/default","ember-metal/error","ember-metal/core","ember-metal/platform","ember-metal/merge","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { +enifed("ember-runtime/mixins/mutable_enumerable", + ["ember-metal/enumerable_utils","ember-runtime/mixins/enumerable","ember-metal/mixin","ember-metal/property_events","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; - var _default = __dependency1__["default"]; - var EmberError = __dependency2__["default"]; - - var Ember = __dependency3__["default"]; - // Ember.assert - var create = __dependency4__.create; - var merge = __dependency5__["default"]; + var forEach = __dependency1__.forEach; + var Enumerable = __dependency2__["default"]; + var Mixin = __dependency3__.Mixin; + var required = __dependency3__.required; + var beginPropertyChanges = __dependency4__.beginPropertyChanges; + var endPropertyChanges = __dependency4__.endPropertyChanges; /** @module ember - @submodule ember-views + @submodule ember-runtime */ - var inBuffer = create(_default); - - merge(inBuffer, { - $: function(view, sel) { - // if we don't have an element yet, someone calling this.$() is - // trying to update an element that isn't in the DOM. Instead, - // rerender the view to allow the render method to reflect the - // changes. - view.rerender(); - return Ember.$(); - }, - - // when a view is rendered in a buffer, rerendering it simply - // replaces the existing buffer with a new one - rerender: function(view) { - throw new EmberError("Something you did caused a view to re-render after it rendered but before it was inserted into the DOM."); - }, + /** + This mixin defines the API for modifying generic enumerables. These methods + can be applied to an object regardless of whether it is ordered or + unordered. - // when a view is rendered in a buffer, appending a child - // view will render that view and append the resulting - // buffer into its buffer. - appendChild: function(view, childView, options) { - var buffer = view.buffer, _childViews = view._childViews; + Note that an Enumerable can change even if it does not implement this mixin. + For example, a MappedEnumerable cannot be directly modified but if its + underlying enumerable changes, it will change also. - childView = view.createChildView(childView, options); - if (!_childViews.length) { _childViews = view._childViews = _childViews.slice(); } - _childViews.push(childView); + ## Adding Objects - childView.renderToBuffer(buffer); + To add an object to an enumerable, use the `addObject()` method. This + method will only add the object to the enumerable if the object is not + already present and is of a type supported by the enumerable. - view.propertyDidChange('childViews'); + ```javascript + set.addObject(contact); + ``` - return childView; - }, + ## Removing Objects - // when a view is rendered in a buffer, destroying the - // element will simply destroy the buffer and put the - // state back into the preRender state. - destroyElement: function(view) { - view.clearBuffer(); - var viewCollection = view._notifyWillDestroyElement(); - viewCollection.transitionTo('preRender', false); + 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. - return view; - }, + ```javascript + set.removeObject(contact); + ``` - empty: function() { - Ember.assert("Emptying a view in the inBuffer state is not allowed and " + - "should not happen under normal circumstances. Most likely " + - "there is a bug in your application. This may be due to " + - "excessive property change notifications."); - }, + ## Implementing In Your Own Code - renderToBufferIfNeeded: function (view, buffer) { - return false; - }, + If you are implementing an object and want to support this API, just include + this mixin in your class and implement the required methods. In your unit + tests, be sure to apply the Ember.MutableEnumerableTests to your 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"); - }, + @class MutableEnumerable + @namespace Ember + @uses Ember.Enumerable + */ + __exports__["default"] = Mixin.create(Enumerable, { - setElement: function(view, value) { - if (value === null) { - view.transitionTo('preRender'); - } else { - view.clearBuffer(); - view.transitionTo('hasElement'); - } + /** + __Required.__ You must implement this method to apply this mixin. - return value; - }, + Attempts to add the passed object to the receiver if the object is not + already present in the collection. If the object is present, this method + has no effect. - invokeObserver: function(target, observer) { - observer.call(target); - } - }); + If the passed object is of a type not supported by the receiver, + then this method should raise an 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"]; + @method addObject + @param {Object} object The object to add to the enumerable. + @return {Object} the passed object + */ + addObject: required(Function), - var hasElement = __dependency5__["default"]; - /** - @module ember - @submodule ember-views - */ + /** + Adds each object in the passed enumerable to the receiver. - var inDOM = create(hasElement); + @method addObjects + @param {Ember.Enumerable} objects the objects to add. + @return {Object} receiver + */ + addObjects: function(objects) { + beginPropertyChanges(this); + forEach(objects, function(obj) { this.addObject(obj); }, this); + endPropertyChanges(this); + return this; + }, - var View; + /** + __Required.__ You must implement this method to apply this mixin. - merge(inDOM, { - enter: function(view) { - if (!View) { View = requireModule('ember-views/views/view')["View"]; } // ES6TODO: this sucks. Have to avoid cycles... + 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. - // Register the view for event handling. This hash is used by - // Ember.EventDispatcher to dispatch incoming events. - if (!view.isVirtual) { - Ember.assert("Attempted to register a view with an id already in use: "+view.elementId, !View.views[view.elementId]); - View.views[view.elementId] = view; - } + If the passed object is of a type not supported by the receiver, + then this method should raise an exception. - view.addBeforeObserver('elementId', function() { - throw new EmberError("Changing a view's elementId after creation is not allowed"); - }); - }, + @method removeObject + @param {Object} object The object to remove from the enumerable. + @return {Object} the passed object + */ + removeObject: required(Function), - exit: function(view) { - if (!View) { View = requireModule('ember-views/views/view')["View"]; } // ES6TODO: this sucks. Have to avoid cycles... - if (!this.isVirtual) delete View.views[view.elementId]; - }, + /** + Removes each object in the passed enumerable from the receiver. - insertElement: function(view, fn) { - throw new EmberError("You can't insert an element into the DOM that has already been inserted"); + @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]); + } + endPropertyChanges(this); + return this; } }); - - __exports__["default"] = inDOM; }); -define("ember-views/views/states/pre_render", - ["ember-views/views/states/default","ember-metal/platform","ember-metal/merge","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { +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__) { "use strict"; - var _default = __dependency1__["default"]; - var create = __dependency2__.create; - var merge = __dependency3__["default"]; - /** @module ember - @submodule ember-views + @submodule ember-runtime */ - var preRender = create(_default); - - merge(preRender, { - // a view leaves the preRender state once its element has been - // created (createElement). - insertElement: function(view, fn) { - view.createElement(); - var viewCollection = view.viewHierarchyCollection(); - - viewCollection.trigger('willInsertElement'); + var Ember = __dependency1__["default"]; + // Ember.assert - fn.call(view); + 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"]; - // We transition to `inDOM` if the element exists in the DOM - var element = view.get('element'); - if (document.body.contains(element)) { - viewCollection.transitionTo('inDOM', false); - viewCollection.trigger('didInsertElement'); - } - }, - renderToBufferIfNeeded: function(view, buffer) { - view.renderToBuffer(buffer); - return true; - }, + var slice = Array.prototype.slice; + /** + ## Overview - empty: Ember.K, + This mixin provides properties and property observing functionality, core + features of the Ember object model. - setElement: function(view, value) { - if (value !== null) { - view.transitionTo('hasElement'); - } - return value; - } - }); + 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. - __exports__["default"] = preRender; - }); -define("ember-views/views/view", - ["ember-metal/core","ember-metal/error","ember-runtime/system/object","ember-runtime/mixins/evented","ember-runtime/mixins/action_handler","ember-views/system/render_buffer","ember-metal/property_get","ember-metal/property_set","ember-metal/set_properties","ember-metal/run_loop","ember-metal/observer","ember-metal/properties","ember-metal/utils","ember-metal/computed","ember-metal/mixin","ember-metal/is_none","container/container","ember-runtime/system/native_array","ember-metal/instrumentation","ember-runtime/system/string","ember-metal/enumerable_utils","ember-runtime/copy","ember-metal/binding","ember-metal/property_events","ember-views/views/states","ember-views/system/jquery","ember-views/system/ext","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__, __dependency22__, __dependency23__, __dependency24__, __dependency25__, __dependency26__, __dependency27__, __exports__) { - "use strict"; - // Ember.assert, Ember.deprecate, Ember.warn, Ember.TEMPLATES, - // Ember.K, jQuery, Ember.lookup, - // Ember.ContainerView circular dependency - // Ember.ENV - var Ember = __dependency1__["default"]; + 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. - var EmberError = __dependency2__["default"]; - var EmberObject = __dependency3__["default"]; - var Evented = __dependency4__["default"]; - var ActionHandler = __dependency5__["default"]; - var RenderBuffer = __dependency6__["default"]; - var get = __dependency7__.get; - var set = __dependency8__.set; - var setProperties = __dependency9__["default"]; - var run = __dependency10__["default"]; - var addObserver = __dependency11__.addObserver; - var removeObserver = __dependency11__.removeObserver; + 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. - var defineProperty = __dependency12__.defineProperty; - var guidFor = __dependency13__.guidFor; - var meta = __dependency13__.meta; - var computed = __dependency14__.computed; - var observer = __dependency15__.observer; + ## Using `get()` and `set()` - var typeOf = __dependency13__.typeOf; - var isArray = __dependency13__.isArray; - var isNone = __dependency16__.isNone; - var Mixin = __dependency15__.Mixin; - var Container = __dependency17__["default"]; - var A = __dependency18__.A; + 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. - var instrument = __dependency19__.instrument; + More documentation about `get` and `set` are below. - var dasherize = __dependency20__.dasherize; + ## Observing Property Changes - // ES6TODO: functions on EnumerableUtils should get their own export - var EnumerableUtils = __dependency21__["default"]; - var a_forEach = EnumerableUtils.forEach, - a_addObject = EnumerableUtils.addObject, - a_removeObject = EnumerableUtils.removeObject; + 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: - var beforeObserver = __dependency15__.beforeObserver; - var copy = __dependency22__["default"]; - var isGlobalPath = __dependency23__.isGlobalPath; + ```javascript + Ember.Object.extend({ + valueObserver: function() { + // Executes whenever the "value" property changes + }.observes('value') + }); + ``` - var propertyWillChange = __dependency24__.propertyWillChange; - var propertyDidChange = __dependency24__.propertyDidChange; + 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. - var cloneStates = __dependency25__.cloneStates; - var states = __dependency25__.states; - var jQuery = __dependency26__["default"]; - // for the side effect of extending Ember.run.queues + To add an observer for a property, call: - /** - @module ember - @submodule ember-views - */ + ```javascript + object.addObserver('propertyKey', targetObject, targetAction) + ``` - var ContainerView; + This will call the `targetAction` method on the `targetObject` whenever + the value of the `propertyKey` changes. - function nullViewsBuffer(view) { - view.buffer = null; + 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({ - function clearCachedElement(view) { - meta(view).cache.element = undefined; - } + /** + Retrieves the value of a property from the object. - var childViewsProperty = computed(function() { - var childViews = this._childViews, ret = A(), view = this; + This method is usually similar to using `object[keyName]` or `object.keyName`, + however it supports both computed properties and the unknownProperty + handler. - a_forEach(childViews, function(view) { - var currentChildViews; - if (view.isVirtual) { - if (currentChildViews = get(view, 'childViews')) { - ret.pushObjects(currentChildViews); - } - } else { - ret.push(view); - } - }); + 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. - ret.replace = function (idx, removedCount, addedViews) { - if (!ContainerView) { ContainerView = requireModule('ember-views/views/container_view')['default']; } // ES6TODO: stupid circular dep + ### Computed Properties - if (view instanceof ContainerView) { - Ember.deprecate("Manipulating an Ember.ContainerView through its childViews property is deprecated. Please use the ContainerView instance itself as an Ember.MutableArray."); - return view.replace(idx, removedCount, addedViews); - } - throw new EmberError("childViews is immutable"); - }; + Computed properties are methods defined with the `property` modifier + declared at the end, such as: - return ret; - }); + ```javascript + fullName: function() { + return this.get('firstName') + ' ' + this.get('lastName'); + }.property('firstName', 'lastName') + ``` - Ember.warn("The VIEW_PRESERVES_CONTEXT flag has been removed and the functionality can no longer be disabled.", Ember.ENV.VIEW_PRESERVES_CONTEXT !== false); + 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. - /** - Global hash of shared templates. This will automatically be populated - by the build tools so that you can store your Handlebars templates in - separate files that get loaded into JavaScript at buildtime. + ### Unknown Properties - @property TEMPLATES - @for Ember - @type Hash - */ - Ember.TEMPLATES = {}; + 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. - /** - `Ember.CoreView` is an abstract class that exists to give view-like behavior - to both Ember's main view class `Ember.View` and other classes like - `Ember._SimpleMetamorphView` that don't need the fully functionaltiy of - `Ember.View`. + @method get + @param {String} keyName The property to retrieve + @return {Object} The property value or undefined. + */ + get: function(keyName) { + return get(this, keyName); + }, - Unless you have specific needs for `CoreView`, you will use `Ember.View` - in your applications. + /** + To get the values of multiple properties at once, call `getProperties` + with a list of strings or an array: - @class CoreView - @namespace Ember - @extends Ember.Object - @uses Ember.Evented - @uses Ember.ActionHandler - */ + ```javascript + record.getProperties('firstName', 'lastName', 'zipCode'); + // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } + ``` - var CoreView = EmberObject.extend(Evented, ActionHandler, { - isView: true, + is equivalent to: - states: cloneStates(states), + ```javascript + record.getProperties(['firstName', 'lastName', 'zipCode']); + // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } + ``` - init: function() { - this._super(); - this.transitionTo('preRender'); - this._isVisible = get(this, 'isVisible'); + @method getProperties + @param {String...|Array} list of keys to get + @return {Hash} + */ + getProperties: function() { + return apply(null, getProperties, [this].concat(slice.call(arguments))); }, /** - If the view is currently inserted into the DOM of a parent view, this - property will point to the parent of the view. - - @property parentView - @type Ember.View - @default null - */ - parentView: computed('_parentView', function() { - var parent = this._parentView; - - if (parent && parent.isVirtual) { - return get(parent, 'parentView'); - } else { - return parent; - } - }), + Sets the provided key or path to the value. - state: null, + 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. - _parentView: null, + ### Computed Properties - // return the current view, not including virtual views - concreteView: computed('parentView', function() { - if (!this.isVirtual) { return this; } - else { return get(this, 'parentView.concreteView'); } - }), + 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. - instrumentName: 'core_view', + ### Unknown Properties - instrumentDetails: function(hash) { - hash.object = this.toString(); - hash.containerKey = this._debugContainerKey; - hash.view = this; - }, + 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. - /** - 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. + ### Property Observers - You should not need to override this method; instead, implement the - `template` property, or if you need more control, override the `render` - method. + 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. - @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 = {}; + ### Chaining - this.instrumentDetails(details); + In addition to property changes, `set()` returns the value of the object + itself so you can do chaining like this: - return instrument(name, details, function instrumentRenderToBuffer() { - return this._renderToBuffer(parentBuffer, bufferOperation); - }, this); - }, + ```javascript + record.set('firstName', 'Charles').set('lastName', 'Jolley'); + ``` - _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; + @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; + }, - if (tagName === null || tagName === undefined) { - tagName = 'div'; - } - var buffer = this.buffer = parentBuffer && parentBuffer.begin(tagName) || RenderBuffer(tagName); - this.transitionTo('inBuffer', false); + /** + Sets a list of properties at once. These properties are set inside + a single `beginPropertyChanges` and `endPropertyChanges` batch, so + observers will be buffered. - this.beforeRender(buffer); - this.render(buffer); - this.afterRender(buffer); + ```javascript + record.setProperties({ firstName: 'Charles', lastName: 'Jolley' }); + ``` - return buffer; + @method setProperties + @param {Hash} hash the hash of keys and values to set + @return {Ember.Observable} + */ + setProperties: function(hash) { + return setProperties(this, hash); }, /** - Override the default event firing from `Ember.Evented` to - also call methods with the given name. + Begins a grouping of property changes. - @method trigger - @param name {String} - @private + 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. + + @method beginPropertyChanges + @return {Ember.Observable} */ - 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); - } + beginPropertyChanges: function() { + beginPropertyChanges(); + return this; }, - deprecatedSendHandles: function(actionName) { - return !!this[actionName]; - }, + /** + Ends a grouping of property changes. - 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; - }, + 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. - has: function(name) { - return typeOf(this[name]) === 'function' || this._super(name); + @method endPropertyChanges + @return {Ember.Observable} + */ + endPropertyChanges: function() { + endPropertyChanges(); + return this; }, - destroy: function() { - var parent = this._parentView; - - if (!this._super()) { return; } - - // destroy the element -- this will avoid each child view destroying - // the element over and over again... - if (!this.removedFromDOM) { this.destroyElement(); } + /** + Notify the observer system that a property is about to change. - // 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); } + 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. - this.transitionTo('destroying', false); + 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. + @method propertyWillChange + @param {String} keyName The property key that is about to change. + @return {Ember.Observable} + */ + propertyWillChange: function(keyName) { + propertyWillChange(this, keyName); return this; }, - clearRenderedChildren: Ember.K, - triggerRecursively: Ember.K, - invokeRecursively: Ember.K, - transitionTo: Ember.K, - destroyElement: Ember.K - }); - - var ViewCollection = function(initialViews) { - var views = this.views = initialViews || []; - this.length = views.length; - }; + /** + Notify the observer system that a property has just changed. - ViewCollection.prototype = { - length: 0, + 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. - 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); } - } - }, + 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. - triggerRecursively: function(eventName) { - var views = this.views; - for (var i = 0, l = views.length; i < l; i++) { - views[i].triggerRecursively(eventName); - } + @method propertyDidChange + @param {String} keyName The property key that has just changed. + @return {Ember.Observable} + */ + propertyDidChange: function(keyName) { + propertyDidChange(this, keyName); + return this; }, - invokeRecursively: function(fn) { - var views = this.views, view; + /** + Convenience method to call `propertyWillChange` and `propertyDidChange` in + succession. - for (var i = 0, l = views.length; i < l; i++) { - view = views[i]; - fn(view); - } + @method notifyPropertyChange + @param {String} keyName The property key to be notified about. + @return {Ember.Observable} + */ + notifyPropertyChange: function(keyName) { + this.propertyWillChange(keyName); + this.propertyDidChange(keyName); + return this; }, - transitionTo: function(state, children) { - var views = this.views; - for (var i = 0, l = views.length; i < l; i++) { - views[i].transitionTo(state, children); - } + 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); }, - push: function() { - this.length += arguments.length; - var views = this.views; - return views.push.apply(views, arguments); - }, + /** + Adds an observer on a property. - objectAt: function(idx) { - return this.views[idx]; - }, + This is the core method used to register an observer for a property. - forEach: function(callback) { - var views = this.views; - return a_forEach(views, callback); - }, + 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. - clear: function() { - this.length = 0; - this.views.length = 0; - } - }; + 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. - var EMPTY_ARRAY = []; + ### Observer Methods - /** - `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. + Observer methods you pass should generally have the following signature if + you do not pass a `context` parameter: - ## HTML Tag + ```javascript + fooDidChange: function(sender, key, value, rev) { }; + ``` - 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: + 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. - ```javascript - ParagraphView = Ember.View.extend({ - tagName: 'em' - }); - ``` + If you pass a `context` parameter, the context will be passed before the + revision like so: - Would result in instances with the following HTML: + ```javascript + fooDidChange: function(sender, key, value, context, rev) { }; + ``` - ```html - - ``` + 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. - ## HTML `class` Attribute + @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); + }, - 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: + /** + 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. - ```javascript - MyView = Ember.View.extend({ - classNames: ['my-class', 'my-other-class'] - }); - ``` + @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); + }, - Will result in view instances with an HTML representation of: + /** + 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. - ```html -
    - ``` + @method hasObserverFor + @param {String} key Key to check + @return {Boolean} + */ + hasObserverFor: function(key) { + return hasListeners(this, key+':change'); + }, - `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: + /** + Retrieves the value of a property, or a default value in the case that the + property returns `undefined`. - ```javascript - MyView = Ember.View.extend({ - classNameBindings: ['propertyA', 'propertyB'], - propertyA: 'from-a', - propertyB: function() { - if (someLogic) { return 'from-b'; } - }.property() - }); - ``` + ```javascript + person.getWithDefault('lastName', 'Doe'); + ``` - Will result in view instances with an HTML representation of: + @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); + }, - ```html -
    - ``` + /** + Set the value of a property to the current value plus some amount. - 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 + person.incrementProperty('age'); + team.incrementProperty('score', 2); + ``` - ```javascript - MyView = Ember.View.extend({ - classNameBindings: ['hovered'], - hovered: true - }); - ``` + @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); + }, - Will result in view instances with an HTML representation of: + /** + Set the value of a property to the current value minus some amount. - ```html -
    - ``` + ```javascript + player.decrementProperty('lives'); + orc.decrementProperty('health', 5); + ``` - 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: + @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); + }, - ```javascript - MyView = Ember.View.extend({ - classNameBindings: ['awesome:so-very-cool'], - awesome: true - }); - ``` + /** + Set the value of a boolean property to the opposite of its + current value. - Will result in view instances with an HTML representation of: + ```javascript + starship.toggleProperty('warpDriveEngaged'); + ``` - ```html -
    - ``` + @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); + }, - Boolean value class name bindings whose property names are in a - camelCase-style format will be converted to a dasherized format: + /** + 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. - ```javascript - MyView = Ember.View.extend({ - classNameBindings: ['isUrgent'], - isUrgent: true - }); - ``` + @method cacheFor + @param {String} keyName + @return {Object} The cached value of the computed property, if any + */ + cacheFor: function(keyName) { + return cacheFor(this, keyName); + }, - Will result in view instances with an HTML representation of: + // intended for debugging purposes + observersForKey: function(keyName) { + return observersFor(this, keyName); + } + }); + }); +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__) { + "use strict"; + var get = __dependency1__.get; + var setProperties = __dependency2__["default"]; + var computed = __dependency3__.computed; + var Mixin = __dependency4__.Mixin; + var EmberError = __dependency5__["default"]; - ```html -
    - ``` + var not = computed.not; + var or = computed.or; - Class name bindings can also refer to object values that are found by - traversing a path relative to the view itself: + /** + @module ember + @submodule ember-runtime + */ - ```javascript - MyView = Ember.View.extend({ - classNameBindings: ['messages.empty'] - messages: Ember.Object.create({ - empty: true - }) + function tap(proxy, promise) { + setProperties(proxy, { + isFulfilled: false, + isRejected: false }); - ``` - - Will result in view instances with an HTML representation of: - ```html -
    - ``` + 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"); + } - 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: + /** + A low level mixin making ObjectProxy, ObjectController or ArrayController's promise aware. ```javascript - // Applies 'enabled' class when isEnabled is true and 'disabled' when isEnabled is false - Ember.View.extend({ - classNameBindings: ['isEnabled:enabled:disabled'] - isEnabled: true + var ObjectPromiseController = Ember.ObjectController.extend(Ember.PromiseProxyMixin); + + var controller = ObjectPromiseController.create({ + promise: $.getJSON('/some/remote/data.json') + }); + + controller.then(function(json){ + // the json + }, function(reason) { + // the reason why you have no json }); ``` - Will result in view instances with an HTML representation of: + the controller has bindable attributes which + track the promises life cycle - ```html -
    + ```javascript + controller.get('isPending') //=> true + controller.get('isSettled') //=> false + controller.get('isRejected') //=> false + controller.get('isFulfilled') //=> false ``` - When isEnabled is `false`, the resulting HTML reprensentation looks like - this: + When the the $.getJSON completes, and the promise is fulfilled + with json, the life cycle attributes will update accordingly. - ```html -
    + ```javascript + controller.get('isPending') //=> false + controller.get('isSettled') //=> true + controller.get('isRejected') //=> false + controller.get('isFulfilled') //=> true ``` - This syntax offers the convenience to add a class if a property is `false`: + As the controller is an ObjectController, and the json now its content, + all the json properties will be available directly from the controller. ```javascript - // Applies no class when isEnabled is true and class 'disabled' when isEnabled is false - Ember.View.extend({ - classNameBindings: ['isEnabled::disabled'] - isEnabled: true - }); + // Assuming the following json: + { + firstName: 'Stefan', + lastName: 'Penner' + } + + // both properties will accessible on the controller + controller.get('firstName') //=> 'Stefan' + controller.get('lastName') //=> 'Penner' ``` - Will result in view instances with an HTML representation of: + If the controller is backing a template, the attributes are + bindable from within that template - ```html -
    + ```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. - When the `isEnabled` property on the view is set to `false`, it will result - in view instances with an HTML representation of: - - ```html -
    - ``` + @property reason + @default null + */ + reason: null, - 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. + /** + Once the proxied promise has settled this will become `false`. - Both `classNames` and `classNameBindings` are concatenated properties. See - [Ember.Object](/api/classes/Ember.Object.html) documentation for more - information about concatenated properties. + @property isPending + @default true + */ + isPending: not('isSettled').readOnly(), - ## HTML Attributes + /** + Once the proxied promise has settled this will become `true`. - 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: + @property isSettled + @default false + */ + isSettled: or('isRejected', 'isFulfilled').readOnly(), - ```javascript - AnchorView = Ember.View.extend({ - tagName: 'a', - attributeBindings: ['href'], - href: 'http://google.com' - }); - ``` + /** + Will become `true` if the proxied promise is rejected. - Will result in view instances with an HTML representation of: + @property isRejected + @default false + */ + isRejected: false, - ```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: + /** + Will become `true` if the proxied promise is fulfilled. - ```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: + @property isFulfilled + @default false + */ + isFulfilled: false, - ```javascript - MyTextInput = Ember.View.extend({ - tagName: 'input', - attributeBindings: ['disabled'], - disabled: true - }); - ``` + /** + The promise whose fulfillment value is being proxied by this object. - Will result in view instances with an HTML representation of: + This property must be specified upon creation, and should not be + changed once created. - ```html - - ``` + Example: - `attributeBindings` can refer to computed properties: + ```javascript + Ember.ObjectController.extend(Ember.PromiseProxyMixin).create({ + promise: + }); + ``` - ```javascript - MyTextInput = Ember.View.extend({ - tagName: 'input', - attributeBindings: ['disabled'], - disabled: function() { - if (someLogic) { - return true; - } else { - return false; - } - }.property() - }); - ``` + @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"); + } + }), - 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. + /** + An alias to the proxied promise's `then`. - `attributeBindings` is a concatenated property. See [Ember.Object](/api/classes/Ember.Object.html) - documentation for more information about concatenated properties. + See RSVP.Promise.then. - ## Templates + @method then + @param {Function} callback + @return {RSVP.Promise} + */ + then: promiseAlias('then'), - 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. + /** + An alias to the proxied promise's `catch`. - ```javascript - AView = Ember.View.extend({ - template: Ember.Handlebars.compile('I am the template') - }); - ``` + See RSVP.Promise.catch. - Will result in view instances with an HTML representation of: + @method catch + @param {Function} callback + @return {RSVP.Promise} + @since 1.3.0 + */ + 'catch': promiseAlias('catch'), - ```html -
    I am the template
    - ``` + /** + An alias to the proxied promise's `finally`. - Within an Ember application is more common to define a Handlebars templates as - part of a page: + See RSVP.Promise.finally. - ```html - - ``` + @method finally + @param {Function} callback + @return {RSVP.Promise} + @since 1.3.0 + */ + 'finally': promiseAlias('finally') - And associate it by name using a view's `templateName` property: + }); - ```javascript - AView = Ember.View.extend({ - templateName: 'some-template' - }); - ``` + function promiseAlias(name) { + return function () { + var promise = get(this, 'promise'); + return promise[name].apply(promise, arguments); + }; + } + }); +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__) { + "use strict"; + /** + @module ember + @submodule ember-runtime + */ - If you have nested resources, your Handlebars template will look like this: + var Ember = __dependency1__["default"]; + // Ember.assert, Ember.A - ```html - - ``` + 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? - And `templateName` property: + /** + `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: ```javascript - AView = Ember.View.extend({ - templateName: 'posts/new' - }); - ``` - - Using a value for `templateName` that does not have a Handlebars template - with a matching `data-template-name` attribute will throw an error. - - 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: + 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'}, + ]; - ```javascript - AView = Ember.View.extend({ - defaultTemplate: Ember.Handlebars.compile('I was the default'), - template: null, - templateName: null + songsController = Ember.ArrayController.create({ + model: songs, + sortProperties: ['trackNumber'], + sortAscending: true }); - ``` - Will result in instances with an HTML representation of: + songsController.get('firstObject'); // {trackNumber: 2, title: 'Back in the U.S.S.R.'} - ```html -
    I was the default
    + songsController.addObject({trackNumber: 1, title: 'Dear Prudence'}); + songsController.get('firstObject'); // {trackNumber: 1, title: 'Dear Prudence'} ``` - If a `template` or `templateName` is provided it will take precedence over - `defaultTemplate`: + If you add or remove the properties to sort by or change the sort direction the model + sort order will be automatically updated. ```javascript - AView = Ember.View.extend({ - defaultTemplate: Ember.Handlebars.compile('I was the default') - }); + songsController.set('sortProperties', ['title']); + songsController.get('firstObject'); // {trackNumber: 2, title: 'Back in the U.S.S.R.'} - aView = AView.create({ - template: Ember.Handlebars.compile('I was the template, not default') - }); + songsController.toggleProperty('sortAscending'); + songsController.get('firstObject'); // {trackNumber: 4, title: 'Ob-La-Di, Ob-La-Da'} ``` - Will result in the following HTML representation when rendered: + `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: - ```html -
    I was the template, not default
    + ```javascript + songsController.get('content').get('firstObject'); // Returns the unsorted original content + songsController.get('firstObject'); // Returns the sorted content. ``` - ## View Context + 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 default context of the compiled template is the view's controller: + @class SortableMixin + @namespace Ember + @uses Ember.MutableEnumerable + */ + __exports__["default"] = Mixin.create(MutableEnumerable, { - ```javascript - AView = Ember.View.extend({ - template: Ember.Handlebars.compile('Hello {{excitedGreeting}}') - }); + /** + Specifies which properties dictate the `arrangedContent`'s sort order. - aController = Ember.Object.create({ - firstName: 'Barry', - excitedGreeting: function() { - return this.get("content.firstName") + "!!!" - }.property() - }); + When specifying multiple properties the sorting will use properties + from the `sortProperties` array prioritized from first to last. - aView = AView.create({ - controller: aController, - }); - ``` + @property {Array} sortProperties + */ + sortProperties: null, - Will result in an HTML representation of: + /** + Specifies the `arrangedContent`'s sort direction. + Sorts the content in ascending order by default. Set to `false` to + use descending order. - ```html -
    Hello Barry!!!
    - ``` + @property {Boolean} sortAscending + @default true + */ + sortAscending: true, - 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. + /** + 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., - ## Layouts + * 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: - 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. + ```javascript + function(x, y) { // These are assumed to be integers + if (x === y) + return 0; + return x < y ? -1 : 1; + } + ``` - Most typically in Ember a layout will be a compiled `Ember.Handlebars` - template. + @property sortFunction + @type {Function} + @default Ember.compare + */ + sortFunction: compare, - A view's layout can be set directly with the `layout` property or reference - an existing Handlebars template by name with the `layoutName` property. + orderBy: function(item1, item2) { + var result = 0; + var sortProperties = get(this, 'sortProperties'); + var sortAscending = get(this, 'sortAscending'); + var sortFunction = get(this, 'sortFunction'); - 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: + Ember.assert("you need to define `sortProperties`", !!sortProperties); - ```javascript - AViewWithLayout = Ember.View.extend({ - layout: Ember.Handlebars.compile("
    {{yield}}
    ") - template: Ember.Handlebars.compile("I got wrapped"), - }); - ``` + 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); - Will result in view instances with an HTML representation of: + return result; + }, - ```html -
    -
    - I got wrapped -
    -
    - ``` + destroy: function() { + var content = get(this, 'content'); + var sortProperties = get(this, 'sortProperties'); - See [Ember.Handlebars.helpers.yield](/api/classes/Ember.Handlebars.helpers.html#method_yield) - for more information. + if (content && sortProperties) { + forEach(content, function(item) { + forEach(sortProperties, function(sortProperty) { + removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); + }, this); + }, this); + } - ## Responding to Browser Events + return this._super(); + }, - 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. + isSorted: computed.notEmpty('sortProperties'), - ### Method Implementation + /** + 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. - 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. + @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; - ```javascript - AView = Ember.View.extend({ - click: function(event) { - // will be called when when an instance's - // rendered element is clicked + 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); } - }); - ``` - ### Event Managers + return content; + }), - 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. + _contentWillChange: beforeObserver('content', function() { + var content = get(this, 'content'); + var sortProperties = get(this, 'sortProperties'); - ```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 - } - }) - }); - ``` + if (content && sortProperties) { + forEach(content, function(item) { + forEach(sortProperties, function(sortProperty) { + removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); + }, this); + }, this); + } - An event defined for an event manager takes precedence over events of the - same name handled through methods on the view. + this._super(); + }), - ```javascript - AView = Ember.View.extend({ - mouseEnter: function(event) { - // will never trigger. - }, - eventManager: Ember.Object.create({ - mouseEnter: function(event, view) { - // takes precedence over AView#mouseEnter - } - }) - }); - ``` + sortPropertiesWillChange: beforeObserver('sortProperties', function() { + this._lastSortAscending = undefined; + }), - 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. + sortPropertiesDidChange: observer('sortProperties', function() { + this._lastSortAscending = undefined; + }), - ```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 - } - }) - }); + sortAscendingWillChange: beforeObserver('sortAscending', function() { + this._lastSortAscending = get(this, 'sortAscending'); + }), - 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. + sortAscendingDidChange: observer('sortAscending', function() { + if (this._lastSortAscending !== undefined && get(this, 'sortAscending') !== this._lastSortAscending) { + var arrangedContent = get(this, 'arrangedContent'); + arrangedContent.reverseObjects(); } - }); - ``` + }), - ### Handlebars `{{action}}` Helper + contentArrayWillChange: function(array, idx, removedCount, addedCount) { + var isSorted = get(this, 'isSorted'); - See [Handlebars.helpers.action](/api/classes/Ember.Handlebars.helpers.html#method_action). + if (isSorted) { + var arrangedContent = get(this, 'arrangedContent'); + var removedObjects = array.slice(idx, idx+removedCount); + var sortProperties = get(this, 'sortProperties'); - ### Event Names + forEach(removedObjects, function(item) { + arrangedContent.removeObject(item); - 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`. + forEach(sortProperties, function(sortProperty) { + removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); + }, this); + }, this); + } - Touch events: + return this._super(array, idx, removedCount, addedCount); + }, - * `touchStart` - * `touchMove` - * `touchEnd` - * `touchCancel` + 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); - Keyboard events + forEach(addedObjects, function(item) { + this.insertItemSorted(item); - * `keyDown` - * `keyUp` - * `keyPress` + forEach(sortProperties, function(sortProperty) { + addObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); + }, this); + }, this); + } - Mouse events + return this._super(array, idx, removedCount, addedCount); + }, - * `mouseDown` - * `mouseUp` - * `contextMenu` - * `click` - * `doubleClick` - * `mouseMove` - * `focusIn` - * `focusOut` - * `mouseEnter` - * `mouseLeave` + insertItemSorted: function(item) { + var arrangedContent = get(this, 'arrangedContent'); + var length = get(arrangedContent, 'length'); - Form events: + var idx = this._binarySearch(item, 0, length); + arrangedContent.insertAt(idx, item); + }, - * `submit` - * `change` - * `focusIn` - * `focusOut` - * `input` + 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); - HTML5 drag and drop events: + if (leftResult < 0 || rightResult > 0) { + arrangedContent.removeObject(item); + this.insertItemSorted(item); + } + }, - * `dragStart` - * `drag` - * `dragEnter` - * `dragLeave` - * `dragOver` - * `dragEnd` - * `drop` + _binarySearch: function(item, low, high) { + var mid, midItem, res, arrangedContent; - ## Handlebars `{{view}}` Helper + if (low === high) { + return low; + } - 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. + arrangedContent = get(this, 'arrangedContent'); - @class View - @namespace Ember - @extends Ember.CoreView - */ - var View = CoreView.extend({ + mid = low + Math.floor((high - low) / 2); + midItem = arrangedContent.objectAt(mid); - concatenatedProperties: ['classNames', 'classNameBindings', 'attributeBindings'], + res = this.orderBy(midItem, item); - /** - @property isView - @type Boolean - @default true - @static - */ - isView: true, + if (res < 0) { + return this._binarySearch(item, mid+1, high); + } else if (res > 0) { + return this._binarySearch(item, low, mid); + } - // .......................................................... - // TEMPLATE SUPPORT - // + 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 - /** - The name of the template to lookup if no template is provided. + var get = __dependency2__.get; + var typeOf = __dependency3__.typeOf; + var Mixin = __dependency4__.Mixin; + var computed = __dependency5__.computed; - By default `Ember.View` will lookup a template with this name in - `Ember.TEMPLATES` (a shared global object). + /** + `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. - @property templateName - @type String - @default null - */ - templateName: null, + See also `Ember.ViewTargetActionSupport`, which has + view-aware defaults for target and actionContext. - /** - The name of the layout to lookup if no layout is provided. + @class TargetActionSupport + @namespace Ember + @extends Ember.Mixin + */ + var TargetActionSupport = Mixin.create({ + target: null, + action: null, + actionContext: null, - By default `Ember.View` will lookup a template with this name in - `Ember.TEMPLATES` (a shared global object). + targetObject: computed(function() { + var target = get(this, 'target'); - @property layoutName - @type String - @default null - */ - layoutName: null, + if (typeOf(target) === "string") { + var value = get(this, target); + if (value === undefined) { value = get(Ember.lookup, target); } + return value; + } else { + return target; + } + }).property('target'), - /** - Used to identify this view during debugging + actionContextObject: computed(function() { + var actionContext = get(this, 'actionContext'); - @property instrumentDisplay - @type String - */ - instrumentDisplay: computed(function() { - if (this.helperName) { - return '{{' + this.helperName + '}}'; + if (typeOf(actionContext) === "string") { + var value = get(this, actionContext); + if (value === undefined) { value = get(Ember.lookup, actionContext); } + return value; + } else { + return actionContext; } - }), + }).property('actionContext'), /** - 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. - - In general, you should set the `templateName` property instead of setting - the template yourself. + Send an `action` with an `actionContext` to a `target`. The action, actionContext + and target will be retrieved from properties of the object. For example: - @property template - @type Function - */ - template: computed('templateName', function(key, value) { - if (value !== undefined) { return value; } + ```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 + } + }); + ``` - var templateName = get(this, 'templateName'), - template = this.templateForName(templateName, 'template'); + The `target`, `action`, and `actionContext` can be provided as properties of + an optional object argument to `triggerAction` as well. - Ember.assert("You specified the templateName " + templateName + " for " + this + ", but it did not exist.", !templateName || template); + ```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 + } + }); + ``` - return template || get(this, 'defaultTemplate'); - }), + 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: - /** - The controller managing this view. If this property is set, it will be - made available for use by the template. + ```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 + } + }); + ``` - @property controller - @type Object + @method triggerAction + @param opts {Hash} (optional, with the optional keys action, target and/or actionContext) + @return {Boolean} true if the action was sent successfully and did not return false */ - controller: computed('_parentView', function(key) { - var parentView = get(this, '_parentView'); - return parentView ? get(parentView, 'controller') : null; - }), + triggerAction: function(opts) { + opts = opts || {}; + var action = opts.action || get(this, 'action'); + var target = opts.target || get(this, 'targetObject'); + var actionContext = opts.actionContext; - /** - 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. + function args(options, actionName) { + var ret = []; + if (actionName) { ret.push(actionName); } - 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. + return ret.concat(options); + } - @property layout - @type Function - */ - layout: computed(function(key) { - var layoutName = get(this, 'layoutName'), - layout = this.templateForName(layoutName, 'layout'); + if (typeof actionContext === 'undefined') { + actionContext = get(this, 'actionContextObject') || this; + } - Ember.assert("You specified the layoutName " + layoutName + " for " + this + ", but it did not exist.", !layoutName || layout); + if (target && action) { + var ret; - return layout || get(this, 'defaultLayout'); - }).property('layoutName'), + if (target.send) { + ret = target.send.apply(target, args(actionContext, action)); + } else { + Ember.assert("The action '" + action + "' did not exist on " + target, typeof target[action] === 'function'); + ret = target[action].apply(target, args(actionContext)); + } - _yield: function(context, options) { - var template = get(this, 'template'); - if (template) { template(context, options); } - }, + if (ret !== false) ret = true; - templateForName: function(name, type) { - if (!name) { return; } - Ember.assert("templateNames are not allowed to contain periods: "+name, name.indexOf('.') === -1); + return ret; + } else { + return false; + } + } + }); - // the defaultContainer is deprecated - var container = this.container || (Container && Container.defaultContainer); - return container && container.lookup('template:' + name); - }, + __exports__["default"] = TargetActionSupport; + }); +enifed("ember-runtime/system/application", + ["ember-runtime/system/namespace","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Namespace = __dependency1__["default"]; - /** - The object from which templates should access properties. + __exports__["default"] = Namespace.extend(); + }); +enifed("ember-runtime/system/array_proxy", + ["ember-metal/core","ember-metal/property_get","ember-metal/utils","ember-metal/computed","ember-metal/mixin","ember-metal/property_events","ember-metal/error","ember-runtime/system/object","ember-runtime/mixins/mutable_array","ember-runtime/mixins/enumerable","ember-runtime/system/string","ember-metal/alias","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // Ember.assert + var get = __dependency2__.get; + var isArray = __dependency3__.isArray; + var apply = __dependency3__.apply; + var computed = __dependency4__.computed; + var beforeObserver = __dependency5__.beforeObserver; + var observer = __dependency5__.observer; + var beginPropertyChanges = __dependency6__.beginPropertyChanges; + var endPropertyChanges = __dependency6__.endPropertyChanges; + var EmberError = __dependency7__["default"]; + var EmberObject = __dependency8__["default"]; + var MutableArray = __dependency9__["default"]; + var Enumerable = __dependency10__["default"]; + var fmt = __dependency11__.fmt; + var alias = __dependency12__["default"]; - 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. + /** + @module ember + @submodule ember-runtime + */ - By default, this will be the view's controller. + var OUT_OF_RANGE_EXCEPTION = "Index out of range"; + var EMPTY = []; - @property context - @type Object - */ - context: computed(function(key, value) { - if (arguments.length === 2) { - set(this, '_context', value); - return value; - } else { - return get(this, '_context'); - } - }).volatile(), + function K() { return this; } - /** - 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. + /** + An ArrayProxy wraps any other object that implements `Ember.Array` and/or + `Ember.MutableArray,` forwarding all requests. This makes it very useful for + a number of binding use cases or other cases where being able to swap + out the underlying array is useful. - The context of a view is looked up as follows: + A simple example of usage: - 1. Supplied context (usually by Handlebars) - 2. Specified controller - 3. `parentView`'s context (for a child of a ContainerView) + ```javascript + var pets = ['dog', 'cat', 'fish']; + var ap = Ember.ArrayProxy.create({ content: Ember.A(pets) }); - 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. + ap.get('firstObject'); // 'dog' + ap.set('content', ['amoeba', 'paramecium']); + ap.get('firstObject'); // 'amoeba' + ``` - @property _context - @private - */ - _context: computed(function(key) { - var parentView, controller; + This class can also be useful as a layer to transform the contents of + an array, as they are accessed. This can be done by overriding + `objectAtContent`: - if (controller = get(this, 'controller')) { - return controller; - } + ```javascript + var pets = ['dog', 'cat', 'fish']; + var ap = Ember.ArrayProxy.create({ + content: Ember.A(pets), + objectAtContent: function(idx) { + return this.get('content').objectAt(idx).toUpperCase(); + } + }); - parentView = this._parentView; - if (parentView) { - return get(parentView, '_context'); - } + ap.get('firstObject'); // . 'DOG' + ``` - return null; - }), + @class ArrayProxy + @namespace Ember + @extends Ember.Object + @uses Ember.MutableArray + */ + var ArrayProxy = EmberObject.extend(MutableArray, { /** - If a value that affects template rendering changes, the view should be - re-rendered to reflect the new value. + The content array. Must be an object that implements `Ember.Array` and/or + `Ember.MutableArray.` - @method _contextDidChange - @private + @property content + @type Ember.Array */ - _contextDidChange: observer('context', function() { - this.rerender(); - }), + content: null, /** - If `false`, the view will appear hidden in DOM. + The array that the proxy pretends to be. In the default `ArrayProxy` + implementation, this and `content` are the same. Subclasses of `ArrayProxy` + can override this property to provide things like sorting and filtering. - @property isVisible - @type Boolean - @default null + @property arrangedContent */ - isVisible: true, + arrangedContent: alias('content'), /** - 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, + Should actually retrieve the object at the specified index from the + content. You can override this method in subclasses to transform the + content item to something new. - // 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'); } - } - }), + This method will only be called if content is non-`null`. - // 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'); } - } - }), + @method objectAtContent + @param {Number} idx The index to retrieve. + @return {Object} the value or undefined if none found + */ + objectAtContent: function(idx) { + return get(this, 'arrangedContent').objectAt(idx); + }, /** - Return the nearest ancestor that is an instance of the provided - class. + Should actually replace the specified objects on the content array. + You can override this method in subclasses to transform the content item + into something new. - @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'); + This method will only be called if content is non-`null`. - while (view) { - if (view instanceof klass) { return view; } - view = get(view, 'parentView'); - } + @method replaceContent + @param {Number} idx The starting index + @param {Number} amt The number of items to remove from the content. + @param {Array} objects Optional array of objects to insert or null if no + objects. + @return {void} + */ + replaceContent: function(idx, amt, objects) { + get(this, 'content').replace(idx, amt, objects); }, /** - Return the nearest ancestor that is an instance of the provided - class or mixin. + Invoked when the content property is about to change. Notifies observers that the + entire array content will change. - @method nearestOfType - @param {Class,Mixin} klass Subclass of Ember.View (or Ember.View itself), - or an instance of Ember.Mixin. - @return Ember.View + @private + @method _contentWillChange */ - 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); }; + _contentWillChange: beforeObserver('content', function() { + this._teardownContent(); + }), - while (view) { - if (isOfType(view)) { return view; } - view = get(view, 'parentView'); + _teardownContent: function() { + var content = get(this, 'content'); + + if (content) { + content.removeArrayObserver(this, { + willChange: 'contentArrayWillChange', + didChange: 'contentArrayDidChange' + }); } }, /** - Return the nearest ancestor that has a given property. + Override to implement content array `willChange` observer. - @method nearestWithProperty - @param {String} property A property name - @return Ember.View - */ - nearestWithProperty: function(property) { - var view = get(this, 'parentView'); + @method contentArrayWillChange - while (view) { - if (property in view) { return view; } - view = get(view, 'parentView'); - } - }, + @param {Ember.Array} contentArray the content array + @param {Number} start starting index of the change + @param {Number} removeCount count of items removed + @param {Number} addCount count of items added + */ + contentArrayWillChange: K, /** - Return the nearest ancestor whose parent is an instance of - `klass`. + Override to implement content array `didChange` observer. - @method nearestChildOf - @param {Class} klass Subclass of Ember.View (or Ember.View itself) - @return Ember.View - */ - nearestChildOf: function(klass) { - var view = get(this, 'parentView'); + @method contentArrayDidChange - while (view) { - if (get(view, 'parentView') instanceof klass) { return view; } - view = get(view, 'parentView'); - } - }, + @param {Ember.Array} contentArray the content array + @param {Number} start starting index of the change + @param {Number} removeCount count of items removed + @param {Number} addCount count of items added + */ + contentArrayDidChange: K, /** - When the parent view changes, recursively invalidate `controller` + Invoked when the content property changes. Notifies observers that the + entire array content has changed. - @method _parentViewDidChange @private + @method _contentDidChange */ - _parentViewDidChange: observer('_parentView', function() { - if (this.isDestroying) { return; } + _contentDidChange: observer('content', function() { + var content = get(this, 'content'); - this.trigger('parentViewDidChange'); + Ember.assert("Can't set ArrayProxy's content to itself", content !== this); - if (get(this, 'parentView.controller') && !get(this, 'controller')) { - this.notifyPropertyChange('controller'); - } + this._setupContent(); }), - _controllerDidChange: observer('controller', function() { - if (this.isDestroying) { return; } + _setupContent: function() { + var content = get(this, 'content'); - this.rerender(); + if (content) { + Ember.assert(fmt('ArrayProxy expects an Array or ' + + 'Ember.ArrayProxy, but you passed %@', [typeof content]), + isArray(content) || content.isDestroyed); - this.forEachChildView(function(view) { - view.propertyDidChange('controller'); - }); - }), + content.addArrayObserver(this, { + willChange: 'contentArrayWillChange', + didChange: 'contentArrayDidChange' + }); + } + }, - cloneKeywords: function() { - var templateData = get(this, 'templateData'); + _arrangedContentWillChange: beforeObserver('arrangedContent', function() { + var arrangedContent = get(this, 'arrangedContent'); + var len = arrangedContent ? get(arrangedContent, 'length') : 0; - var keywords = templateData ? copy(templateData.keywords) : {}; - set(keywords, 'view', get(this, 'concreteView')); - set(keywords, '_view', this); - set(keywords, 'controller', get(this, 'controller')); + this.arrangedContentArrayWillChange(this, 0, len, undefined); + this.arrangedContentWillChange(this); - return keywords; - }, + this._teardownArrangedContent(arrangedContent); + }), - /** - 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. + _arrangedContentDidChange: observer('arrangedContent', function() { + var arrangedContent = get(this, 'arrangedContent'); + var len = arrangedContent ? get(arrangedContent, 'length') : 0; - 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. + Ember.assert("Can't set ArrayProxy's content to itself", arrangedContent !== this); - @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'); + this._setupArrangedContent(); - if (template) { - var context = get(this, 'context'); - var keywords = this.cloneKeywords(); - var output; + this.arrangedContentDidChange(this); + this.arrangedContentArrayDidChange(this, 0, undefined, len); + }), - var data = { - view: this, - buffer: buffer, - isRenderData: true, - keywords: keywords, - insideGroup: get(this, 'templateData.insideGroup') - }; + _setupArrangedContent: function() { + var arrangedContent = get(this, 'arrangedContent'); - // 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. + if (arrangedContent) { + Ember.assert(fmt('ArrayProxy expects an Array or ' + + 'Ember.ArrayProxy, but you passed %@', [typeof arrangedContent]), + isArray(arrangedContent) || arrangedContent.isDestroyed); - 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 }); + arrangedContent.addArrayObserver(this, { + willChange: 'arrangedContentArrayWillChange', + didChange: 'arrangedContentArrayDidChange' + }); + } + }, - // If the template returned a string instead of writing to the buffer, - // push the string onto the buffer. - if (output !== undefined) { buffer.push(output); } + _teardownArrangedContent: function() { + var arrangedContent = get(this, 'arrangedContent'); + + if (arrangedContent) { + arrangedContent.removeArrayObserver(this, { + willChange: 'arrangedContentArrayWillChange', + didChange: 'arrangedContentArrayDidChange' + }); } }, - /** - 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. + arrangedContentWillChange: K, + arrangedContentDidChange: K, - 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`. + objectAt: function(idx) { + return get(this, 'content') && this.objectAtContent(idx); + }, - 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. + length: computed(function() { + var arrangedContent = get(this, 'arrangedContent'); + return arrangedContent ? get(arrangedContent, 'length') : 0; + // No dependencies since Enumerable notifies length of change + }), - @method rerender - */ - rerender: function() { - return this.currentState.rerender(this); + _replace: function(idx, amt, objects) { + var content = get(this, 'content'); + Ember.assert('The content property of '+ this.constructor + ' should be set before modifying it', content); + if (content) this.replaceContent(idx, amt, objects); + return this; }, - clearRenderedChildren: function() { - var lengthBefore = this.lengthBeforeRender, - lengthAfter = this.lengthAfterRender; + replace: function() { + if (get(this, 'arrangedContent') === get(this, 'content')) { + apply(this, this._replace, arguments); + } else { + throw new EmberError("Using replace on an arranged ArrayProxy is not allowed."); + } + }, - // 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. + _insertAt: function(idx, object) { + if (idx > get(this, 'content.length')) throw new EmberError(OUT_OF_RANGE_EXCEPTION); + this._replace(idx, 0, [object]); + return this; + }, - // VIEW-TODO: Unit test this path. - var childViews = this._childViews; - for (var i=lengthAfter-1; i>=lengthBefore; i--) { - if (childViews[i]) { childViews[i].destroy(); } + insertAt: function(idx, object) { + if (get(this, 'arrangedContent') === get(this, 'content')) { + return this._insertAt(idx, object); + } else { + throw new EmberError("Using insertAt on an arranged ArrayProxy is not allowed."); } }, - /** - 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. + removeAt: function(start, len) { + if ('number' === typeof start) { + var content = get(this, 'content'); + var arrangedContent = get(this, 'arrangedContent'); + var indices = []; + var i; - @method _applyClassNameBindings - @private - */ - _applyClassNameBindings: function(classBindings) { - var classNames = this.classNames, - elem, newClass, dasherizedClass; + if ((start < 0) || (start >= get(this, 'length'))) { + throw new EmberError(OUT_OF_RANGE_EXCEPTION); + } - // 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 (len === undefined) len = 1; - 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); + // Get a list of indices in original content to remove + for (i=start; i 0 && + indexOf(concatenatedProperties, keyName) >= 0) { + var baseValue = this[keyName]; - mutateChildViews: function(callback) { - var childViews = this._childViews, - idx = childViews.length, - view; + if (baseValue) { + if ('function' === typeof baseValue.concat) { + value = baseValue.concat(value); + } else { + value = makeArray(baseValue).concat(value); + } + } else { + value = makeArray(value); + } + } - while(--idx >= 0) { - view = childViews[idx]; - callback(this, view, idx); + 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; + } + } + } + } + } } - return this; - }, + finishPartial(this, m); - forEachChildView: function(callback) { - var childViews = this._childViews; + var length = arguments.length; - if (!childViews) { return this; } + 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); + } - var len = childViews.length, - view, idx; + m.proto = proto; + finishChains(this); + sendEvent(this, 'init'); + }; - for (idx = 0; idx < len; idx++) { - view = childViews[idx]; - callback(view); + Class.toString = Mixin.prototype.toString; + Class.willReopen = function() { + if (wasApplied) { + Class.PrototypeMixin = Mixin.create(Class.PrototypeMixin); } - return this; - }, - - /** - Appends the view's element to the specified parent element. + wasApplied = false; + }; + Class._initMixins = function(args) { initMixins = args; }; + Class._initProperties = function(args) { initProperties = args; }; - If the view does not have an HTML representation yet, `createElement()` - will be called automatically. + Class.proto = function() { + var superclass = Class.superclass; + if (superclass) { superclass.proto(); } - 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. + if (!wasApplied) { + wasApplied = true; + Class.PrototypeMixin.applyPartial(Class.prototype); + } - 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. + return this.prototype; + }; - @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); - }); + return Class; + + } + /** + @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; }, /** - 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. + An overridable method called when objects are instantiated. By default, + does nothing unless it is overridden during class definition. - 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 + Example: - @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')); + ```javascript + App.Person = Ember.Object.extend({ + init: function() { + alert('Name is ' + this.get('name')); + } + }); - this._insertElementLater(function() { - jQuery(target).empty(); - this.$().appendTo(target); + var steve = App.Person.create({ + name: "Steve" }); - return this; - }, + // alerts 'Name is Steve'. + ``` + + 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. + + @method init + */ + init: function() {}, /** - Schedules a DOM operation to occur during the next render phase. This - ensures that all bindings have finished synchronizing before the view is - rendered. + Defines the properties that will be concatenated from the superclass + (instead of overridden). - To use, pass a function that performs a DOM operation. + 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`. - 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. + Here is some sample code showing the difference between a concatenated + property and a normal one: ```javascript - view._insertElementLater(function() { - this.createElement(); - this.$().appendTo('body'); + App.BarView = Ember.View.extend({ + someNonConcatenatedProperty: ['bar'], + classNames: ['bar'] }); + + App.FooBarView = App.BarView.extend({ + someNonConcatenatedProperty: ['foo'], + classNames: ['foo'] + }); + + var fooBarView = App.FooBarView.create(); + fooBarView.get('someNonConcatenatedProperty'); // ['foo'] + fooBarView.get('classNames'); // ['ember-view', 'bar', 'foo'] ``` - @method _insertElementLater - @param {Function} fn the function that inserts the element into the DOM - @private - */ - _insertElementLater: function(fn) { - this._scheduledInsert = run.scheduleOnce('render', this, '_insertElement', fn); - }, + This behavior extends to object creation as well. Continuing the + above example: - _insertElement: function (fn) { - this._scheduledInsert = null; - this.currentState.insertElement(this, fn); - }, + ```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: - /** - Appends the view's element to the document body. If the view does - not have an HTML representation yet, `createElement()` will be called - automatically. + ```javascript + var view = App.FooBarView.create({ + classNames: 'baz' + }) + view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz'] + ``` - If your application uses the `rootElement` property, you must append - the view within that element. Rendering views outside of the `rootElement` - is not supported. + Using the `concatenatedProperties` property, we can tell to Ember that mix + the content of the properties. - 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. + In `Ember.View` the `classNameBindings` and `attributeBindings` properties + are also concatenated, in addition to `classNames`. - @method append - @return {Ember.View} receiver + 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). + + @property concatenatedProperties + @type Array + @default null */ - append: function() { - return this.appendTo(document.body); - }, + concatenatedProperties: null, /** - Removes the view's element from the element to which it is attached. + Destroyed object property flag. - @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(); } - }); - }, + if this property is `true` the observers and bindings were already + removed by the effect of calling the `destroy()` method. - elementId: null, + @property isDestroyed + @default false + */ + isDestroyed: false, /** - 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. + Destruction scheduled flag. The `destroy()` method has been called. - @method findElementInParentElement - @param {DOMElement} parentElement The parent's DOM element - @return {DOMElement} The discovered element + The object stays intact until the end of the run loop at which point + the `isDestroyed` flag is set. + + @property isDestroying + @default false */ - findElementInParentElement: function(parentElem) { - var id = "#" + this.elementId; - return jQuery(id)[0] || jQuery(id, parentElem)[0]; - }, + isDestroying: false, /** - Creates a DOM representation of the view and all of its - child views by recursively calling the `render()` method. + Destroys an object by setting the `isDestroyed` flag and removing its + metadata, which effectively destroys observers and bindings. - After the element has been created, `didInsertElement` will - be called on this view and all of its child views. + If you try to set a property on a destroyed object, an exception will be + raised. - @method createElement - @return {Ember.View} receiver - */ - createElement: function() { - if (get(this, 'element')) { return this; } + Note that destruction is scheduled for the end of the run loop and does not + happen immediately. It will set an isDestroying flag immediately. - var buffer = this.renderToBuffer(); - set(this, 'element', buffer.element()); + @method destroy + @return {Ember.Object} receiver + */ + destroy: function() { + if (this.isDestroying) { return; } + this.isDestroying = true; + schedule('actions', this, this.willDestroy); + schedule('destroy', this, this._scheduledDestroy); return this; }, /** - Called when a view is going to insert an element into the DOM. + Override to implement teardown. - @event willInsertElement - */ - willInsertElement: Ember.K, + @method willDestroy + */ + willDestroy: K, /** - 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. + Invoked by the run loop to actually destroy the object. This is + scheduled for execution by the `destroy` method. - @event didInsertElement + @private + @method _scheduledDestroy */ - didInsertElement: Ember.K, + _scheduledDestroy: function() { + if (this.isDestroyed) { return; } + destroy(this); + this.isDestroyed = true; + }, + + bind: function(to, from) { + if (!(from instanceof Binding)) { from = Binding.from(from); } + from.to(to).connect(this); + return from; + }, /** - 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 + Returns a string representation which attempts to provide more information + than Javascript's `toString` typically does, in a generic way for all Ember + objects. - @event willClearRender - */ - willClearRender: Ember.K, + ```javascript + App.Person = Em.Object.extend() + person = App.Person.create() + person.toString() //=> "" + ``` - /** - Run this callback on the current view (unless includeSelf is false) and recursively on child views. + If the object's class is not defined on an Ember namespace, it will + indicate it is a subclass of the registered superclass: - @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; + ```javascript + Student = App.Person.extend() + student = Student.create() + student.toString() //=> "<(subclass of App.Person):ember1025>" + ``` - while (childViews.length) { - currentViews = childViews.slice(); - childViews = []; + If the method `toStringExtension` is defined, its return value will be + included in the output. - for (var i=0, l=currentViews.length; i "" + ``` - triggerRecursively: function(eventName) { - var childViews = [this], currentViews, view, currentChildViews; + @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+'>'; - while (childViews.length) { - currentViews = childViews.slice(); - childViews = []; + this.toString = makeToString(ret); + return ret; + } + }); - for (var i=0, l=currentViews.length; i 0) { + var args = new Array(l); + for (var i = 0; i < l; i++) { + args[i] = arguments[i]; + } + this._initMixins(args); + } + return new C(); + }, /** - 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. + Creates an instance of a class. Accepts either no arguments, or an object + containing values to initialize the newly instantiated object with. - By default, the render buffer will use a `
    ` tag for views. + ```javascript + App.Person = Ember.Object.extend({ + helloWorld: function() { + alert("Hi, my name is " + this.get('name')); + } + }); - @property tagName - @type String - @default null - */ + var tom = App.Person.create({ + name: 'Tom Dale' + }); - // We leave this null by default so we can tell the difference between - // the default case and a user-specified tag. - tagName: null, + tom.helloWorld(); // alerts "Hi, my name is Tom Dale". + ``` - /** - 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. + `create` will call the `init` function if defined during + `Ember.AnyObject.extend` - 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) + If no arguments are passed to `create`, it will not set values to the new + instance during initialization: - @property ariaRole - @type String - @default null - */ - ariaRole: null, + ```javascript + var noName = App.Person.create(); + noName.helloWorld(); // alerts undefined + ``` - /** - 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. + 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. - @property classNames - @type Array - @default ['ember-view'] + @method create + @static + @param [arguments]* */ - classNames: ['ember-view'], + 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(); + }, /** - 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. + Augments a constructor's prototype with additional + properties and functions: ```javascript - // Applies the 'high' class to the view element - Ember.View.extend({ - classNameBindings: ['priority'] - priority: 'high' + MyObject = Ember.Object.extend({ + name: 'an object' }); - ``` - If the value of the property is a Boolean, the name of that property is - added as a dasherized class name. + o = MyObject.create(); + o.get('name'); // 'an object' - ```javascript - // Applies the 'is-urgent' class to the view element - Ember.View.extend({ - classNameBindings: ['isUrgent'] - isUrgent: true - }); - ``` + MyObject.reopen({ + say: function(msg){ + console.log(msg); + } + }) - If you would prefer to use a custom value instead of the dasherized - property name, you can pass a binding like this: + o2 = MyObject.create(); + o2.say("hello"); // logs "hello" - ```javascript - // Applies the 'urgent' class to the view element - Ember.View.extend({ - classNameBindings: ['isUrgent:urgent'] - isUrgent: true - }); + o.say("goodbye"); // logs "goodbye" ``` - This list of properties is inherited from the view's superclasses as well. + To add functions and properties to the constructor itself, + see `reopenClass` - @property classNameBindings - @type Array - @default [] + @method reopen */ - classNameBindings: EMPTY_ARRAY, + reopen: function() { + this.willReopen(); + + var l = arguments.length; + var args = new Array(l); + if (l > 0) { + for (var i = 0; i < l; i++) { + args[i] = arguments[i]; + } + } + + apply(this.PrototypeMixin, reopen, args); + return this; + }, /** - 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. + Augments a constructor's own properties and functions: ```javascript - // Applies the type attribute to the element - // with the value "button", like
    - Ember.View.extend({ - attributeBindings: ['type'], - type: 'button' + MyObject = Ember.Object.extend({ + name: 'an object' + }); + + MyObject.reopenClass({ + canBuild: false }); + + MyObject.canBuild; // false + o = MyObject.create(); ``` - If the value of the property is a Boolean, the name of that property is - added as an attribute. + 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 - // Renders something like
    - Ember.View.extend({ - attributeBindings: ['enabled'], - enabled: true + App.Person = Ember.Object.extend({ + name : "", + sayHello : function(){ + alert("Hello. My name is " + this.get('name')); + } }); - ``` - - @property attributeBindings - */ - attributeBindings: EMPTY_ARRAY, - // ....................................................... - // CORE DISPLAY METHODS - // + App.Person.reopenClass({ + species : "Homo sapiens", + createPerson: function(newPersonsName){ + return App.Person.create({ + name:newPersonsName + }); + } + }); - /** - Setup a view, but do not finish waking it up. + var tom = App.Person.create({ + name : "Tom Dale" + }); + var yehuda = App.Person.createPerson("Yehuda Katz"); - * configure `childViews` - * register the view with the global views hash, which is used for event - dispatch + tom.sayHello(); // "Hello. My name is Tom Dale" + yehuda.sayHello(); // "Hello. My name is Yehuda Katz" + alert(App.Person.species); // "Homo sapiens" + ``` - @method init - @private - */ - init: function() { - this.elementId = this.elementId || guidFor(this); + Note that `species` and `createPerson` are *not* valid on the `tom` and `yehuda` + variables. They are only valid on `App.Person`. - this._super(); + To add functions and properties to instances of + a constructor by extending the constructor's prototype + see `reopen` - // setup child views. be sure to clone the child views array first - this._childViews = this._childViews.slice(); + @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]; + } + } - Ember.assert("Only arrays are allowed for 'classNameBindings'", typeOf(this.classNameBindings) === 'array'); - this.classNameBindings = A(this.classNameBindings.slice()); + apply(this.ClassMixin, reopen, args); + applyMixin(this, arguments, false); + return this; + }, - Ember.assert("Only arrays are allowed for 'classNames'", typeOf(this.classNames) === 'array'); - this.classNames = A(this.classNames.slice()); + detect: function(obj) { + if ('function' !== typeof obj) { return false; } + while(obj) { + if (obj===this) { return true; } + obj = obj.superclass; + } + return false; }, - appendChild: function(view, options) { - return this.currentState.appendChild(this, view, options); + detectInstance: function(obj) { + return obj instanceof this; }, /** - Removes the child view from the parent view. - - @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; } - - // update parent node - set(view, '_parentView', null); + 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. - // remove view from childViews array. - var childViews = this._childViews; + You can pass a hash of these values to a computed property like this: - a_removeObject(childViews, view); + ```javascript + person: function() { + var personId = this.get('personId'); + return App.Person.create({ id: personId }); + }.property().meta({ type: App.Person }) + ``` - this.propertyDidChange('childViews'); // HUH?! what happened to will change? + Once you've done this, you can retrieve the values saved to the computed + property from your class like this: - return this; - }, + ```javascript + MyClass.metaForProperty('person'); + ``` - /** - Removes all children from the `parentView`. + This will return the original hash that was passed to `meta()`. - @method removeAllChildren - @return {Ember.View} receiver + @method metaForProperty + @param key {String} property name */ - removeAllChildren: function() { - return this.mutateChildViews(function(parentView, view) { - parentView.removeChild(view); - }); - }, + metaForProperty: function(key) { + var meta = this.proto()['__ember_meta__']; + var desc = meta && meta.descs[key]; - destroyAllChildren: function() { - return this.mutateChildViews(function(parentView, view) { - view.destroy(); - }); + Ember.assert("metaForProperty() could not find a computed property with key '"+key+"'.", !!desc && desc instanceof ComputedProperty); + return desc._meta || {}; }, - /** - Removes the view from its `parentView`, if one is found. Otherwise - does nothing. - - @method removeFromParent - @return {Ember.View} receiver - */ - removeFromParent: function() { - var parent = this._parentView; + _computedProperties: computed(function() { + hasCachedComputedProperties = true; + var proto = this.proto(); + var descs = meta(proto).descs; + var property; + var properties = []; - // Remove DOM element from parent - this.remove(); + for (var name in descs) { + property = descs[name]; - if (parent) { parent.removeChild(this); } - return this; - }, + if (property instanceof ComputedProperty) { + properties.push({ + name: name, + meta: property._meta + }); + } + } + return properties; + }).readOnly(), /** - You must call `destroy` on a view to destroy the view (and all of its - child views). This will remove the view from any parent node, then make - sure that the DOM element managed by the view can be released by the - memory manager. + Iterate over each computed property for the class, passing its name + and any associated metadata (see `metaForProperty`) to the callback. - @method destroy + @method eachComputedProperty + @param {Function} callback + @param {Object} binding */ - destroy: function() { - var childViews = this._childViews, - // get parentView before calling super because it'll be destroyed - nonVirtualParentView = get(this, 'parentView'), - viewName = this.viewName, - childLen, i; + eachComputedProperty: function(callback, binding) { + var property, name; + var empty = {}; - if (!this._super()) { return; } + var properties = get(this, '_computedProperties'); - childLen = childViews.length; - for (i=childLen-1; i>=0; i--) { - childViews[i].removedFromDOM = true; + 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); } + } + }; - // remove from non-virtual parent view if viewName was specified - if (viewName && nonVirtualParentView) { - nonVirtualParentView.set(viewName, null); - } + function injectedPropertyAssertion() { + Ember.assert("Injected properties are invalid", validatePropertyInjections(this)); + } - childLen = childViews.length; - for (i=childLen-1; i>=0; i--) { - childViews[i].destroy(); - } + function addOnLookupHandler() { + Ember.runInDebug(function() { + /** + Provides lookup-time type validation for injected properties. - return this; - }, + @private + @method _onLookup + */ + ClassMixinProps._onLookup = injectedPropertyAssertion; + }); + } + + + addOnLookupHandler(); /** - 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. + Returns a hash of property names and container names that injected + properties will lookup on the container lazily. - @method createChildView - @param {Class|String} viewClass - @param {Hash} [attrs] Attributes to add - @return {Ember.View} new instance + @method _lazyInjections + @return {Object} Hash of all lazy injected property keys to container names */ - createChildView: function(view, attrs) { - if (!view) { - throw new TypeError("createChildViews first argument must exist"); - } + ClassMixinProps._lazyInjections = function() { + var injections = {}; + var proto = this.proto(); + var descs = meta(proto).descs; + var key, desc; - if (view.isView && view._parentView === this && view.container === this.container) { - return view; + for (key in descs) { + desc = descs[key]; + if (desc instanceof InjectedProperty) { + injections[key] = desc.type + ':' + (desc.name || key); + } } - attrs = attrs || {}; - attrs._parentView = this; + return injections; + }; + - if (CoreView.detect(view)) { - attrs.templateData = attrs.templateData || get(this, 'templateData'); + var ClassMixin = Mixin.create(ClassMixinProps); - attrs.container = this.container; - view = view.create(attrs); + ClassMixin.ownerConstructor = CoreObject; - // 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); + CoreObject.ClassMixin = ClassMixin; - Ember.assert("Could not find view: '" + fullName + "'", !!ViewKlass); + ClassMixin.apply(CoreObject); - attrs.templateData = get(this, 'templateData'); - view = ViewKlass.create(attrs); - } else { - Ember.assert('You must pass instance or subclass of View', view.isView); - attrs.container = this.container; + CoreObject.reopen({ + didDefineProperty: function(proto, key, value) { + if (hasCachedComputedProperties === false) { return; } + if (value instanceof Ember.ComputedProperty) { + var cache = Ember.meta(this.constructor).cache; - if (!get(view, 'templateData')) { - attrs.templateData = get(this, 'templateData'); + if (cache._computedProperties !== undefined) { + cache._computedProperties = undefined; } - - setProperties(view, attrs); - } + } + }); - return view; - }, - - becameVisible: Ember.K, - becameHidden: Ember.K, - - /** - When the view's `isVisible` property changes, toggle the visibility - element of the actual DOM element. + __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"]; - @method _isVisibleDidChange - @private - */ - _isVisibleDidChange: observer('isVisible', function() { - if (this._isVisible === get(this, 'isVisible')) { return ; } - run.scheduleOnce('render', this, this._toggleVisibility); - }), + var Deferred = EmberObject.extend(DeferredMixin, { + init: function() { + Ember.deprecate('Usage of Ember.Deferred is deprecated.'); + this._super(); + } + }); - _toggleVisibility: function() { - var $el = this.$(); - if (!$el) { return; } + Deferred.reopenClass({ + promise: function(callback, binding) { + var deferred = Deferred.create(); + callback.call(binding, deferred); + return deferred; + } + }); - var isVisible = get(this, 'isVisible'); + __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 + */ - if (this._isVisible === isVisible) { return ; } + var Ember = __dependency1__["default"]; + // Ember.assert - $el.toggle(isVisible); + 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; - this._isVisible = isVisible; + var EachArray = EmberObject.extend(EmberArray, { - if (this._isAncestorHidden()) { return; } + init: function(content, keyName, owner) { + this._super(); + this._keyName = keyName; + this._owner = owner; + this._content = content; + }, - if (isVisible) { - this._notifyBecameVisible(); - } else { - this._notifyBecameHidden(); - } + objectAt: function(idx) { + var item = this._content.objectAt(idx); + return item && get(item, this._keyName); }, - _notifyBecameVisible: function() { - this.trigger('becameVisible'); + length: computed(function() { + var content = this._content; + return content ? get(content, 'length') : 0; + }) - this.forEachChildView(function(view) { - var isVisible = get(view, 'isVisible'); + }); - if (isVisible || isVisible === null) { - view._notifyBecameVisible(); - } - }); - }, + var IS_OBSERVER = /^.+:(before|change)$/; - _notifyBecameHidden: function() { - this.trigger('becameHidden'); - this.forEachChildView(function(view) { - var isVisible = get(view, 'isVisible'); + function addObserverForContentKey(content, keyName, proxy, idx, loc) { + var objects = proxy._objects; + var guid; + if (!objects) objects = proxy._objects = {}; - if (isVisible || isVisible === null) { - view._notifyBecameHidden(); - } - }); - }, + 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'); - _isAncestorHidden: function() { - var parent = get(this, 'parentView'); + // 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); + } + } + } - while (parent) { - if (get(parent, 'isVisible') === false) { return true; } + function removeObserverForContentKey(content, keyName, proxy, idx, loc) { + var objects = proxy._objects; + if (!objects) objects = proxy._objects = {}; + var indicies, guid; - parent = get(parent, 'parentView'); - } + while(--loc>=idx) { + var item = content.objectAt(loc); + if (item) { + removeBeforeObserver(item, keyName, proxy, 'contentKeyWillChange'); + removeObserver(item, keyName, proxy, 'contentKeyDidChange'); - return false; - }, + guid = guidFor(item); + indicies = objects[guid]; + indicies[indexOf.call(indicies, loc)] = null; + } + } + } - clearBuffer: function() { - this.invokeRecursively(nullViewsBuffer); - }, + /** + 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. - transitionTo: function(state, children) { - var priorState = this.currentState, - currentState = this.currentState = this.states[state]; - this.state = state; + @private + @class EachProxy + @namespace Ember + @extends Ember.Object + */ + var EachProxy = EmberObject.extend({ - if (priorState && priorState.exit) { priorState.exit(this); } - if (currentState.enter) { currentState.enter(this); } - if (state === 'inDOM') { meta(this).cache.element = undefined; } + init: function(content) { + this._super(); + this._content = content; + content.addArrayObserver(this); - if (children !== false) { - this.forEachChildView(function(view) { - view.transitionTo(state); - }); - } + // in case someone is already observing some keys make sure they are + // added + forEach(watchedEvents(this), function(eventName) { + this.didAddListener(eventName); + }, this); }, - // ....................................................... - // EVENT HANDLING - // - /** - Handle events from `Ember.EventDispatcher` + You can directly access mapped properties by simply requesting them. + The `unknownProperty` handler will generate an EachArray of each item. - @method handleEvent - @param eventName {String} - @param evt {Event} - @private + @method unknownProperty + @param keyName {String} + @param value {*} */ - handleEvent: function(eventName, evt) { - return this.currentState.handleEvent(this, eventName, evt); + unknownProperty: function(keyName, value) { + var ret; + ret = new EachArray(this._content, keyName, this); + defineProperty(this, keyName, null, ret); + this.beginObservingContentKey(keyName); + return ret; }, - registerObserver: function(root, path, target, observer) { - if (!observer && 'function' === typeof target) { - observer = target; - target = null; - } - - 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); - }); - } - - }); - - /* - Describe how the specified actions should behave in the various - states that a view can exist in. Possible states: - - * 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. - */ - - // in the destroyed state, everything is illegal + // .......................................................... + // ARRAY CHANGES + // Invokes whenever the content array itself changes. - // before rendering has begun, all legal manipulations are noops. + arrayWillChange: function(content, idx, removedCnt, addedCnt) { + var keys = this._keys; + var key, lim; - // inside the buffer, legal manipulations are done on the buffer + lim = removedCnt>0 ? idx+removedCnt : -1; + beginPropertyChanges(this); - // once the view has been inserted into the DOM, legal manipulations - // are done on the DOM element. + for(key in keys) { + if (!keys.hasOwnProperty(key)) { continue; } - function notifyMutationListeners() { - run.once(View, 'notifyMutationListeners'); - } + if (lim>0) { removeObserverForContentKey(content, key, this, idx, lim); } - var DOMManager = { - prepend: function(view, html) { - view.$().prepend(html); - notifyMutationListeners(); - }, + propertyWillChange(this, key); + } - after: function(view, html) { - view.$().after(html); - notifyMutationListeners(); + propertyWillChange(this._content, '@each'); + endPropertyChanges(this); }, - html: function(view, html) { - view.$().html(html); - notifyMutationListeners(); - }, + arrayDidChange: function(content, idx, removedCnt, addedCnt) { + var keys = this._keys; + var lim; - replace: function(view) { - var element = get(view, 'element'); + lim = addedCnt>0 ? idx+addedCnt : -1; + changeProperties(function() { + for(var key in keys) { + if (!keys.hasOwnProperty(key)) { continue; } - set(view, 'element', null); + if (lim>0) { addObserverForContentKey(content, key, this, idx, lim); } - view._insertElementLater(function() { - jQuery(element).replaceWith(get(view, 'element')); - notifyMutationListeners(); - }); - }, + propertyDidChange(this, key); + } - remove: function(view) { - view.$().remove(); - notifyMutationListeners(); + propertyDidChange(this._content, '@each'); + }, this); }, - empty: function(view) { - view.$().empty(); - notifyMutationListeners(); - } - }; + // .......................................................... + // LISTEN FOR NEW OBSERVERS AND OTHER EVENT LISTENERS + // Start monitoring keys based on who is listening... - View.reopen({ - domManager: DOMManager - }); + didAddListener: function(eventName) { + if (IS_OBSERVER.test(eventName)) { + this.beginObservingContentKey(eventName.slice(0, -7)); + } + }, - View.reopenClass({ + didRemoveListener: function(eventName) { + if (IS_OBSERVER.test(eventName)) { + this.stopObservingContentKey(eventName.slice(0, -7)); + } + }, - /** - Parse a path and return an object which holds the parsed properties. + // .......................................................... + // CONTENT KEY OBSERVING + // Actual watch keys on the source content. - For example a path like "content.isEnabled:enabled:disabled" will return the - following object: + 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'); - ```javascript - { - path: "content.isEnabled", - className: "enabled", - falsyClassName: "disabled", - classNames: ":enabled:disabled" + addObserverForContentKey(content, keyName, this, 0, len); + } else { + keys[keyName]++; } - ``` - - @method _parsePropertyPath - @static - @private - */ - _parsePropertyPath: function(path) { - var split = path.split(':'), - propertyPath = split[0], - classNames = "", - className, - falsyClassName; + }, - // 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]; } + stopObservingContentKey: function(keyName) { + var keys = this._keys; + if (keys && (keys[keyName]>0) && (--keys[keyName]<=0)) { + var content = this._content; + var len = get(content, 'length'); - classNames = ':' + className; - if (falsyClassName) { classNames += ":" + falsyClassName; } + removeObserverForContentKey(content, keyName, this, 0, len); } - - return { - path: propertyPath, - classNames: classNames, - className: (className === '') ? undefined : className, - falsyClassName: falsyClassName - }; }, - /** - Get the class name for a given value, based on the path, optional - `className` and optional `falsyClassName`. + contentKeyWillChange: function(obj, keyName) { + propertyWillChange(this, keyName); + }, - - 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 + contentKeyDidChange: function(obj, keyName) { + propertyDidChange(this, keyName); + } + }); - @method _classStringForValue - @param path - @param val - @param className - @param falsyClassName - @static - @private - */ - _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; + __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 */ - } else if (falsyClassName && !val) { - return falsyClassName; + var Ember = __dependency1__["default"]; + // Ember.ENV.EMBER_LOAD_HOOKS + var forEach = __dependency2__.forEach; + // make sure Ember.A is setup. - } else { - return null; - } + /** + @module ember + @submodule ember-runtime + */ - // 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]); + var loadHooks = Ember.ENV.EMBER_LOAD_HOOKS || {}; + var loaded = {}; - // If the value is not false, undefined, or null, return the current - // value of the property. - } else if (val !== false && val != null) { - return val; + /** + Detects when a specific package of Ember (e.g. 'Ember.Handlebars') + has fully loaded and is available for extension. - // Nothing to display. Return null so that the old class is removed - // but no new class is added. - } else { - return null; - } - } - }); + The provided `callback` will be called with the `name` passed + resolved from a string into the object: - var mutation = EmberObject.extend(Evented).create(); + ``` javascript + Ember.onLoad('Ember.Handlebars' function(hbars) { + hbars.registerHelper(...); + }); + ``` - View.addMutationListener = function(callback) { - mutation.on('change', callback); - }; + @method onLoad + @for Ember + @param name {String} name of hook + @param callback {Function} callback to be called + */ + function onLoad(name, callback) { + var object; - View.removeMutationListener = function(callback) { - mutation.off('change', callback); - }; + loadHooks[name] = loadHooks[name] || Ember.A(); + loadHooks[name].pushObject(callback); - View.notifyMutationListeners = function() { - mutation.trigger('change'); - }; + if (object = loaded[name]) { + callback(object); + } + } - /** - Global views hash + __exports__.onLoad = onLoad;/** + Called when an Ember.js package (e.g Ember.Handlebars) has finished + loading. Triggers any callbacks registered for this event. - @property views - @static - @type Hash + @method runLoadHooks + @for Ember + @param name {String} name of hook + @param object {Object} object to pass to callbacks */ - View.views = {}; - - // 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; + function runLoadHooks(name, object) { + loaded[name] = object; - View.applyAttributeBindings = function(elem, name, value) { - var type = typeOf(value); + if (typeof window === 'object' && typeof window.dispatchEvent === 'function' && typeof CustomEvent === "function") { + var event = new CustomEvent(name, {detail: object, name: name}); + window.dispatchEvent(event); + } - // 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); + if (loadHooks[name]) { + forEach.call(loadHooks[name], function(callback) { + callback(object); + }); } - }; + } - __exports__.CoreView = CoreView; - __exports__.View = View; - __exports__.ViewCollection = ViewCollection; + __exports__.runLoadHooks = runLoadHooks; }); -})(); - -(function() { -define("metamorph", - [], - function() { +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"; - // ========================================================================== - // 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. + /** + @module ember + @submodule ember-runtime + */ - var Metamorph = function(html) { - var self; + // 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; - if (this instanceof Metamorph) { - self = this; - } else { - self = new K(); - } + var EmberObject = __dependency6__["default"]; - self.innerHTML = html; - var myGuid = 'metamorph-'+(guid++); - self.start = myGuid + '-start'; - self.end = myGuid + '-end'; + /** + 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. - return self; - }; + # Example Usage - K.prototype = Metamorph.prototype; + ```javascript + MyFramework = Ember.Namespace.create({ + VERSION: '1.0.0' + }); + ``` - var rangeFor, htmlFunc, removeFunc, outerHTMLFunc, appendToFunc, afterFunc, prependFunc, startTagFunc, endTagFunc; + @class Namespace + @namespace Ember + @extends Ember.Object + */ + var Namespace = EmberObject.extend({ + isNamespace: true, - outerHTMLFunc = function() { - return this.startTag() + this.innerHTML + this.endTag(); - }; + init: function() { + Namespace.NAMESPACES.push(this); + Namespace.PROCESSED = false; + }, - 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; + for (var i=0, l=namespaces.length; i. 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); - } - }; + function makeToString(ret) { + return function() { return ret; }; + } - 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; } - } + Mixin.prototype.toString = classToString; // ES6TODO: altering imported objects. SBB. - node.parentNode.removeChild(node); + __exports__["default"] = Namespace; + }); +enifed("ember-runtime/system/native_array", + ["ember-metal/core","ember-metal/property_get","ember-metal/enumerable_utils","ember-metal/mixin","ember-metal/array","ember-runtime/mixins/array","ember-runtime/mixins/mutable_array","ember-runtime/mixins/observable","ember-runtime/mixins/copyable","ember-runtime/mixins/freezable","ember-runtime/copy","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-runtime + */ - // 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; } + var Ember = __dependency1__["default"]; + // Ember.EXTEND_PROTOTYPES - node = nextSibling; - } + var get = __dependency2__.get; + var replace = __dependency3__._replace; + var forEach = __dependency3__.forEach; + var Mixin = __dependency4__.Mixin; + var indexOf = __dependency5__.indexOf; + var lastIndexOf = __dependency5__.lastIndexOf; + var EmberArray = __dependency6__["default"]; + var MutableArray = __dependency7__["default"]; + var Observable = __dependency8__["default"]; + var Copyable = __dependency9__["default"]; + var FROZEN_ERROR = __dependency10__.FROZEN_ERROR; + var copy = __dependency11__["default"]; - // 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); + // Add Ember.Array to Array.prototype. Remove methods with native + // implementations and supply some more optimized versions of generic methods + // because they are so common. - if (outerToo) { - start.parentNode.removeChild(start); - } + /** + The NativeArray mixin contains the properties needed to to make the native + Array support Ember.MutableArray and all of its dependent APIs. Unless you + have `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Array` set to + false, this will be applied automatically. Otherwise you can apply the mixin + at anytime by calling `Ember.NativeArray.activate`. - // copy the nodes for the HTML between the starting and ending - // placeholder. - while (node) { - nextSibling = node.nextSibling; - parentNode.insertBefore(node, end); - node = nextSibling; - } - }; + @class NativeArray + @namespace Ember + @uses Ember.MutableArray + @uses Ember.Observable + @uses Ember.Copyable + */ + var NativeArray = Mixin.create(MutableArray, Observable, Copyable, { - // 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); - }; + // because length is a built-in property we need to know to just get the + // original property. + get: function(key) { + if (key==='length') return this.length; + else if ('number' === typeof key) return this[key]; + else return this._super(key); + }, + + objectAt: function(idx) { + return this[idx]; + }, + + // primitive for array support. + replace: function(idx, amt, objects) { + + if (this.isFrozen) throw FROZEN_ERROR; - appendToFunc = function(parentNode) { - var node = firstNodeFor(parentNode, this.outerHTML()); - var nextSibling; + // if we replaced exactly the same number of items, then pass only the + // replaced range. Otherwise, pass the full remaining array length + // since everything has shifted + var len = objects ? get(objects, 'length') : 0; + this.arrayContentWillChange(idx, amt, len); - while (node) { - nextSibling = node.nextSibling; - parentNode.appendChild(node); - node = nextSibling; + if (len === 0) { + this.splice(idx, amt); + } else { + replace(this, idx, amt, objects); } - }; - 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; + this.arrayContentDidChange(idx, amt, len); + return this; + }, + + // If you ask for an unknown property, then try to collect the value + // from member items. + unknownProperty: function(key, value) { + var ret;// = this.reducedProperty(key, value) ; + if (value !== undefined && ret === undefined) { + ret = this[key] = value; } - }; + return ret; + }, - prependFunc = function(html) { - var start = document.getElementById(this.start); - var parentNode = start.parentNode; - var nextSibling; - var node; + indexOf: indexOf, - node = firstNodeFor(parentNode, html); - var insertBefore = start.nextSibling; + lastIndexOf: lastIndexOf, - while (node) { - nextSibling = node.nextSibling; - parentNode.insertBefore(node, insertBefore); - node = nextSibling; + copy: function(deep) { + if (deep) { + return this.map(function(item) { return copy(item, true); }); } - }; + + return this.slice(); + } + }); + + // Remove any methods implemented natively so we don't override them + var ignore = ['length']; + forEach(NativeArray.keys(), function(methodName) { + if (Array.prototype[methodName]) ignore.push(methodName); + }); + + if (ignore.length > 0) { + NativeArray = NativeArray.without.apply(NativeArray, ignore); } - Metamorph.prototype.html = function(html) { - this.checkRemoved(); - if (html === undefined) { return this.innerHTML; } + /** + 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`. + + Example + + ```js + var Pagination = Ember.CollectionView.extend({ + tagName: 'ul', + classNames: ['pagination'], - htmlFunc.call(this, html); + init: function() { + this._super(); + if (!this.get('content')) { + this.set('content', Ember.A()); + } + } + }); + ``` - this.innerHTML = html; + @method A + @for Ember + @return {Ember.NativeArray} + */ + var A = function(arr) { + if (arr === undefined) { arr = []; } + return EmberArray.detect(arr) ? arr : NativeArray.apply(arr); }; - Metamorph.prototype.replaceWith = function(html) { - this.checkRemoved(); - htmlFunc.call(this, html, true); - }; + /** + 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`. - 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; + Example - Metamorph.prototype.isRemoved = function() { - var before = document.getElementById(this.start); - var after = document.getElementById(this.end); + ```js + if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Array) { + Ember.NativeArray.activate(); + } + ``` - return !before || !after; - }; + @method activate + @for Ember.NativeArray + @static + @return {void} + */ + NativeArray.activate = function() { + NativeArray.apply(Array.prototype); - Metamorph.prototype.checkRemoved = function() { - if (this.isRemoved()) { - throw new Error("Cannot perform operations on a Metamorph that is not in the DOM."); - } + A = function(arr) { return arr || []; }; }; - return Metamorph; - }); - -})(); + if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Array) { + NativeArray.activate(); + } -(function() { -define("ember-handlebars-compiler", - ["ember-metal/core","exports"], - function(__dependency1__, __exports__) { + 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-handlebars-compiler + @submodule ember-runtime */ - var Ember = __dependency1__["default"]; + var CoreObject = __dependency1__["default"]; + var Observable = __dependency2__["default"]; - // 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(){} }; }; + /** + `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. - var objectCreate = Object.create || function(parent) { - function F() {} - F.prototype = parent; - return new F(); + @class Object + @namespace Ember + @extends Ember.CoreObject + @uses Ember.Observable + */ + var EmberObject = CoreObject.extend(Observable); + EmberObject.toString = function() { + return "Ember.Object"; }; - // 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); + __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"]; /** - Prepares the Handlebars templating library for use inside Ember's view - system. + `Ember.ObjectProxy` forwards all properties not defined by the proxy itself + to a proxied `content` object. - 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. + ```javascript + object = Ember.Object.create({ + name: 'Foo' + }); - To create an `Ember.Handlebars` template, call `Ember.Handlebars.compile()`. - This will return a function that can be used by `Ember.View` for rendering. + proxy = Ember.ObjectProxy.create({ + content: object + }); - @class Handlebars - @namespace Ember - */ - var EmberHandlebars = Ember.Handlebars = objectCreate(Handlebars); + // Access and change existing properties + proxy.get('name') // 'Foo' + proxy.set('name', 'Bar'); + object.get('name') // 'Bar' - /** - Register a bound helper or custom view helper. + // Create new 'description' property on `object` + proxy.set('description', 'Foo is a whizboo baz'); + object.get('description') // 'Foo is a whizboo baz' + ``` - ## Simple bound helper example + While `content` is unset, setting a property to be delegated will throw an + Error. ```javascript - Ember.Handlebars.helper('capitalize', function(value) { - return value.toUpperCase(); + 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 ``` - The above bound helper can be used inside of templates as follows: - - ```handlebars - {{capitalize name}} - ``` + Delegated properties can be bound to and will change when content is updated. - 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. + Computed properties on the proxy itself can depend on delegated properties. - For more examples of bound helpers, see documentation for - `Ember.Handlebars.registerBoundHelper`. + ```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') + }); - ## Custom view helper example + proxy = ProxyWithComputedProperty.create(); - Assuming a view subclass named `App.CalendarView` were defined, a helper - for rendering instances of this view could be registered as follows: + proxy.get('fullName'); // undefined + proxy.set('content', { + firstName: 'Tom', lastName: 'Dale' + }); // triggers property change for fullName on proxy - ```javascript - Ember.Handlebars.helper('calendar', App.CalendarView): + proxy.get('fullName'); // 'Tom Dale' ``` - The above bound helper can be used inside of templates as follows: - - ```handlebars - {{calendar}} - ``` + @class ObjectProxy + @namespace Ember + @extends Ember.Object + @extends Ember._ProxyMixin + */ - Which is functionally equivalent to: + __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; - ```handlebars - {{view App.CalendarView}} - ``` + var Service; - Options in the helper will be passed to the view in exactly the same - manner as with the `view` helper. + + /** + @class Service + @namespace Ember + @extends Ember.Object + */ + Service = Object.extend(); - @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 + /** + 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. - Ember.assert("You tried to register a component named '" + name + "', but component names must include a '-'", !Component.detect(value) || name.match(/-/)); + Example: - if (View.detect(value)) { - EmberHandlebars.registerHelper(name, EmberHandlebars.makeViewHelper(value)); - } else { - EmberHandlebars.registerBoundHelper.apply(null, arguments); - } - }; + ```javascript + App.ApplicationRoute = Ember.Route.extend({ + authManager: Ember.inject.service('auth'), - /** - Returns a helper function that renders the provided ViewClass. + model: function() { + return this.get('authManager').findCurrentUser(); + } + }); + ``` - Used internally by Ember.Handlebars.helper and other methods - involving helper/component registration. + 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. - @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); - }; - }; + @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 + */ + createInjectionHelper('service'); + + __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"; /** - @class helpers - @namespace Ember.Handlebars + @module ember + @submodule ember-runtime */ - EmberHandlebars.helpers = objectCreate(Handlebars.helpers); + var Ember = __dependency1__["default"]; + // Ember.isNone, Ember.A + + 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; /** - Override the the opcode compiler and JavaScript compiler for Handlebars. + An unordered collection of objects. - @class Compiler - @namespace Ember.Handlebars - @private - @constructor - */ - EmberHandlebars.Compiler = function() {}; + 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. - // Handlebars.Compiler doesn't exist in runtime-only - if (Handlebars.Compiler) { - EmberHandlebars.Compiler.prototype = objectCreate(Handlebars.Compiler.prototype); - } + All Sets are observable via the Enumerable Observer API - which works + on any enumerable object including both Sets and Arrays. - EmberHandlebars.Compiler.prototype.compiler = EmberHandlebars.Compiler; + ## Creating a Set - /** - @class JavaScriptCompiler - @namespace Ember.Handlebars - @private - @constructor - */ - EmberHandlebars.JavaScriptCompiler = function() {}; + 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. - // Handlebars.JavaScriptCompiler doesn't exist in runtime-only - if (Handlebars.JavaScriptCompiler) { - EmberHandlebars.JavaScriptCompiler.prototype = objectCreate(Handlebars.JavaScriptCompiler.prototype); - EmberHandlebars.JavaScriptCompiler.prototype.compiler = EmberHandlebars.JavaScriptCompiler; - } + 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()`. + ```javascript + // creates a new empty set + var foundNames = new Ember.Set(); - EmberHandlebars.JavaScriptCompiler.prototype.namespace = "Ember.Handlebars"; + // creates a set with four names in it. + var names = new Ember.Set(["Charles", "Tom", "Juan", "Alex"]); // :P - EmberHandlebars.JavaScriptCompiler.prototype.initializeBuffer = function() { - return "''"; - }; + // creates a copy of the names set. + var namesCopy = new Ember.Set(names); - /** - 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. + // same as above. + var anotherNamesCopy = names.copy(); + ``` - @private - @method appendToBuffer - @param string {String} - */ - EmberHandlebars.JavaScriptCompiler.prototype.appendToBuffer = function(string) { - return "data.buffer.push("+string+");"; - }; + ## Adding/Removing Objects - // 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 + 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. - var DOT_LOOKUP_REGEX = /helpers\.(.*?)\)/, - BRACKET_STRING_LOOKUP_REGEX = /helpers\['(.*?)'/, - INVOCATION_SPLITTING_REGEX = /(.*blockHelperMissing\.call\(.*)(stack[0-9]+)(,.*)/; + 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. - 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); + NOTE: You cannot add/remove `null` or `undefined` to a set. Any attempt to do + so will be ignored. - source[source.length - 1] = matches[1] + "'" + helperName + "'" + matches[3]; - }; + In addition to add/remove you can also call `push()`/`pop()`. Push behaves + just like `add()` but `pop()`, unlike `remove()` will pick an arbitrary + object, remove it and return it. This is a good way to use a set as a job + queue when you don't care which order the jobs are executed in. - var stringifyBlockHelperMissing = EmberHandlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation; + ## Testing for an Object - var originalBlockValue = EmberHandlebars.JavaScriptCompiler.prototype.blockValue; - EmberHandlebars.JavaScriptCompiler.prototype.blockValue = function() { - originalBlockValue.apply(this, arguments); - stringifyBlockHelperMissing(this.source); - }; + To test for an object's presence in a set you simply call + `Ember.Set#contains()`. - var originalAmbiguousBlockValue = EmberHandlebars.JavaScriptCompiler.prototype.ambiguousBlockValue; - EmberHandlebars.JavaScriptCompiler.prototype.ambiguousBlockValue = function() { - originalAmbiguousBlockValue.apply(this, arguments); - stringifyBlockHelperMissing(this.source); - }; + ## Observing changes - /** - 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. + When using `Ember.Set`, you can observe the `"[]"` property to be + alerted whenever the content changes. You can also add an enumerable + observer to the set to be notified of specific objects that are added and + removed from the set. See [Ember.Enumerable](/api/classes/Ember.Enumerable.html) + for more information on enumerables. - @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' }]); + This is often unhelpful. If you are filtering sets of objects, for instance, + it is very inefficient to re-filter all of the items each time the set + changes. It would be better if you could just adjust the filtered set based + on what was changed on the original set. The same issue applies to merging + sets, as well. - // 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); - } + ## Other Methods - return Handlebars.Compiler.prototype.mustache.call(this, mustache); - }; + `Ember.Set` primary implements other mixin APIs. For a complete reference + on the methods you will use with `Ember.Set`, please consult these mixins. + The most useful ones will be `Ember.Enumerable` and + `Ember.MutableEnumerable` which implement most of the common iterator + methods you are used to on Array. - /** - Used for precompilation of Ember Handlebars templates. This will not be used - during normal app execution. + Note that you can also use the `Ember.Copyable` and `Ember.Freezable` + APIs on `Ember.Set` as well. Once a set is frozen it can no longer be + modified. The benefit of this is that when you call `frozenCopy()` on it, + Ember will avoid making copies of the set. This allows you to write + code that can know with certainty when the underlying set data will or + will not be modified. - @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 + @class Set + @namespace Ember + @extends Ember.CoreObject + @uses Ember.MutableEnumerable + @uses Ember.Copyable + @uses Ember.Freezable + @since Ember 0.9 + @deprecated */ - EmberHandlebars.precompile = function(string, asObject) { - var ast = Handlebars.parse(string); + __exports__["default"] = CoreObject.extend(MutableEnumerable, Copyable, Freezable, { - var options = { - knownHelpers: { - action: true, - unbound: true, - 'bind-attr': true, - template: true, - view: true, - _triageMustache: true - }, - data: true, - stringParams: true - }; - - asObject = asObject === undefined ? true : asObject; - - var environment = new EmberHandlebars.Compiler().compile(ast, options); - return new EmberHandlebars.JavaScriptCompiler().compile(environment, options, undefined, asObject); - }; + // .......................................................... + // IMPLEMENT ENUMERABLE APIS + // - // 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. + This property will change as the number of objects in the set changes. - @method compile - @for Ember.Handlebars - @static - @param {String} string The template to compile - @return {Function} + @property length + @type number + @default 0 */ - 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); - - var template = EmberHandlebars.template(templateSpec); - template.isMethod = false; //Make sure we don't wrap templates with ._super + length: 0, - return template; - }; - } + /** + Clears the set. This is useful if you want to reuse an existing set + without having to recreate it. - __exports__["default"] = EmberHandlebars; - }); -})(); + ```javascript + var colors = new Ember.Set(["red", "green", "blue"]); + colors.length; // 3 + colors.clear(); + colors.length; // 0 + ``` -(function() { -define("ember-handlebars/component_lookup", - ["ember-runtime/system/object","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var EmberObject = __dependency1__["default"]; + @method clear + @return {Ember.Set} An empty Set + */ + clear: function() { + if (this.isFrozen) { throw new EmberError(FROZEN_ERROR); } - var ComponentLookup = EmberObject.extend({ - lookupFactory: function(name, container) { + var len = get(this, 'length'); + if (len === 0) { return this; } - container = container || this.container; + var guid; - var fullName = 'component:' + name, - templateFullName = 'template:components/' + name, - templateRegistered = container && container.has(templateFullName); + this.enumerableContentWillChange(len, 0); + propertyWillChange(this, 'firstObject'); + propertyWillChange(this, 'lastObject'); - if (templateRegistered) { - container.injection(fullName, 'layout', templateFullName); + for (var i=0; i < len; i++) { + guid = guidFor(this[i]); + delete this[guid]; + delete this[i]; } - var Component = container.lookupFactory(fullName); + set(this, 'length', 0); - // 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; - } - } - }); + propertyDidChange(this, 'firstObject'); + propertyDidChange(this, 'lastObject'); + this.enumerableContentDidChange(len, 0); - __exports__["default"] = ComponentLookup; - }); -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"]; + return this; + }, - var Ember = __dependency4__["default"]; - // Ember.assert - // var emberAssert = Ember.assert; + /** + Returns true if the passed object is also an enumerable that contains the + same objects as the receiver. - var EmberHandlebars = __dependency5__["default"]; - var helpers = EmberHandlebars.helpers; - /** - @module ember - @submodule ember-handlebars-compiler - */ + ```javascript + var colors = ["red", "green", "blue"], + same_colors = new Ember.Set(colors); - /** + same_colors.isEqual(colors); // true + same_colors.isEqual(["purple", "brown"]); // false + ``` - 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. + @method isEqual + @param {Ember.Set} obj the other object. + @return {Boolean} + */ + isEqual: function(obj) { + // fail fast + if (!Enumerable.detect(obj)) return false; - ## 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: + var loc = get(this, 'length'); + if (get(obj, 'length') !== loc) return false; - - - - - - - - - - - -
    `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` 
    + while(--loc >= 0) { + if (!obj.contains(this[loc])) return false; + } + return true; + }, - 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). + /** + 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. - ## Unbound: + This is an alias for `Ember.MutableEnumerable.addObject()`. - ```handlebars - {{input value="http://www.facebook.com"}} - ``` + ```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"] + ``` + @method add + @param {Object} obj The object to add. + @return {Ember.Set} The set itself. + */ + add: aliasMethod('addObject'), - ```html - - ``` + /** + 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()`. - ## Bound: + ```javascript + var colors = new Ember.Set(["red", "green", "blue"]); + colors.remove("red"); // ["blue", "green"] + colors.remove("purple"); // ["blue", "green"] + colors.remove(null); // ["blue", "green"] + ``` - ```javascript - App.ApplicationController = Ember.Controller.extend({ - firstName: "Stanley", - entryNotAllowed: true - }); - ``` + @method remove + @param {Object} obj The object to remove + @return {Ember.Set} The set itself. + */ + remove: aliasMethod('removeObject'), + /** + Removes the last element from the set and returns it, or `null` if it's empty. - ```handlebars - {{input type="text" value=firstName disabled=entryNotAllowed size="50"}} - ``` + ```javascript + var colors = new Ember.Set(["green", "blue"]); + colors.pop(); // "blue" + colors.pop(); // "green" + colors.pop(); // null + ``` + + @method pop + @return {Object} The removed object from the set or 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; + }, + + /** + Inserts the given object on to the end of the set. It returns + the set itself. + This is an alias for `Ember.MutableEnumerable.addObject()`. - ```html - - ``` + ```javascript + var colors = new Ember.Set(); + colors.push("red"); // ["red"] + colors.push("green"); // ["red", "green"] + colors.push("blue"); // ["red", "green", "blue"] + ``` - ## Extension + @method push + @return {Ember.Set} The set itself. + */ + push: aliasMethod('addObject'), - 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: + /** + Removes the last element from the set and returns it, or `null` if it's empty. + This is an alias for `Ember.Set.pop()`. - ```javascript - Ember.TextField.reopen({ - attributeBindings: ['data-error'] - }); - ``` + ```javascript + var colors = new Ember.Set(["green", "blue"]); + colors.shift(); // "blue" + colors.shift(); // "green" + colors.shift(); // null + ``` - 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. + @method shift + @return {Object} The removed object from the set or null. + */ + shift: aliasMethod('pop'), - See more about [Ember components](api/classes/Ember.Component.html) + /** + Inserts the given object on to the end of the set. It returns + the set itself. + This is an alias of `Ember.Set.push()` - ## Use as checkbox + ```javascript + var colors = new Ember.Set(); + colors.unshift("red"); // ["red"] + colors.unshift("green"); // ["red", "green"] + colors.unshift("blue"); // ["red", "green", "blue"] + ``` - An `{{input}}` with a `type` of `checkbox` will render an HTML checkbox input. - The following HTML attributes can be set via the helper: + @method unshift + @return {Ember.Set} The set itself. + */ + unshift: aliasMethod('push'), - * `checked` - * `disabled` - * `tabindex` - * `indeterminate` - * `name` - * `autofocus` - * `form` + /** + Adds each object in the passed enumerable to the set. + This is an alias of `Ember.MutableEnumerable.addObjects()` - 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). + ```javascript + var colors = new Ember.Set(); + colors.addEach(["red", "green", "blue"]); // ["red", "green", "blue"] + ``` - ## Unbound: + @method addEach + @param {Ember.Enumerable} objects the objects to add. + @return {Ember.Set} The set itself. + */ + addEach: aliasMethod('addObjects'), - ```handlebars - {{input type="checkbox" name="isAdmin"}} - ``` + /** + Removes each object in the passed enumerable to the set. - ```html - - ``` + This is an alias of `Ember.MutableEnumerable.removeObjects()` - ## Bound: + ```javascript + var colors = new Ember.Set(["red", "green", "blue"]); + colors.removeEach(["red", "blue"]); // ["green"] + ``` - ```javascript - App.ApplicationController = Ember.Controller.extend({ - isAdmin: true - }); - ``` + @method removeEach + @param {Ember.Enumerable} objects the objects to remove. + @return {Ember.Set} The set itself. + */ + removeEach: aliasMethod('removeObjects'), + // .......................................................... + // PRIVATE ENUMERABLE SUPPORT + // - ```handlebars - {{input type="checkbox" checked=isAdmin }} - ``` + init: function(items) { + Ember.deprecate('Ember.Set is deprecated and will be removed in a future release.'); + this._super(); + if (items) this.addObjects(items); + }, + // implement Ember.Enumerable + nextObject: function(idx) { + return this[idx]; + }, - ```html - - ``` + // more optimized version + firstObject: computed(function() { + return this.length > 0 ? this[0] : undefined; + }), - ## Extension + // more optimized version + lastObject: computed(function() { + return this.length > 0 ? this[this.length-1] : undefined; + }), - 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: + // implements Ember.MutableEnumerable + addObject: function(obj) { + if (get(this, 'isFrozen')) throw new EmberError(FROZEN_ERROR); + if (isNone(obj)) return this; // nothing to do + var guid = guidFor(obj); + var idx = this[guid]; + var len = get(this, 'length'); + var added; - ```javascript - Ember.Checkbox.reopen({ - classNames: ['my-app-checkbox'] - }); - ``` + if (idx>=0 && idx` tag into the template. - The attributes of `{{textarea}}` match those of the native HTML tags as - closely as possible. + // implements Ember.MutableEnumerable + removeObject: function(obj) { + if (get(this, 'isFrozen')) throw new EmberError(FROZEN_ERROR); + if (isNone(obj)) return this; // nothing to do - The following HTML attributes can be set: + var guid = guidFor(obj); + var idx = this[guid]; + var len = get(this, 'length'); + var isFirst = idx === 0; + var isLast = idx === len-1; + var last, removed; - * `value` - * `name` - * `rows` - * `cols` - * `placeholder` - * `disabled` - * `maxlength` - * `tabindex` - * `selectionEnd` - * `selectionStart` - * `selectionDirection` - * `wrap` - * `readonly` - * `autofocus` - * `form` - * `spellcheck` - * `required` - When set to a quoted string, these value 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). + if (idx>=0 && idx - Lots of static text that ISN'T bound - - ``` + if (isFirst) { propertyDidChange(this, 'firstObject'); } + if (isLast) { propertyDidChange(this, 'lastObject'); } + this.enumerableContentDidChange(removed, null); + } - Bound: + return this; + }, - 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. + // optimized version + contains: function(obj) { + return this[guidFor(obj)]>=0; + }, - ```javascript - App.ApplicationController = Ember.Controller.extend({ - writtenWords: "Lots of text that IS bound" - }); - ``` + 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; + } + return ret; + }, - ```handlebars - {{textarea value=writtenWords}} - ``` + toString: function() { + var len = this.length, idx, array = []; + for(idx = 0; idx < len; idx++) { + array[idx] = this[idx]; + } + 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; - Would result in the following HTML: + var Cache = __dependency3__["default"]; - ```html - - ``` + var STRING_DASHERIZE_REGEXP = (/[ _]/g); - 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`: + var STRING_DASHERIZE_CACHE = new Cache(1000, function(key) { + return decamelize(key).replace(STRING_DASHERIZE_REGEXP, '-'); + }); - ```javascript - App.ApplicationController = Ember.Controller.extend({ - writtenWords: "Lots of text that IS bound", - outputWrittenWords: Ember.computed.oneWay("writtenWords") + 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(); }); - ``` - - ```handlebars - {{textarea value=writtenWords}} + }); -
    - {{outputWrittenWords}} -
    - ``` + var CLASSIFY_CACHE = new Cache(1000, function(str) { + var parts = str.split("."); + var out = []; - Would result in the following HTML: + for (var i=0, l=parts.length; i - Lots of text that IS bound - + return out.join("."); + }); - <-- the following div will be updated in real time as you type --> + var UNDERSCORE_CACHE = new Cache(1000, function(str) { + return str.replace(STRING_UNDERSCORE_REGEXP_1, '$1_$2'). + replace(STRING_UNDERSCORE_REGEXP_2, '_').toLowerCase(); + }); -
    - Lots of text that IS bound -
    - ``` + var CAPITALIZE_CACHE = new Cache(1000, function(str) { + return str.charAt(0).toUpperCase() + str.substr(1); + }); - 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: + var DECAMELIZE_CACHE = new Cache(1000, function(str) { + return str.replace(STRING_DECAMELIZE_REGEXP, '$1_$2').toLowerCase(); + }); - ```javascript - App.ApplicationController = Ember.Controller.extend({ - writtenWords: "Lots of text that IS bound", - twoWayWrittenWords: Ember.computed.alias("writtenWords") - }); - ``` + var STRING_DECAMELIZE_REGEXP = (/([a-z\d])([A-Z])/g); + var STRING_CAMELIZE_REGEXP = (/(\-|_|\.|\s)+(.)?/g); + var STRING_UNDERSCORE_REGEXP_1 = (/([a-z\d])([A-Z]+)/g); + var STRING_UNDERSCORE_REGEXP_2 = (/\-|\s+/g); - ```handlebars - {{textarea value=writtenWords}} - {{textarea value=twoWayWrittenWords}} - ``` + function fmt(str, formats) { + var cachedFormats = formats; - ```html - + if (!isArray(cachedFormats) || arguments.length > 2) { + cachedFormats = new Array(arguments.length - 1); - <-- both updated in real time --> + for (var i = 1, l = arguments.length; i < l; i++) { + cachedFormats[i - 1] = arguments[i]; + } + } - - ``` + // 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); + }); + } - ## Extension + function loc(str, formats) { + if (!isArray(formats) || arguments.length > 2) { + formats = Array.prototype.slice.call(arguments, 1); + } - 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: + str = Ember.STRINGS[str] || str; + return fmt(str, formats); + } - ```javascript - Ember.TextArea.reopen({ - attributeBindings: ['data-error'] - }); - ``` + function w(str) { + return str.split(/\s+/); + } - 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. + function decamelize(str) { + return DECAMELIZE_CACHE.get(str); + } - See more about [Ember components](api/classes/Ember.Component.html) + function dasherize(str) { + return STRING_DASHERIZE_CACHE.get(str); + } - @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); + function camelize(str) { + return CAMELIZE_CACHE.get(str); + } - var hash = options.hash, - types = options.hashTypes; + function classify(str) { + return CLASSIFY_CACHE.get(str); + } - return helpers.view.call(this, TextArea, options); + function underscore(str) { + return UNDERSCORE_CACHE.get(str); } - __exports__.inputHelper = inputHelper; - __exports__.textareaHelper = textareaHelper; - }); -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 get = __dependency1__.get; - var set = __dependency2__.set; - var View = __dependency3__.View; + function capitalize(str) { + return CAPITALIZE_CACHE.get(str); + } /** - @module ember - @submodule ember-handlebars + 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. + + @property STRINGS + @for Ember + @type Hash */ + Ember.STRINGS = {}; /** - The internal class used to create text inputs when the `{{input}}` - helper is used with `type` of `checkbox`. - - See [handlebars.helpers.input](/api/classes/Ember.Handlebars.helpers.html#method_input) for usage details. - - ## Direct manipulation of `checked` - - 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. - - ## Layout and LayoutName properties - - 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. + 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. - @class Checkbox + @class String @namespace Ember - @extends Ember.View + @static */ - var Checkbox = View.extend({ - instrumentDisplay: '{{input type="checkbox"}}', - - classNames: ['ember-checkbox'], + __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. - tagName: 'input', + Ordered insertions are most useful when building loc strings where values + you need to insert may appear in different orders. - attributeBindings: ['type', 'checked', 'indeterminate', 'disabled', 'tabindex', 'name', - 'autofocus', 'required', 'form'], + ```javascript + "Hello %@ %@".fmt('John', 'Doe'); // "Hello John Doe" + "Hello %@2, %@1".fmt('John', 'Doe'); // "Hello Doe, John" + ``` - type: "checkbox", - checked: false, - disabled: false, - indeterminate: false, + @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, - init: function() { - this._super(); - this.on("change", this, this._updateElementValue); - }, + /** + 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. - didInsertElement: function() { - this._super(); - get(this, 'element').indeterminate = !!get(this, 'indeterminate'); - }, + 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. - _updateElementValue: function() { - set(this, 'checked', this.$().prop('checked')); - } - }); + ```javascript + Ember.STRINGS = { + '_Hello World': 'Bonjour le monde', + '_Hello %@ %@': 'Bonjour %@ %@' + }; - __exports__["default"] = Checkbox; - }); -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"; - /*jshint eqeqeq:false newcap:false */ + Ember.String.loc("_Hello World"); // 'Bonjour le monde'; + Ember.String.loc("_Hello %@ %@", ["John", "Smith"]); // "Bonjour John Smith"; + ``` - /** - @module ember - @submodule ember-handlebars - */ + @method loc + @param {String} str The string to format + @param {Array} formats Optional array of parameters to interpolate into string. + @return {String} formatted string + */ + loc: loc, - 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; + /** + 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`. - var indexOf = EnumerableUtils.indexOf, - indexesOf = EnumerableUtils.indexesOf, - forEach = EnumerableUtils.forEach, - replace = EnumerableUtils.replace, - precompileTemplate = EmberHandlebars.compile; + ```javascript + Ember.String.w("alpha beta gamma").forEach(function(key) { + console.log(key); + }); - var SelectOption = View.extend({ - instrumentDisplay: 'Ember.SelectOption', + // > alpha + // > beta + // > gamma + ``` - tagName: 'option', - attributeBindings: ['value', 'selected'], + @method w + @param {String} str The string to split + @return {Array} array containing the split strings + */ + w: w, - defaultTemplate: function(context, options) { - options = { data: options.data, hash: {} }; - EmberHandlebars.helpers.bind.call(context, "view.label", options); - }, + /** + Converts a camelized string into all lower case separated by underscores. - init: function() { - this.labelPathDidChange(); - this.valuePathDidChange(); + ```javascript + 'innerHTML'.decamelize(); // 'inner_html' + 'action_name'.decamelize(); // 'action_name' + 'css-class-name'.decamelize(); // 'css-class-name' + 'my favorite items'.decamelize(); // 'my favorite items' + ``` - this._super(); - }, + @method decamelize + @param {String} str The string to decamelize. + @return {String} the decamelized string. + */ + decamelize: decamelize, - 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'), + /** + Replaces underscores, spaces, or camelCase with dashes. - labelPathDidChange: observer('parentView.optionLabelPath', function() { - var labelPath = get(this, 'parentView.optionLabelPath'); + ```javascript + 'innerHTML'.dasherize(); // 'inner-html' + 'action_name'.dasherize(); // 'action-name' + 'css-class-name'.dasherize(); // 'css-class-name' + 'my favorite items'.dasherize(); // 'my-favorite-items' + ``` - if (!labelPath) { return; } + @method dasherize + @param {String} str The string to dasherize. + @return {String} the dasherized string. + */ + dasherize: dasherize, - defineProperty(this, 'label', computed(function() { - return get(this, labelPath); - }).property(labelPath)); - }), + /** + Returns the lowerCamelCase form of a string. - valuePathDidChange: observer('parentView.optionValuePath', function() { - var valuePath = get(this, 'parentView.optionValuePath'); + ```javascript + 'innerHTML'.camelize(); // 'innerHTML' + 'action_name'.camelize(); // 'actionName' + 'css-class-name'.camelize(); // 'cssClassName' + 'my favorite items'.camelize(); // 'myFavoriteItems' + 'My Favorite Items'.camelize(); // 'myFavoriteItems' + ``` - if (!valuePath) { return; } + @method camelize + @param {String} str The string to camelize. + @return {String} the camelized string. + */ + camelize: camelize, - defineProperty(this, 'value', computed(function() { - return get(this, valuePath); - }).property(valuePath)); - }) - }); + /** + Returns the UpperCamelCase form of a string. - var SelectOptgroup = CollectionView.extend({ - instrumentDisplay: 'Ember.SelectOptgroup', + ```javascript + 'innerHTML'.classify(); // 'InnerHTML' + 'action_name'.classify(); // 'ActionName' + 'css-class-name'.classify(); // 'CssClassName' + 'my favorite items'.classify(); // 'MyFavoriteItems' + ``` - tagName: 'optgroup', - attributeBindings: ['label'], + @method classify + @param {String} str the string to classify + @return {String} the classified string + */ + classify: classify, - selectionBinding: 'parentView.selection', - multipleBinding: 'parentView.multiple', - optionLabelPathBinding: 'parentView.optionLabelPath', - optionValuePathBinding: 'parentView.optionValuePath', + /** + More general than decamelize. Returns the lower\_case\_and\_underscored + form of a string. - itemViewClassBinding: 'parentView.optionView' - }); + ```javascript + 'innerHTML'.underscore(); // 'inner_html' + 'action_name'.underscore(); // 'action_name' + 'css-class-name'.underscore(); // 'css_class_name' + 'my favorite items'.underscore(); // 'my_favorite_items' + ``` - /** - 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. + @method underscore + @param {String} str The string to underscore. + @return {String} the underscored string. + */ + underscore: underscore, - The text and `value` property of each ` - - - ``` + self._composeAt(self._operations.length-1); + }); - The `value` attribute of the selected `"); - return buffer; - } + /** + Track that `newItems` were added to the tracked array at `index`. -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}))); - } + @method addItems + @param index + @param newItems + */ + addItems: function (index, newItems) { + var count = get(newItems, 'length'); + if (count < 1) { return; } + + var match = this._findArrayOperation(index); + var arrayOperation = match.operation; + var arrayOperationIndex = match.index; + var arrayOperationRangeStart = match.rangeStart; + var composeIndex, newArrayOperation; -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}))); - } + newArrayOperation = new ArrayOperation(INSERT, count, newItems); - 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'], + 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; + } + + this._composeInsert(composeIndex); + }, /** - The `multiple` attribute of the select element. Indicates whether multiple - options can be selected. + Track that `count` items were removed at `index`. - @property multiple - @type Boolean - @default false + @method removeItems + @param index + @param count */ - multiple: false, + removeItems: function (index, count) { + if (count < 1) { return; } - /** - The `disabled` attribute of the select element. Indicates whether - the element is disabled from interactions. + var match = this._findArrayOperation(index); + var arrayOperationIndex = match.index; + var arrayOperationRangeStart = match.rangeStart; + var newArrayOperation, composeIndex; - @property disabled - @type Boolean - @default false - */ - disabled: false, + 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; + } + + return this._composeDelete(composeIndex); + }, /** - The `required` attribute of the select element. Indicates whether - a selected option is required for form validation. + Apply all operations, reducing them to retain:n, for `n`, the number of + items in the array. - @property required - @type Boolean - @default false - @since 1.5.0 - */ - required: false, + `callback` will be called for each operation and will be passed the following arguments: - /** - The list of options. + * {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 `optionLabelPath` and `optionValuePath` are not overridden, this should - be a list of strings, which will serve simultaneously as labels and values. + @method apply + @param {Function} callback + */ + apply: function (callback) { + var items = []; + var offset = 0; - Otherwise, this should be a list of objects. For instance: + forEach(this._operations, function (arrayOperation, operationIndex) { + callback(arrayOperation.items, offset, arrayOperation.type, operationIndex); - ```javascript - Ember.Select.create({ - content: A([ - { id: 1, firstName: 'Yehuda' }, - { id: 2, firstName: 'Tom' } - ]), - optionLabelPath: 'content.firstName', - optionValuePath: 'content.id' + if (arrayOperation.type !== DELETE) { + offset += arrayOperation.count; + items = items.concat(arrayOperation.items); + } }); - ``` - @property content - @type Array - @default null - */ - content: null, + this._operations = [new ArrayOperation(RETAIN, items.length, items)]; + }, /** - When `multiple` is `false`, the element of `content` that is currently - selected, if any. + Return an `ArrayOperationMatch` for the operation that contains the item at `index`. - When `multiple` is `true`, an array of such elements. + @method _findArrayOperation - @property selection - @type Object or Array - @default null + @param {Number} index the index of the item whose operation information + should be returned. + @private */ - selection: null, - - /** - In single selection mode (when `multiple` is `false`), value can be used to - get the current selection's value or set the selection by it's value. + _findArrayOperation: function (index) { + var split = false; + var arrayOperationIndex, arrayOperation, + arrayOperationRangeStart, arrayOperationRangeEnd, + len; - It is not currently supported in multiple selection mode. + // 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]; - @property value - @type String - @default null - */ - value: computed(function(key, value) { - if (arguments.length === 2) { return value; } - var valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''); - return valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection'); - }).property('selection'), + if (arrayOperation.type === DELETE) { continue; } - /** - If given, a top-most dummy option will be rendered to serve as a user - prompt. + arrayOperationRangeEnd = arrayOperationRangeStart + arrayOperation.count - 1; - @property prompt - @type String - @default null - */ - prompt: null, + if (index === arrayOperationRangeStart) { + break; + } else if (index > arrayOperationRangeStart && index <= arrayOperationRangeEnd) { + split = true; + break; + } else { + arrayOperationRangeStart = arrayOperationRangeEnd + 1; + } + } - /** - The path of the option labels. See [content](/api/classes/Ember.Select.html#property_content). + return new ArrayOperationMatch(arrayOperation, arrayOperationIndex, split, arrayOperationRangeStart); + }, - @property optionLabelPath - @type String - @default 'content' - */ - optionLabelPath: 'content', + _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); - /** - The path of the option values. See [content](/api/classes/Ember.Select.html#property_content). + // truncate LHS + arrayOperation.count = splitIndex; + arrayOperation.items = arrayOperation.items.slice(0, splitIndex); - @property optionValuePath - @type String - @default 'content' - */ - optionValuePath: 'content', + this._operations.splice(arrayOperationIndex + 1, 0, newArrayOperation, splitArrayOperation); + }, - /** - The path of the option group. - When this property is used, `content` should be sorted by `optionGroupPath`. + // 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; - @property optionGroupPath - @type String - @default null - */ - optionGroupPath: null, + if (leftOp === INSERT) { + // merge left + leftArrayOperation.count += newArrayOperation.count; + leftArrayOperation.items = leftArrayOperation.items.concat(newArrayOperation.items); - /** - The view class for optgroup. + 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); + } + }, - @property groupView - @type Ember.View - @default Ember.SelectOptgroup - */ - groupView: SelectOptgroup, + _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 = []; - groupedContent: computed(function() { - var groupPath = get(this, 'optionGroupPath'); - var groupedContent = A(); - var content = get(this, 'content') || []; + if (leftOp === DELETE) { + arrayOperation = leftArrayOperation; + index -= 1; + } - forEach(content, function(item) { - var label = get(item, groupPath); + for (var i = index + 1; deletesToGo > 0; ++i) { + nextArrayOperation = this._operations[i]; + nextOp = nextArrayOperation.type; + nextCount = nextArrayOperation.count; - if (get(groupedContent, 'lastObject.label') !== label) { - groupedContent.pushObject({ - label: label, - content: A() - }); + if (nextOp === DELETE) { + arrayOperation.count += nextCount; + continue; } - get(groupedContent, 'lastObject.content').push(item); - }); + 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; - return groupedContent; - }).property('optionGroupPath', 'content.@each'), + // 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; - /** - The view class for option. + 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; + } - @property optionView - @type Ember.View - @default Ember.SelectOption - */ - optionView: SelectOption, + if (nextOp === INSERT) { + // d:2 i:3 will result in delete going away + arrayOperation.count -= nextCount; + } + } - _change: function() { - if (get(this, 'multiple')) { - this._changeMultiple(); + 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 { - this._changeSingle(); + // 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); } + + return removedItems; }, - 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(); - } - }), + toString: function () { + var str = ""; + forEach(this._operations, function (operation) { + str += " " + operation.type + ":" + operation.count; + }); + return str.substring(1); + } + }; - 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; + /** + Internal data structure to represent an array operation. - if (value !== selectedValue) { - selection = content ? content.find(function(obj) { - return value === (valuePath ? get(obj, valuePath) : obj); - }) : null; + @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; + } - this.set('selection', selection); - } - }), + /** + Internal data structure used to include information when looking up operations + by item index. + + @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; + } + }); +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__) { + "use strict"; + var _Ember = __dependency1__["default"]; + var precompile = __dependency2__["default"]; + var compile = __dependency3__["default"]; + var template = __dependency4__["default"]; + var registerPlugin = __dependency5__.registerPlugin; + var TransformEachInToHash = __dependency6__["default"]; + var TransformWithAsToHash = __dependency7__["default"]; - _triggerChange: function() { - var selection = get(this, 'selection'); - var value = get(this, 'value'); + registerPlugin('ast', TransformWithAsToHash); + registerPlugin('ast', TransformEachInToHash); - if (!isNone(selection)) { this.selectionDidChange(); } - if (!isNone(value)) { this.valueDidChange(); } + __exports__._Ember = _Ember; + __exports__.precompile = precompile; + __exports__.compile = compile; + __exports__.template = template; + __exports__.registerPlugin = registerPlugin; + }); +enifed("ember-template-compiler/plugins", + ["exports"], + function(__exports__) { + "use strict"; + /** + @module ember + @submodule ember-template-compiler + */ - this._change(); - }, + /** + @private + @property helpers + */ + var plugins = { + ast: [ ] + }; - _changeSingle: function() { - var selectedIndex = this.$()[0].selectedIndex, - content = get(this, 'content'), - prompt = get(this, 'prompt'); + /** + Adds an AST plugin to be used by Ember.HTMLBars.compile. - if (!content || !get(content, 'length')) { return; } - if (prompt && selectedIndex === 0) { set(this, 'selection', null); return; } + @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.'); + } - if (prompt) { selectedIndex -= 1; } - set(this, 'selection', content.objectAt(selectedIndex)); - }, + plugins[type].push(Plugin); + } + __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 + */ - _changeMultiple: function() { - var options = this.$('option:selected'), - prompt = get(this, 'prompt'), - offset = prompt ? 1 : 0, - content = get(this, 'content'), - selection = get(this, 'selection'); - if (!content) { return; } - if (options) { - var selectedIndexes = options.map(function() { - return this.index - offset; - }).toArray(); - var newSelection = content.objectsAt(selectedIndexes); + /** + An HTMLBars AST transformation that replaces all instances of - if (isArray(selection)) { - replace(selection, 0, get(selection, 'length'), newSelection); - } else { - set(this, 'selection', newSelection); + ```handlebars + {{#each item in items}} + {{/each}} + ``` + + with + + ```handlebars + {{#each items keyword="item"}} + {{/each}} + ``` + + @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; + + walker.visit(ast, function(node) { + if (pluginContext.validate(node)) { + var removedParams = node.sexpr.params.splice(0, 2); + var keyword = removedParams[0].original; + + // TODO: This may not be necessary. + if (!node.sexpr.hash) { + node.sexpr.hash = b.hash(); } + + node.sexpr.hash.pairs.push(b.pair( + 'keyword', + b.string(keyword) + )); } - }, + }); - _selectionDidChangeSingle: function() { - var el = this.get('element'); - if (!el) { return; } + return ast; + }; - var content = get(this, 'content'), - selection = get(this, 'selection'), - selectionIndex = content ? indexOf(content, selection) : -1, - prompt = get(this, 'prompt'); + 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'; + }; - if (prompt) { selectionIndex += 1; } - if (el) { el.selectedIndex = selectionIndex; } - }, + __exports__["default"] = TransformEachInToHash; + }); +enifed("ember-template-compiler/plugins/transform-with-as-to-hash", + ["exports"], + function(__exports__) { + "use strict"; + /** + @module ember + @submodule ember-htmlbars + */ - _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; + /** + An HTMLBars AST transformation that replaces all instances of - if (options) { - options.each(function() { - adjusted = this.index > -1 ? this.index - offset : -1; - this.selected = indexOf(selectedIndexes, adjusted) > -1; - }); + ```handlebars + {{#with foo.bar as bar}} + {{/with}} + ``` + + with + + ```handlebars + {{#with foo.bar as |bar|}} + {{/with}} + ``` + + @private + @class TransformWithAsToHash + */ + function TransformWithAsToHash() { + // set later within HTMLBars to the syntax package + this.syntax = null; + } + + /** + @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(); + + walker.visit(ast, function(node) { + if (pluginContext.validate(node)) { + var removedParams = node.sexpr.params.splice(1, 2); + var keyword = removedParams[1].original; + node.program.blockParams = [ keyword ]; } - }, + }); - init: function() { - this._super(); - this.on("didInsertElement", this, this._triggerChange); - this.on("change", this, this._change); - } - }); + return ast; + }; - __exports__["default"] = Select - __exports__.Select = Select; - __exports__.SelectOption = SelectOption; - __exports__.SelectOptgroup = SelectOptgroup; + 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'; + }; + + __exports__["default"] = TransformWithAsToHash; }); -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__) { +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-handlebars + @submodule ember-template-compiler */ - var get = __dependency1__.get; - var Component = __dependency2__["default"]; - var TextSupport = __dependency3__["default"]; - var observer = __dependency4__.observer; - /** - The internal class used to create textarea element when the `{{textarea}}` - helper is used. + var compile = __dependency1__.compile; + var compileOptions = __dependency2__["default"]; + var template = __dependency3__["default"]; - See [handlebars.helpers.textarea](/api/classes/Ember.Handlebars.helpers.html#method_textarea) for usage details. + /** + Uses HTMLBars `compile` function to process a string into a compiled template. - ## Layout and LayoutName properties + This is not present in production builds. - 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. + @private + @method compile + @param {String} templateString This is the string to be compiled by HTMLBars. + */ + __exports__["default"] = function(templateString) { + var templateSpec = compile(templateString, compileOptions()); - @class TextArea - @namespace Ember - @extends Ember.Component - @uses Ember.TextSupport + 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 */ - var TextArea = Component.extend(TextSupport, { - instrumentDisplay: '{{textarea}}', - classNames: ['ember-text-area'], + var Ember = __dependency1__["default"]; + var plugins = __dependency2__["default"]; - tagName: "textarea", - attributeBindings: ['rows', 'cols', 'name', 'selectionEnd', 'selectionStart', 'wrap'], - rows: null, - cols: null, + /** + @private + @property compileOptions + */ + __exports__["default"] = function() { + var disableComponentGeneration = true; + + return { + disableComponentGeneration: disableComponentGeneration, - _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); - } - }), + 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 + */ - init: function() { - this._super(); - this.on("didInsertElement", this, this._updateElementValue); - } + var compileSpec = __dependency1__.compileSpec; + var compileOptions = __dependency2__["default"]; + + /** + Uses HTMLBars `compile` function to process a string into a compiled template string. + The returned string must be passed through `Ember.HTMLBars.template`. - }); + This is not present in production builds. - __exports__["default"] = TextArea; + @private + @method precompile + @param {String} templateString This is the string to be compiled by HTMLBars. + */ + __exports__["default"] = function(templateString) { + return compileSpec(templateString, compileOptions()); + } }); -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__) { +enifed("ember-template-compiler/system/template", + ["exports"], + function(__exports__) { "use strict"; /** @module ember - @submodule ember-handlebars + @submodule ember-template-compiler */ - var get = __dependency1__.get; - var set = __dependency2__.set; - var Component = __dependency3__["default"]; - var TextSupport = __dependency4__["default"]; - /** + Augments the detault precompiled output of an HTMLBars template with + additional information needed by Ember. - The internal class used to create text inputs when the `{{input}}` - helper is used with `type` of `text`. + @private + @method template + @param {Function} templateSpec This is the compiled HTMLBars template spec. + */ - See [Handlebars.helpers.input](/api/classes/Ember.Handlebars.helpers.html#method_input) for usage details. + __exports__["default"] = function(templateSpec) { + templateSpec.isTop = true; + templateSpec.isMethod = false; - ## Layout and LayoutName properties + 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"]; - 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. + // to setup initializer + // to handle various edge cases - @class TextField - @namespace Ember - @extends Ember.Component - @uses Ember.TextSupport - */ - var TextField = Component.extend(TextSupport, { - instrumentDisplay: '{{input type="text"}}', + var setupForTesting = __dependency4__["default"]; + var Test = __dependency5__["default"]; + var Adapter = __dependency6__["default"]; + var QUnitAdapter = __dependency7__["default"]; + // adds helpers to helpers object in Test - 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'], + /** + Ember Testing - /** - The `value` attribute of the input element. As the user inputs text, this - property is updated live. + @module ember + @submodule ember-testing + @requires ember-application + */ - @property value - @type String - @default "" - */ - value: "", + Ember.Test = Test; + Ember.Test.Adapter = Adapter; + Ember.Test.QUnitAdapter = QUnitAdapter; + Ember.setupForTesting = setupForTesting; + }); +enifed("ember-testing/adapters/adapter", + ["ember-runtime/system/object","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var EmberObject = __dependency1__["default"]; - /** - The `type` attribute of the input element. + function K() { return this; } - @property type - @type String - @default "text" - */ - type: "text", + /** + @module ember + @submodule ember-testing + */ + + /** + 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({ /** - The `size` of the text field in characters. + This callback will be called whenever an async operation is about to start. - @property size - @type String - @default null + Override this to call your framework's methods that handle async + operations. + + @public + @method asyncStart */ - size: null, + asyncStart: K, /** - The `pattern` attribute of input element. + This callback will be called whenever an async operation has completed. - @property pattern - @type String - @default null + @public + @method asyncEnd */ - pattern: null, + asyncEnd: K, /** - The `min` attribute of input element used with `type="number"` or `type="range"`. + Override this method with your testing framework's false assertion. + This function is called whenever an exception occurs causing the testing + promise to fail. - @property min - @type String - @default null - @since 1.4.0 - */ - min: null, + QUnit example: - /** - The `max` attribute of input element used with `type="number"` or `type="range"`. + ```javascript + exception: function(error) { + ok(false, error); + }; + ``` - @property max - @type String - @default null - @since 1.4.0 + @public + @method exception + @param {String} error The exception to be raised. */ - max: null + exception: function(error) { + throw error; + } }); - __exports__["default"] = TextField; + __exports__["default"] = Adapter; }); -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__) { +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; + /** - @module ember - @submodule ember-handlebars - */ + This class implements the methods defined by Ember.Test.Adapter for the + QUnit testing framework. + @class QUnitAdapter + @namespace Ember.Test + @extends Ember.Test.Adapter + */ + __exports__["default"] = Adapter.extend({ + asyncStart: function() { + QUnit.stop(); + }, + asyncEnd: function() { + QUnit.start(); + }, + exception: function(error) { + ok(false, inspect(error)); + } + }); + }); +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__) { + "use strict"; var get = __dependency1__.get; - var set = __dependency2__.set; - var Mixin = __dependency3__.Mixin; - var TargetActionSupport = __dependency4__["default"]; + var EmberError = __dependency2__["default"]; + var run = __dependency3__["default"]; + var jQuery = __dependency4__["default"]; + var Test = __dependency5__["default"]; /** - Shared mixin used by `Ember.TextField` and `Ember.TextArea`. - - @class TextSupport - @namespace Ember - @uses Ember.TargetActionSupport - @extends Ember.Mixin - @private + * @module ember + * @submodule ember-testing */ - var TextSupport = Mixin.create(TargetActionSupport, { - value: "", - attributeBindings: ['placeholder', 'disabled', 'maxlength', 'tabindex', 'readonly', - 'autofocus', 'form', 'selectionDirection', 'spellcheck', 'required', - 'title', 'autocapitalize', 'autocorrect'], - placeholder: null, - disabled: false, - maxlength: null, + var helper = Test.registerHelper; + var asyncHelper = Test.registerAsyncHelper; + var countAsync = 0; - 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); - }, + function currentRouteName(app){ + var appController = app.__container__.lookup('controller:application'); - /** - The action to be sent when the user presses the return key. + return get(appController, 'currentRouteName'); + } - 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. + function currentPath(app){ + var appController = app.__container__.lookup('controller:application'); - @property action - @type String - @default null - */ - action: null, + return get(appController, 'currentPath'); + } - /** - The event that should send the action. + function currentURL(app){ + var router = app.__container__.lookup('router:main'); - Options are: + return get(router, 'location').getURL(); + } - * `enter`: the user pressed enter - * `keyPress`: the user pressed a key + function pauseTest(){ + Test.adapter.asyncStart(); + return new Ember.RSVP.Promise(function(){ }, 'TestAdapter paused promise'); + } - @property onEvent - @type String - @default enter - */ - onEvent: 'enter', + function visit(app, url) { + var router = app.__container__.lookup('router:main'); + router.location.setURL(url); - /** - Whether they `keyUp` event that triggers an `action` to be sent continues - propagating to other views. + if (app._readinessDeferrals > 0) { + router['initialURL'] = url; + run(app, 'advanceReadiness'); + delete router['initialURL']; + } else { + run(app, app.handleURL, url); + } - 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. + return app.testHelpers.wait(); + } - If you would like parent views to receive the `keyUp` event even after an - action has been dispatched, set `bubbles` to true. + function click(app, selector, context) { + var $el = app.testHelpers.findWithAssert(selector, context); + run($el, 'mousedown'); - @property bubbles - @type Boolean - @default false - */ - bubbles: false, + 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'); + } + }); + } + } - interpretKeyEvents: function(event) { - var map = TextSupport.KEY_EVENTS; - var method = map[event.keyCode]; + run($el, 'mouseup'); + run($el, 'click'); - this._elementValueDidChange(); - if (method) { return this[method](event); } - }, + return app.testHelpers.wait(); + } - _elementValueDidChange: function() { - set(this, 'value', this.$().val()); - }, + function triggerEvent(app, selector, contextOrType, typeOrOptions, possibleOptions){ + var arity = arguments.length; + var context, type, options; - /** - The action to be sent when the user inserts a new line. + 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; + } - Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 13. - Uses sendAction to send the `enter` action to the controller. + var $el = app.testHelpers.findWithAssert(selector, context); - @method insertNewline - @param {Event} event - */ - insertNewline: function(event) { - sendAction('enter', this, event); - sendAction('insert-newline', this, event); - }, + var event = jQuery.Event(type, options); - /** - Called when the user hits escape. + run($el, 'trigger', event); - Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 27. - Uses sendAction to send the `escape-press` action to the controller. + return app.testHelpers.wait(); + } - @method cancel - @param {Event} event - */ - cancel: function(event) { - sendAction('escape-press', this, event); - }, + function keyEvent(app, selector, contextOrType, typeOrKeyCode, keyCode) { + var context, type; - /** - Called when the text area is focused. + if (typeof keyCode === 'undefined') { + context = null; + keyCode = typeOrKeyCode; + type = contextOrType; + } else { + context = contextOrType; + type = typeOrKeyCode; + } - @method focusIn - @param {Event} event - */ - focusIn: function(event) { - sendAction('focus-in', this, event); - }, + return app.testHelpers.triggerEvent(selector, context, type, { keyCode: keyCode, which: keyCode }); + } - /** - Called when the text area is blurred. + function fillIn(app, selector, contextOrText, text) { + var $el, context; + if (typeof text === 'undefined') { + text = contextOrText; + } else { + context = contextOrText; + } + $el = app.testHelpers.findWithAssert(selector, context); + run(function() { + $el.val(text).change(); + }); + return app.testHelpers.wait(); + } - @method focusOut - @param {Event} event - */ - focusOut: function(event) { - sendAction('focus-out', this, event); - }, + function findWithAssert(app, selector, context) { + var $el = app.testHelpers.find(selector, context); + if ($el.length === 0) { + throw new EmberError("Element " + selector + " not found."); + } + return $el; + } - /** - The action to be sent when the user presses a key. Enabled by setting - the `onEvent` property to `keyPress`. + function find(app, selector, context) { + var $el; + context = context || get(app, 'rootElement'); + $el = app.$(selector, context); - Uses sendAction to send the `keyPress` action to the controller. + return $el; + } - @method keyPress - @param {Event} event - */ - keyPress: function(event) { - sendAction('key-press', this, event); - } + function andThen(app, callback) { + return app.testHelpers.wait(callback(app)); + } - }); + function wait(app, value) { + return Test.promise(function(resolve) { + // If this is the first async promise, kick off the async test + if (++countAsync === 1) { + Test.adapter.asyncStart(); + } - TextSupport.KEY_EVENTS = { - 13: 'insertNewline', - 27: 'cancel' - }; + // Every 10ms, poll for the async thing to have finished + var watcher = setInterval(function() { + // 1. If the router is loading, keep polling + var routerIsLoading = !!app.__container__.lookup('router:main').router.activeTransition; + if (routerIsLoading) { return; } - // 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'); + // 2. If there are pending Ajax requests, keep polling + if (Test.pendingAjaxRequests) { return; } - // 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); - } + // 3. If there are scheduled timers or we are inside of a run loop, keep polling + if (run.hasScheduledTimers() || run.currentRunLoop) { return; } + if (Test.waiters && Test.waiters.any(function(waiter) { + var context = waiter[0]; + var callback = waiter[1]; + return !callback.call(context); + })) { return; } + // Stop polling + clearInterval(watcher); - view.sendAction(eventName, value); + // If this is the last async promise, end the async test + if (--countAsync === 0) { + Test.adapter.asyncEnd(); + } + + // Synchronously resolve the promise + run(null, resolve, value); + }, 10); + }); - if (action || on === eventName) { - if(!get(view, 'bubbles')) { - event.stopPropagation(); - } - } } - __exports__["default"] = TextSupport; - }); -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"; - var Ember = __dependency1__["default"]; - // Ember.FEATURES, Ember.assert, Ember.Handlebars, Ember.lookup - // var emberAssert = Ember.assert; - var fmt = __dependency2__.fmt; + /** + * Loads a route, sets up any controllers, and renders any templates associated + * with the route as though a real user had triggered the route change while + * using your app. + * + * Example: + * + * ```javascript + * visit('posts/index').then(function() { + * // assert something + * }); + * ``` + * + * @method visit + * @param {String} url the name of the route + * @return {RSVP.Promise} + */ + asyncHelper('visit', visit); + + /** + * Clicks an element and triggers any actions triggered by the element's `click` + * event. + * + * Example: + * + * ```javascript + * click('.some-jQuery-selector').then(function() { + * // assert something + * }); + * ``` + * + * @method click + * @param {String} selector jQuery selector for finding element on the DOM + * @return {RSVP.Promise} + */ + asyncHelper('click', click); + + /** + * Simulates a key event, e.g. `keypress`, `keydown`, `keyup` with the desired keyCode + * + * Example: + * + * ```javascript + * keyEvent('.some-jQuery-selector', 'keypress', 13).then(function() { + * // assert something + * }); + * ``` + * + * @method keyEvent + * @param {String} selector jQuery selector for finding element on the DOM + * @param {String} type the type of key event, e.g. `keypress`, `keydown`, `keyup` + * @param {Number} keyCode the keyCode of the simulated key event + * @return {RSVP.Promise} + * @since 1.5.0 + */ + asyncHelper('keyEvent', keyEvent); + + /** + * Fills in an input element with some text. + * + * Example: + * + * ```javascript + * fillIn('#email', 'you@example.com').then(function() { + * // assert something + * }); + * ``` + * + * @method fillIn + * @param {String} selector jQuery selector finding an input element on the DOM + * to fill text with + * @param {String} text text to place inside the input element + * @return {RSVP.Promise} + */ + asyncHelper('fillIn', fillIn); + + /** + * Finds an element in the context of the app's container element. A simple alias + * for `app.$(selector)`. + * + * Example: + * + * ```javascript + * var $el = find('.my-selector'); + * ``` + * + * @method find + * @param {String} selector jQuery string selector for element lookup + * @return {Object} jQuery object representing the results of the query + */ + helper('find', find); + + /** + * Like `find`, but throws an error if the element selector returns no results. + * + * Example: + * + * ```javascript + * var $el = findWithAssert('.doesnt-exist'); // throws error + * ``` + * + * @method findWithAssert + * @param {String} selector jQuery selector string for finding an element within + * the DOM + * @return {Object} jQuery object representing the results of the query + * @throws {Error} throws error if jQuery object returned has a length of 0 + */ + helper('findWithAssert', findWithAssert); + + /** + Causes the run loop to process any pending events. This is used to ensure that + any async operations from other helpers (or your assertions) have been processed. - var EmberHandlebars = __dependency3__["default"]; - var helpers = EmberHandlebars.helpers; + This is most often used as the return value for the helper functions (see 'click', + 'fillIn','visit',etc). - var get = __dependency4__.get; - var isGlobalPath = __dependency5__.isGlobalPath; - var EmberError = __dependency6__["default"]; - var IS_BINDING = __dependency7__.IS_BINDING; + Example: - // late bound via requireModule because of circular dependencies. - var resolveHelper, - SimpleHandlebarsView; + ```javascript + Ember.Test.registerAsyncHelper('loginUser', function(app, username, password) { + visit('secured/path/here') + .fillIn('#username', username) + .fillIn('#password', password) + .click('.submit') - var isEmpty = __dependency8__["default"]; + return app.testHelpers.wait(); + }); - var slice = [].slice, originalTemplate = EmberHandlebars.template; + @method wait + @param {Object} value The value to be returned. + @return {RSVP.Promise} + */ + asyncHelper('wait', wait); + asyncHelper('andThen', andThen); - /** - 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); - } - } + /** + Returns the currently active route name. - return { root: root, path: path, isKeyword: isKeyword }; - }; + Example: + ```javascript + function validateRouteName(){ + equal(currentRouteName(), 'some.path', "correct route was transitioned into."); + } - /** - 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. + visit('/some/path').then(validateRouteName) + ``` - @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 + @method currentRouteName + @return {Object} The name of the currently active route. + @since 1.5.0 */ - function handlebarsGet(root, path, options) { - var data = options && options.data, - normalizedPath = normalizePath(root, path, data), - value; - - - root = normalizedPath.root; - path = normalizedPath.path; + helper('currentRouteName', currentRouteName); - value = get(root, path); + /** + Returns the current path. - if (value === undefined && root !== Ember.lookup && isGlobalPath(path)) { - value = get(Ember.lookup, path); - } - + Example: - return value; + ```javascript + function validateURL(){ + equal(currentPath(), 'some.path.index', "correct path was transitioned into."); } - /** - 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. + click('#some-link-id').then(validateURL); + ``` - @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 + @method currentPath + @return {Object} The currently active path. + @since 1.5.0 */ - function getEscaped(root, path, options) { - var result = handlebarsGet(root, path, options); - - if (result === null || result === undefined) { - result = ""; - } else if (!(result instanceof Handlebars.SafeString)) { - result = String(result); - } - if (!options.hash.unescaped){ - result = Handlebars.Utils.escapeExpression(result); - } + helper('currentPath', currentPath); - return result; - }; + /** + Returns the current URL. - function resolveParams(context, params, options) { - var resolvedParams = [], types = options.types, param, type; + Example: - for (var i=0, l=params.length; i') + .css({ position: 'absolute', left: '-1000px', top: '-1000px' }) + .appendTo('body') + .on('click', handler) + .trigger('click') + .remove(); + } - Like normal handlebars helpers, bound helpers have access to the options - passed into the helper call. + $(function() { + /* + Determine whether a checkbox checked using jQuery's "click" method will have + the correct value for its checked property. - ```javascript - Ember.Handlebars.registerBoundHelper('repeat', function(value, options) { - var count = options.hash.count; - var a = []; - while(a.length < count) { - a.push(value); + 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. + */ + 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; + } + } + }; } - return a.join(''); }); - ``` - - This helper could be used in a template as follows: - - ```handlebars - {{repeat text count=3}} - ``` - - ## Example with bound options - - Bound hash options are also supported. Example: - ```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. + // 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"]; - ## Example with extra dependencies + /** + @module ember + @submodule ember-testing + */ + var slice = [].slice; + var helpers = {}; + var injectHelpersCallbacks = []; - 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. + /** + This is a container for an assortment of testing related functionality: - ```javascript - Ember.Handlebars.registerBoundHelper('capitalizeName', function(value) { - return value.get('name').toUpperCase(); - }, 'name'); - ``` + * 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. - ## Example with multiple bound properties + @class Test + @namespace Ember + */ + var Test = { + /** + Hash containing all known test helpers. - `Ember.Handlebars.registerBoundHelper` supports binding to - multiple properties, e.g.: + @property _helpers + @private + @since 1.7.0 + */ + _helpers: helpers, - ```javascript - Ember.Handlebars.registerBoundHelper('concatenate', function() { - var values = Array.prototype.slice.call(arguments, 0, -1); - return values.join('||'); - }); - ``` + /** + `registerHelper` is used to register a test helper that will be injected + when `App.injectTestHelpers` is called. - 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. + The helper method will always be called with the current Application as + the first parameter. - ## Use with unbound helper + For example: - The `{{unbound}}` helper can be used with bound helper invocations - to render them in their unbound form, e.g. + ```javascript + Ember.Test.registerHelper('boot', function(app) { + Ember.run(app, app.advanceReadiness); + }); + ``` - ```handlebars - {{unbound capitalize name}} - ``` + This helper can later be called without arguments because it will be + called with `app` as the first parameter. - In this example, if the name property changes, the helper - will not re-render. + ```javascript + App = Ember.Application.create(); + App.injectTestHelpers(); + boot(); + ``` - ## Use with blocks not supported + @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 } + }; + }, - Bound helpers do not support use with Handlebars blocks or - the addition of child views of any kind. + /** + `registerAsyncHelper` is used to register an async test helper that will be injected + when `App.injectTestHelpers` is called. - @method registerBoundHelper - @for Ember.Handlebars - @param {String} name - @param {Function} function - @param {String} dependentKeys* - */ - function registerBoundHelper(name, fn) { - var boundHelperArgs = slice.call(arguments, 1), - boundFn = makeBoundHelper.apply(this, boundHelperArgs); - EmberHandlebars.registerHelper(name, boundFn); - }; + The helper method will always be called with the current Application as + the first parameter. - /** - A helper function used by `registerBoundHelper`. Takes the - provided Handlebars helper function fn and returns it in wrapped - bound helper form. + For example: - The main use case for using this outside of `registerBoundHelper` - is for registering helpers on the container: + ```javascript + Ember.Test.registerAsyncHelper('boot', function(app) { + Ember.run(app, app.advanceReadiness); + }); + ``` - ```js - var boundHelperFn = Ember.Handlebars.makeBoundHelper(function(word) { - return word.toUpperCase(); - }); + 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. - container.register('helper:my-bound-helper', boundHelperFn); - ``` - In the above example, if the helper function hadn't been wrapped in - `makeBoundHelper`, the registered helper would be unbound. + For example: - @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); - } - } - } + ```javascript + Ember.Test.registerAsyncHelper('deletePost', function(app, postId) { + click('.delete-' + postId); + }); - // Handle case when helper invocation is preceded by `unbound`, e.g. - // {{unbound myHelper foo}} - if (data.isUnbound) { - return evaluateUnboundHelper(this, fn, normalizedProperties, options); - } + // ... in your test + visit('/post/2'); + deletePost(2); + visit('/post/3'); + deletePost(3); + ``` - var bindView = new SimpleHandlebarsView(null, null, !options.hash.unescaped, options.data); + @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 } + }; + }, - // Override SimpleHandlebarsView's method for generating the view's content. - bindView.normalizedValue = function() { - var args = [], boundOption; + /** + Remove a previously added helper method. - // 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); - } + Example: - 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); + ```javascript + Ember.Test.unregisterHelper('wait'); + ``` - // Run the supplied helper function. - return fn.apply(currentContext, args); - }; + @public + @method unregisterHelper + @param {String} name The helper to remove. + */ + unregisterHelper: function(name) { + delete helpers[name]; + delete Test.Promise.prototype[name]; + }, - view.appendChild(bindView); + /** + Used to register callbacks to be fired whenever `App.injectTestHelpers` + is called. - // 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)); - } - } + The callback will receive the current application as an argument. - // Observe each property. - for (loc = 0, len = watchedProperties.length; loc < len; ++loc) { - property = watchedProperties[loc]; - view.registerObserver(property.root, property.path, bindView, bindView.rerender); - } + Example: - if (types[0] !== 'ID' || normalizedProperties.length === 0) { - return; - } + ```javascript + Ember.Test.onInjectHelpers(function() { + Ember.$(document).ajaxSend(function() { + Test.pendingAjaxRequests++; + }); - // Add dependent key observers to the first param - var normalized = normalizedProperties[0], - pathRoot = normalized.root, - path = normalized.path; + Ember.$(document).ajaxComplete(function() { + Test.pendingAjaxRequests--; + }); + }); + ``` - if(!isEmpty(path)) { - prefixPathForDependentKeys = path + '.'; - } - for (var i=0, l=dependentKeys.length; i{{user.name}} + var read = __dependency1__.read; + var subscribe = __dependency1__.subscribe; + var unsubscribe = __dependency1__.unsubscribe; + var run = __dependency2__["default"]; -
    -
    {{user.role.label}}
    - {{user.role.id}} + function AttrNode(attrName, attrValue) { + this.init(attrName, attrValue); + } -

    {{user.role.description}}

    -
    - ``` + AttrNode.prototype.init = function init(attrName, simpleAttrValue){ + this.isView = true; - `{{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: + // That these semantics are used is very unfortunate. + this.tagName = ''; + this.classNameBindings = []; - ```handlebars -
    {{user.name}}
    + this.attrName = attrName; + this.attrValue = simpleAttrValue; + this.isDirty = true; + this.lastValue = null; -
    - {{#with user.role}} -
    {{label}}
    - {{id}} + subscribe(this.attrValue, this.rerender, this); + }; -

    {{description}}

    - {{/with}} -
    - ``` + AttrNode.prototype.renderIfDirty = function renderIfDirty(){ + if (this.isDirty) { + var value = read(this.attrValue); + if (value !== this.lastValue) { + this._renderer.renderTree(this, this._parentView); + } else { + this.isDirty = false; + } + } + }; - ### `as` operator + AttrNode.prototype.render = function render(buffer) { + this.isDirty = false; + var value = read(this.attrValue); - 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. + this._morph.setContent(value); - ```handlebars - // posts might not be - {{#with user.posts as blogPosts}} -
    - There are {{blogPosts.length}} blog posts written by {{user.name}}. -
    + this.lastValue = value; + }; - {{#each post in blogPosts}} -
  • {{post.title}}
  • - {{/each}} - {{/with}} - ``` + AttrNode.prototype.rerender = function render() { + this.isDirty = true; + run.schedule('render', this, this.renderIfDirty); + }; - Without the `as` operator, it would be impossible to reference `user.name` in the example above. + AttrNode.prototype.destroy = function render() { + this.isDirty = false; + unsubscribe(this.attrValue, this.rerender, 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}}`. + var parent = this._parentView; + if (parent) { parent.removeChild(this); } + }; - ### `controller` option + __exports__["default"] = AttrNode; + }); +enifed("ember-views/attr_nodes/legacy_bind", + ["./attr_node","ember-runtime/system/string","ember-metal/utils","ember-metal/streams/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-htmlbars + */ - Adding `controller='something'` instructs the `{{with}}` helper to create and use an instance of - the specified controller with the new context as its content. + var AttrNode = __dependency1__["default"]; + var fmt = __dependency2__.fmt; + var typeOf = __dependency3__.typeOf; + var read = __dependency4__.read; - This is very similar to using an `itemController` option with the `{{each}}` helper. + function LegacyBindAttrNode(attrName, attrValue) { + this.init(attrName, attrValue); + } - ```handlebars - {{#with users.posts controller='userBlogPosts'}} - {{!- The current context is wrapped in our controller instance }} - {{/with}} - ``` + LegacyBindAttrNode.prototype = AttrNode.prototype; - 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. + LegacyBindAttrNode.prototype.render = function render(buffer) { + this.isDirty = false; + var value = read(this.attrValue); + var type = typeOf(value); - @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'; + Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [value]), + value === null || value === undefined || type === 'number' || type === 'string' || type === 'boolean'); - if (arguments.length === 4) { - var keywordName, path, rootPath, normalized, contextPath; + this._morph.setContent(value); - 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]; + this.lastValue = value; + }; - if (path) { - helperName += ' ' + path + ' as ' + keywordName; - } + __exports__["default"] = LegacyBindAttrNode; + }); +enifed("ember-views/component_lookup", + ["ember-runtime/system/object","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var EmberObject = __dependency1__["default"]; - Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop); + __exports__["default"] = EmberObject.extend({ + lookupFactory: function(name, container) { - var localizedOptions = o_create(options); - localizedOptions.data = o_create(options.data); - localizedOptions.data.keywords = o_create(options.data.keywords || {}); + container = container || this.container; - if (isGlobalPath(path)) { - contextPath = path; - } else { - normalized = normalizePath(this, path, options.data); - path = normalized.path; - rootPath = normalized.root; + var fullName = 'component:' + name; + var templateFullName = 'template:components/' + name; + var templateRegistered = container && container.has(templateFullName); - // 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; + if (templateRegistered) { + container.injection(fullName, 'layout', templateFullName); } - localizedOptions.hash.keywordName = keywordName; - localizedOptions.hash.keywordPath = contextPath; - - bindContext = this; - context = path; - options = localizedOptions; - preserveContext = true; - } else { - 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); + var Component = container.lookupFactory(fullName); - helperName += ' ' + context; - bindContext = options.contexts[0]; - preserveContext = false; + // 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; + } } + }); + }); +enifed("ember-views/mixins/component_template_deprecation", + ["ember-metal/core","ember-metal/property_get","ember-metal/mixin","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // Ember.deprecate + var get = __dependency2__.get; + var Mixin = __dependency3__.Mixin; - options.helperName = helperName; - options.isWithHelper = true; - - 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) + The ComponentTemplateDeprecation mixin is used to provide a useful + deprecation warning when using either `template` or `templateName` with + a component. The `template` and `templateName` properties specified at + extend time are moved to `layout` and `layoutName` respectively. - @method if - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string + `Ember.ComponentTemplateDeprecation` is used internally by Ember in + `Ember.Component`. + + @class ComponentTemplateDeprecation + @namespace Ember */ - 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); + __exports__["default"] = Mixin.create({ + /** + @private - options.helperName = options.helperName || ('if ' + context); + Moves `templateName` to `layoutName` and `template` to `layout` at extend + time if a layout is not also specified. - if (options.data.isUnbound) { - return helpers.unboundIf.call(options.contexts[0], context, options); - } else { - return helpers.boundIf.call(options.contexts[0], context, options); - } - } + Note that this currently modifies the mixin themselves, which is technically + dubious but is practically of little consequence. This may change in the + future. - /** - @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); + @method willMergeMixin + @since 1.4.0 + */ + willMergeMixin: function(props) { + // must call _super here to ensure that the ActionHandler + // mixin is setup properly (moves actions -> _actions) + // + // Calling super is only OK here since we KNOW that + // there is another Mixin loaded first. + this._super.apply(this, arguments); - var fn = options.fn, inverse = options.inverse, helperName = 'unless'; + var deprecatedProperty, replacementProperty; + var layoutSpecified = (props.layoutName || props.layout || get(this, 'layoutName')); - if (context) { - helperName += ' ' + context; - } + if (props.templateName && !layoutSpecified) { + deprecatedProperty = 'templateName'; + replacementProperty = 'layoutName'; + + props.layoutName = props.templateName; + delete props['templateName']; + } - options.fn = inverse; - options.inverse = fn; + if (props.template && !layoutSpecified) { + deprecatedProperty = 'template'; + replacementProperty = 'layout'; - options.helperName = options.helperName || helperName; + props.layout = props.template; + delete props['template']; + } - if (options.data.isUnbound) { - return helpers.unboundIf.call(options.contexts[0], context, options); - } else { - return helpers.boundIf.call(options.contexts[0], context, options); + 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"; /** - `bind-attr` allows you to create a binding between DOM element attributes and - Ember objects. For example: + @module ember + @submodule ember-views + */ - ```handlebars - imageTitle - ``` + var get = __dependency1__.get; + var set = __dependency2__.set; + var Mixin = __dependency3__.Mixin; + var TargetActionSupport = __dependency4__["default"]; - 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"`. + /** + `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. - If the rendering context of this template is the following object: + 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: - ```javascript - { - imageUrl: 'http://lolcats.info/haz-a-funny', - imageTitle: 'A humorous image of a cat' - } + ```handlebars + {{! application.hbs}} + + {{input escape-press='alertUser'}} ``` - The resulting HTML output will be: + ```javascript + App = Ember.Application.create(); - ```html - A humorous image of a cat + App.ApplicationController = Ember.Controller.extend({ + actions: { + alertUser: function ( currentValue ) { + alert( 'escape pressed, current value: ' + currentValue ); + } + } + }); ``` - `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: + 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: ```handlebars - imageTitle - ``` + {{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 | + +--------------------+----------------+ - ### `bind-attr` and the `class` attribute + @class TextSupport + @namespace Ember + @uses Ember.TargetActionSupport + @extends Ember.Mixin + @private + */ + var TextSupport = Mixin.create(TargetActionSupport, { + value: "", - `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: + attributeBindings: [ + 'autocapitalize', + 'autocorrect', + 'autofocus', + 'disabled', + 'form', + 'maxlength', + 'placeholder', + 'readonly', + 'required', + 'selectionDirection', + 'spellcheck', + 'tabindex', + 'title' + ], + placeholder: null, + disabled: false, + maxlength: null, - * a string return value of an object's property. - * a boolean return value of an object's property - * a hard-coded value + init: function() { + this._super(); + this.on("paste", this, this._elementValueDidChange); + this.on("cut", this, this._elementValueDidChange); + this.on("input", this, this._elementValueDidChange); + }, - 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: + /** + The action to be sent when the user presses the return key. - ```javascript - AView = View.extend({ - someProperty: function() { - return "aValue"; - }.property() - }) - ``` + 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. - ```handlebars - - ``` + Options are: - 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`. + * `enter`: the user pressed enter + * `keyPress`: the user pressed a key - A class name is provided via the syntax - `somePropertyName:class-name-if-true`. + @property onEvent + @type String + @default enter + */ + onEvent: 'enter', - ```javascript - AView = View.extend({ - someBool: true - }) - ``` + /** + Whether the `keyUp` event that triggers an `action` to be sent continues + propagating to other views. - ```handlebars - - ``` + 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. - Result in the following rendered output: + If you would like parent views to receive the `keyUp` event even after an + action has been dispatched, set `bubbles` to true. - ```html - - ``` + @property bubbles + @type Boolean + @default false + */ + bubbles: false, - 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: + interpretKeyEvents: function(event) { + var map = TextSupport.KEY_EVENTS; + var method = map[event.keyCode]; - ```handlebars - - ``` + this._elementValueDidChange(); + if (method) { return this[method](event); } + }, - A hard-coded value can be used by prepending `:` to the desired - class name: `:class-name-to-always-apply`. + _elementValueDidChange: function() { + set(this, 'value', this.$().val()); + }, - ```handlebars - - ``` + change: function(event) { + this._elementValueDidChange(event); + }, - Results in the following rendered output: + /** + Allows you to specify a controller action to invoke when either the `enter` + key is pressed or, in the case of the field being a textarea, when a newline + is inserted. To use this method, give your field an `insert-newline` + attribute. The value of that attribute should be the name of the action + in your controller that you wish to invoke. - ```html - - ``` + For an example on how to use the `insert-newline` attribute, please + reference the example near the top of this file. - All three strategies - string return value, boolean return value, and - hard-coded value – can be combined in a single declaration: + @method insertNewline + @param {Event} event + */ + insertNewline: function(event) { + sendAction('enter', this, event); + sendAction('insert-newline', this, event); + }, - ```handlebars - - ``` + /** + Allows you to specify a controller action to invoke when the escape button + is pressed. To use this method, give your field an `escape-press` + attribute. The value of that attribute should be the name of the action + in your controller that you wish to invoke. - @method bind-attr - @for Ember.Handlebars.helpers - @param {Hash} options - @return {String} HTML string - */ - function bindAttrHelper(options) { - var attrs = options.hash; + For an example on how to use the `escape-press` attribute, please reference + the example near the top of this file. - Ember.assert("You must specify at least one hash argument to bind-attr", !!keys(attrs).length); + @method cancel + @param {Event} event + */ + cancel: function(event) { + sendAction('escape-press', this, event); + }, - var view = options.data.view; - var ret = []; + /** + Allows you to specify a controller action to invoke when a field receives + focus. To use this method, give your field a `focus-in` attribute. The value + of that attribute should be the name of the action in your controller + that you wish to invoke. - // 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; + For an example on how to use the `focus-in` attribute, please reference the + example near the top of this file. - // 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; + @method focusIn + @param {Event} event + */ + focusIn: function(event) { + sendAction('focus-in', this, event); + }, - // Handle classes differently, as we can bind multiple classes - var classBindings = attrs['class']; - if (classBindings != null) { - var classResults = bindClasses(ctx, classBindings, view, dataId, options); + /** + Allows you to specify a controller action to invoke when a field loses + focus. To use this method, give your field a `focus-out` attribute. The value + of that attribute should be the name of the action in your controller + that you wish to invoke. - ret.push('class="' + Handlebars.Utils.escapeExpression(classResults.join(' ')) + '"'); - delete attrs['class']; - } + For an example on how to use the `focus-out` attribute, please reference the + example near the top of this file. - var attrKeys = keys(attrs); + @method focusOut + @param {Event} event + */ + focusOut: function(event) { + this._elementValueDidChange(event); + sendAction('focus-out', this, event); + }, - // 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; + /** + Allows you to specify a controller action to invoke when a key is pressed. + To use this method, give your field a `key-press` attribute. The value of + that attribute should be the name of the action in your controller you + that wish to invoke. - Ember.assert(fmt("You must provide an expression as the value of bound attribute. You specified: %@=%@", [attr, path]), typeof path === 'string'); + For an example on how to use the `key-press` attribute, please reference the + example near the top of this file. - normalized = normalizePath(ctx, path, options.data); + @method keyPress + @param {Event} event + */ + keyPress: function(event) { + sendAction('key-press', this, event); + }, - var value = (path === 'this') ? normalized.root : handlebarsGet(ctx, path, options), - type = typeOf(value); + /** + Allows you to specify a controller action to invoke when a key-up event is + fired. To use this method, give your field a `key-up` attribute. The value + of that attribute should be the name of the action in your controller + that you wish to invoke. - Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [value]), value === null || value === undefined || type === 'number' || type === 'string' || type === 'boolean'); + For an example on how to use the `key-up` attribute, please reference the + example near the top of this file. - var observer, invoker; + @method keyUp + @param {Event} event + */ + keyUp: function(event) { + this.interpretKeyEvents(event); - observer = function observer() { - var result = handlebarsGet(ctx, path, options); + this.sendAction('key-up', get(this, 'value'), event); + }, - 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'); + /** + Allows you to specify a controller action to invoke when a key-down event is + fired. To use this method, give your field a `key-down` attribute. The value + of that attribute should be the name of the action in your controller that + you wish to invoke. - var elem = view.$("[data-bindattr-" + dataId + "='" + dataId + "']"); + For an example on how to use the `key-down` attribute, please reference the + example near the top of this file. - // 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; - } + @method keyDown + @param {Event} event + */ + keyDown: function(event) { + this.sendAction('key-down', get(this, 'value'), event); + } + }); - View.applyAttributeBindings(elem, attr, result); - }; + TextSupport.KEY_EVENTS = { + 13: 'insertNewline', + 27: 'cancel' + }; - // 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); - } + // 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); + var on = get(view, 'onEvent'); + var value = get(view, 'value'); - // 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); + // 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); + } + + view.sendAction(eventName, value); - // 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(' ')); + if (action || on === eventName) { + if(!get(view, 'bubbles')) { + event.stopPropagation(); + } + } } + __exports__["default"] = TextSupport; + }); +enifed("ember-views/mixins/view_target_action_support", + ["ember-metal/mixin","ember-runtime/mixins/target_action_support","ember-metal/alias","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var Mixin = __dependency1__.Mixin; + var TargetActionSupport = __dependency2__["default"]; + var alias = __dependency3__["default"]; + /** - See `bind-attr` + `Ember.ViewTargetActionSupport` is a mixin that can be included in a + view class to add a `triggerAction` method with semantics similar to + the Handlebars `{{action}}` helper. It provides intelligent defaults + for the action's target: the view's controller; and the context that is + sent with the action: the view's context. - @method bindAttr - @for Ember.Handlebars.helpers - @deprecated - @param {Function} context - @param {Hash} options - @return {String} HTML string + Note: 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 custom View subclasses. + + For example: + + ```javascript + App.SaveButtonView = Ember.View.extend(Ember.ViewTargetActionSupport, { + action: 'save', + click: function() { + this.triggerAction(); // Sends the `save` action, along with the current context + // to the current controller + } + }); + ``` + + The `action` can be provided as properties of an optional object argument + to `triggerAction` as well. + + ```javascript + App.SaveButtonView = Ember.View.extend(Ember.ViewTargetActionSupport, { + click: function() { + this.triggerAction({ + action: 'save' + }); // Sends the `save` action, along with the current context + // to the current controller + } + }); + ``` + + @class ViewTargetActionSupport + @namespace Ember + @extends Ember.TargetActionSupport */ - function bindAttrHelperDeprecated() { - Ember.warn("The 'bindAttr' view helper is deprecated in favor of 'bind-attr'"); - return helpers['bind-attr'].apply(this, arguments); - } + __exports__["default"] = Mixin.create(TargetActionSupport, { + /** + @property target + */ + target: alias('controller'), + /** + @property actionContext + */ + actionContext: alias('context') + }); + }); +enifed("ember-views/streams/class_name_binding", + ["ember-metal/streams/utils","ember-metal/property_get","ember-runtime/system/string","ember-metal/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + var chainStream = __dependency1__.chainStream; + var read = __dependency1__.read; + var get = __dependency2__.get; + var dasherize = __dependency3__.dasherize; + var isArray = __dependency4__.isArray; /** - Helper that, given a space-separated string of property paths and a context, - returns an array of class names. Calling this method also has the side - effect of setting up observers at those property paths, such that if they - change, the correct class name will be reapplied to the DOM element. + Parse a path and return an object which holds the parsed properties. - For example, if you pass the string "fooBar", it will first look up the - "fooBar" value of the context. If that value is true, it will add the - "foo-bar" class to the current element (i.e., the dasherized form of - "fooBar"). If the value is a string, it will add that string as the class. - Otherwise, it will not add any new class name. + For example a path like "content.isEnabled:enabled:disabled" will return the + following object: + ```javascript + { + path: "content.isEnabled", + className: "enabled", + falsyClassName: "disabled", + classNames: ":enabled:disabled" + } + ``` + + @method parsePropertyPath + @static @private - @method bindClasses - @for Ember.Handlebars - @param {Ember.Object} context The context from which to lookup properties - @param {String} classBindings A string, space-separated, of class bindings - to use - @param {View} view The view in which observers should look for the - element to update - @param {Srting} bindAttrId Optional bindAttr id used to lookup elements - @return {Array} An array of class names to add - */ - function bindClasses(context, classBindings, view, bindAttrId, options) { - var ret = [], newClass, value, elem; - - // Helper method to retrieve the property from the context and - // determine which class string to return, based on whether it is - // a Boolean or not. - var classStringForPath = function(root, parsedPath, options) { - var val, - path = parsedPath.path; - - if (path === 'this') { - val = root; - } else if (path === '') { - val = true; - } else { - val = handlebarsGet(root, path, options); + */ + function parsePropertyPath(path) { + var split = path.split(':'); + var propertyPath = split[0]; + var classNames = ""; + var className, falsyClassName; + + // 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]; + } + + classNames = ':' + className; + if (falsyClassName) { + classNames += ":" + falsyClassName; } + } - return View._classStringForValue(path, val, parsedPath.className, parsedPath.falsyClassName); + return { + path: propertyPath, + classNames: classNames, + className: (className === '') ? undefined : className, + falsyClassName: falsyClassName }; + } - // For each property passed, loop through and setup - // an observer. - forEach.call(classBindings.split(' '), function(binding) { + __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 + @private + */ + function classStringForValue(path, val, className, falsyClassName) { + if(isArray(val)) { + val = get(val, 'length') !== 0; + } - // 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; + // 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; - var observer, invoker; + } else if (falsyClassName && !val) { + return falsyClassName; - var parsedPath = View._parsePropertyPath(binding), - path = parsedPath.path, - pathRoot = context, - normalized; + } else { + return null; + } - if (path !== '' && path !== 'this') { - normalized = normalizePath(context, path, options.data); + // 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]); - pathRoot = normalized.root; - path = normalized.path; - } + // If the value is not false, undefined, or null, return the current + // value of the property. + } else if (val !== false && val != null) { + return val; - // Set up an observer on the context. If the property changes, toggle the - // class name. - observer = function() { - // Get the current value of the property - newClass = classStringForPath(context, parsedPath, options); - elem = bindAttrId ? view.$("[data-bindattr-" + bindAttrId + "='" + bindAttrId + "']") : view.$(); + // Nothing to display. Return null so that the old class is removed + // but no new class is added. + } else { + return null; + } + } - // If we can't find the element anymore, a parent template has been - // re-rendered and we've been nuked. Remove the observer. - if (!elem || elem.length === 0) { - removeObserver(pathRoot, path, invoker); - } else { - // If we had previously added a class to the element, remove it. - if (oldClass) { - elem.removeClass(oldClass); - } + __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 + ); + }); + } + } - // 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; - } - } - }; + __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; - if (path !== '' && path !== 'this') { - view.registerObserver(pathRoot, path, observer); - } + function ConditionalStream(test, consequent, alternate) { + this.init(); - // We've already setup the observer; now we just need to figure out the - // correct behavior right now on the first pass through. - value = classStringForPath(context, parsedPath, options); + this.oldTestResult = undefined; + this.test = test; + this.consequent = consequent; + this.alternate = alternate; + } - if (value) { - ret.push(value); + ConditionalStream.prototype = create(Stream.prototype); - // Make sure we save the current value so that it can be removed if the - // observer fires. - oldClass = value; + ConditionalStream.prototype.valueFn = function() { + var oldTestResult = this.oldTestResult; + var newTestResult = !!read(this.test); + + 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); } - }); - return ret; + switch (newTestResult) { + case true: subscribe(this.consequent, this.notify, this); break; + case false: subscribe(this.alternate, this.notify, this); + } + + this.oldTestResult = newTestResult; + } + + return newTestResult ? read(this.consequent) : read(this.alternate); }; - __exports__.bind = bind; - __exports__._triageMustacheHelper = _triageMustacheHelper; - __exports__.resolveHelper = resolveHelper; - __exports__.bindHelper = bindHelper; - __exports__.boundIfHelper = boundIfHelper; - __exports__.unboundIfHelper = unboundIfHelper; - __exports__.withHelper = withHelper; - __exports__.ifHelper = ifHelper; - __exports__.unlessHelper = unlessHelper; - __exports__.bindAttrHelper = bindAttrHelper; - __exports__.bindAttrHelperDeprecated = bindAttrHelperDeprecated; - __exports__.bindClasses = bindClasses; + __exports__["default"] = ConditionalStream; }); -define("ember-handlebars/helpers/collection", - ["ember-metal/core","ember-metal/utils","ember-handlebars-compiler","ember-runtime/system/string","ember-metal/property_get","ember-handlebars/ext","ember-handlebars/helpers/view","ember-metal/computed","ember-views/views/collection_view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __exports__) { +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"; - /** - @module ember - @submodule ember-handlebars - */ - var Ember = __dependency1__["default"]; - // Ember.assert, Ember.deprecate - var inspect = __dependency2__.inspect; - - // var emberAssert = Ember.assert; - // emberDeprecate = Ember.deprecate; - - var EmberHandlebars = __dependency3__["default"]; - var helpers = EmberHandlebars.helpers; - var fmt = __dependency4__.fmt; - var get = __dependency5__.get; - var handlebarsGet = __dependency6__.handlebarsGet; - var ViewHelper = __dependency7__.ViewHelper; - var computed = __dependency8__.computed; - var CollectionView = __dependency9__["default"]; - - var alias = computed.alias; - /** - `{{collection}}` is a `Ember.Handlebars` helper for adding instances of - `Ember.CollectionView` to a template. See [Ember.CollectionView](/api/classes/Ember.CollectionView.html) - for additional information on how a `CollectionView` functions. - - `{{collection}}`'s primary use is as a block helper with a `contentBinding` - option pointing towards an `Ember.Array`-compatible object. An `Ember.View` - instance will be created for each item in its `content` property. Each view - will have its own `content` property set to the appropriate item in the - collection. + var merge = __dependency2__["default"]; + var create = __dependency3__.create; + var isGlobal = __dependency4__.isGlobal; + var Stream = __dependency5__["default"]; + var SimpleStream = __dependency6__["default"]; - The provided block will be applied as the template for each item's view. + function ContextStream(view) { + Ember.assert("ContextStream error: the argument is not a view", view && view.isView); - Given an empty `` the following template: + this.init(); + this.view = view; + } - ```handlebars - {{#collection contentBinding="App.items"}} - Hi {{view.content.name}} - {{/collection}} - ``` + ContextStream.prototype = create(Stream.prototype); - And the following application code + merge(ContextStream.prototype, { + value: function() {}, - ```javascript - App = Ember.Application.create() - App.items = [ - Ember.Object.create({name: 'Dave'}), - Ember.Object.create({name: 'Mary'}), - Ember.Object.create({name: 'Sara'}) - ] - ``` + _makeChildStream: function(key, _fullPath) { + var stream; - Will result in the HTML structure below + 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)); + } - ```html -
    -
    Hi Dave
    -
    Hi Mary
    -
    Hi Sara
    -
    - ``` + stream._isRoot = true; - ### Blockless use in a collection + if (key === 'controller') { + stream._isController = true; + } - If you provide an `itemViewClass` option that has its own `template` you can - omit the block. + return stream; + } + }); - The following template: + __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"]; - ```handlebars - {{collection contentBinding="App.items" itemViewClass="App.AnItemView"}} - ``` + 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; - And application code + 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); - ```javascript - App = Ember.Application.create(); - App.items = [ - Ember.Object.create({name: 'Dave'}), - Ember.Object.create({name: 'Mary'}), - Ember.Object.create({name: 'Sara'}) - ]; + this.init(); + this.source = source; + this.obj = undefined; + this.key = key; - App.AnItemView = Ember.View.extend({ - template: Ember.Handlebars.compile("Greetings {{view.content.name}}") - }); - ``` + if (isStream(source)) { + source.subscribe(this._didChange, this); + } + } - Will result in the HTML structure below + KeyStream.prototype = create(Stream.prototype); - ```html -
    -
    Greetings Dave
    -
    Greetings Mary
    -
    Greetings Sara
    -
    - ``` + merge(KeyStream.prototype, { + valueFn: function() { + var prevObj = this.obj; + var nextObj = read(this.source); - ### Specifying a CollectionView subclass + if (nextObj !== prevObj) { + if (prevObj && typeof prevObj === 'object') { + removeObserver(prevObj, this.key, this, this._didChange); + } - By default the `{{collection}}` helper will create an instance of - `Ember.CollectionView`. You can supply a `Ember.CollectionView` subclass to - the helper by passing it as the first argument: + if (nextObj && typeof nextObj === 'object') { + addObserver(nextObj, this.key, this, this._didChange); + } - ```handlebars - {{#collection App.MyCustomCollectionClass contentBinding="App.items"}} - Hi {{view.content.name}} - {{/collection}} - ``` + this.obj = nextObj; + } - ### Forwarded `item.*`-named Options + if (nextObj) { + return get(nextObj, this.key); + } + }, - As with the `{{view}}`, helper options passed to the `{{collection}}` will be - set on the resulting `Ember.CollectionView` as properties. Additionally, - options prefixed with `item` will be applied to the views rendered for each - item (note the camelcasing): + setValue: function(value) { + if (this.obj) { + set(this.obj, this.key, value); + } + }, - ```handlebars - {{#collection contentBinding="App.items" - itemTagName="p" - itemClassNames="greeting"}} - Howdy {{view.content.name}} - {{/collection}} - ``` + setSource: function(nextSource) { + Ember.assert("KeyStream error: source must be an object", typeof nextSource === 'object'); - Will result in the following HTML structure: + var prevSource = this.source; - ```html -
    -

    Howdy Dave

    -

    Howdy Mary

    -

    Howdy Sara

    -
    - ``` + if (nextSource !== prevSource) { + if (isStream(prevSource)) { + prevSource.unsubscribe(this._didChange, this); + } - @method collection - @for Ember.Handlebars.helpers - @param {String} path - @param {Hash} options - @return {String} HTML string - @deprecated Use `{{each}}` helper instead. - */ - function collectionHelper(path, options) { - Ember.deprecate("Using the {{collection}} helper without specifying a class has been deprecated as the {{each}} helper now supports the same functionality.", path !== 'collection'); + if (isStream(nextSource)) { + nextSource.subscribe(this._didChange, this); + } - // If no path is provided, treat path param as options. - if (path && path.data && path.data.isRenderData) { - options = path; - path = undefined; - Ember.assert("You cannot pass more than one argument to the collection helper", arguments.length === 1); - } else { - Ember.assert("You cannot pass more than one argument to the collection helper", arguments.length === 2); - } + this.source = nextSource; + this.notify(); + } + }, - var fn = options.fn; - var data = options.data; - var inverse = options.inverse; - var view = options.data.view; + _didChange: function() { + this.notify(); + }, + _super$destroy: Stream.prototype.destroy, - var controller, container; - // If passed a path string, convert that into an object. - // Otherwise, just default to the standard class. - var collectionClass; - if (path) { - controller = data.keywords.controller; - container = controller && controller.container; - collectionClass = handlebarsGet(this, path, options) || container.lookupFactory('view:' + path); - Ember.assert(fmt("%@ #collection: Could not find collection class %@", [data.view, path]), !!collectionClass); - } - else { - collectionClass = CollectionView; + destroy: function() { + if (this._super$destroy()) { + if (isStream(this.source)) { + this.source.unsubscribe(this._didChange, this); + } + + if (this.obj && typeof this.obj === 'object') { + removeObserver(this.obj, this.key, this, this._didChange); + } + + this.source = undefined; + this.obj = undefined; + return true; + } } + }); - var hash = options.hash, itemHash = {}, match; + __exports__["default"] = KeyStream; - // Extract item view class if provided else default to the standard class - var collectionPrototype = collectionClass.proto(), itemViewClass; + // The transpiler does not resolve cycles, so we export + // the `_makeChildStream` method onto `Stream` here. - if (hash.itemView) { - controller = data.keywords.controller; - Ember.assert('You specified an itemView, but the current context has no ' + - 'container to look the itemView up in. This probably means ' + - 'that you created a view manually, instead of through the ' + - 'container. Instead, use container.lookup("view:viewName"), ' + - 'which will properly instantiate your view.', - controller && controller.container); - container = controller.container; - itemViewClass = container.lookupFactory('view:' + hash.itemView); - Ember.assert('You specified the itemView ' + hash.itemView + ", but it was " + - "not found at " + container.describe("view:" + hash.itemView) + - " (and it was not registered in the container)", !!itemViewClass); - } else if (hash.itemViewClass) { - itemViewClass = handlebarsGet(collectionPrototype, hash.itemViewClass, options); + 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); + } else { + Ember.assert("View requires a container to resolve views not passed in through the context", !!container); + viewClass = container.lookupFactory('view:'+value); + } } else { - itemViewClass = collectionPrototype.itemViewClass; + viewClass = value; } - Ember.assert(fmt("%@ #collection: Could not find itemViewClass %@", [data.view, itemViewClass]), !!itemViewClass); + Ember.assert(fmt(value+" must be a subclass or an instance of Ember.View, not %@", [viewClass]), View.detect(viewClass) || View.detectInstance(viewClass)); - delete hash.itemViewClass; - delete hash.itemView; + return viewClass; + } - // Go through options passed to the {{collection}} helper and extract options - // that configure item views instead of the collection itself. - for (var prop in hash) { - if (hash.hasOwnProperty(prop)) { - match = prop.match(/^item(.)(.*)$/); + __exports__.readViewFactory = readViewFactory;function readUnwrappedModel(object) { + if (isStream(object)) { + var result = object.value(); - if (match && prop !== 'itemController') { - // Convert itemShouldFoo -> shouldFoo - itemHash[match[1].toLowerCase() + match[2]] = hash[prop]; - // Delete from hash as this will end up getting passed to the - // {{view}} helper method. - delete hash[prop]; + // If the path is exactly `controller` then we don't unwrap it. + if (!object._isController) { + while (ControllerMixin.detect(result)) { + result = get(result, 'model'); } } - } - - if (fn) { - itemHash.template = fn; - delete options.fn; - } - - var emptyViewClass; - if (inverse && inverse !== EmberHandlebars.VM.noop) { - emptyViewClass = get(collectionPrototype, 'emptyViewClass'); - emptyViewClass = emptyViewClass.extend({ - template: inverse, - tagName: itemHash.tagName - }); - } else if (hash.emptyViewClass) { - emptyViewClass = handlebarsGet(this, hash.emptyViewClass, options); - } - if (emptyViewClass) { hash.emptyView = emptyViewClass; } - if (hash.keyword) { - itemHash._context = this; + return result; } else { - itemHash._context = alias('content'); + return object; } + } - var viewOptions = ViewHelper.propertiesFromHTMLOptions({ data: data, hash: itemHash }, this); - hash.itemViewClass = itemViewClass.extend(viewOptions); + __exports__.readUnwrappedModel = readUnwrappedModel; + }); +enifed("ember-views/system/action_manager", + ["exports"], + function(__exports__) { + "use strict"; + /** + @module ember + @submodule ember-views + */ - options.helperName = options.helperName || 'collection'; + function ActionManager() {} - return helpers.view.call(this, collectionClass, options); - } + /** + Global action id hash. + + @private + @property registeredActions + @type Object + */ + ActionManager.registeredActions = {}; - __exports__["default"] = collectionHelper; + __exports__["default"] = ActionManager; }); -define("ember-handlebars/helpers/debug", - ["ember-metal/core","ember-metal/utils","ember-metal/logger","ember-metal/property_get","ember-handlebars/ext","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { +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"; - /*jshint debug:true*/ - /** @module ember - @submodule ember-handlebars + @submodule ember-views */ var Ember = __dependency1__["default"]; - // Ember.FEATURES, - var inspect = __dependency2__.inspect; - var Logger = __dependency3__["default"]; + // Ember.assert - var get = __dependency4__.get; - var normalizePath = __dependency5__.normalizePath; - var handlebarsGet = __dependency5__.handlebarsGet; + 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"]; - var a_slice = [].slice; + //ES6TODO: + // find a better way to do Ember.View.views without global state /** - `log` allows you to output the value of variables in the current rendering - context. `log` also accepts primitive types such as strings or numbers. - - ```handlebars - {{log "myVariable:" myVariable }} - ``` + `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. - @method log - @for Ember.Handlebars.helpers - @param {String} property + @class EventDispatcher + @namespace Ember + @private + @extends Ember.Object */ - function logHelper() { - var params = a_slice.call(arguments, 0, -1), - options = arguments[arguments.length - 1], - logger = Logger.log, - values = [], - allowPrimitives = true; + __exports__["default"] = EmberObject.extend({ - for (var i = 0; i < params.length; i++) { - var type = options.types[i]; + /** + 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. - if (type === 'ID' || !allowPrimitives) { - var context = (options.contexts && options.contexts[i]) || this, - normalized = normalizePath(context, params[i], options.data); + This set will be modified by `setup` to also include any events added at that time. - if (normalized.path === 'this') { - values.push(normalized.root); - } else { - values.push(handlebarsGet(normalized.root, normalized.path, options)); + @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' + }, + + /** + The root DOM element to which event listeners should be attached. Event + listeners will be attached to the document unless this is overridden. + + Can be specified as a DOMElement or a selector string. + + The default body is a string since this may be evaluated before document.body + exists in the DOM. + + @private + @property rootElement + @type DOMElement + @default 'body' + */ + rootElement: 'body', + + /** + 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. + + 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. + + ```javascript + var EventDispatcher = Em.EventDispatcher.extend({ + events: { + click : 'click', + focusin : 'focusIn', + focusout : 'focusOut', + change : 'change' + }, + canDispatchToEventManager: false + }); + container.register('event_dispatcher:main', EventDispatcher); + ``` + + @property canDispatchToEventManager + @type boolean + @default 'true' + @since 1.7.0 + */ + canDispatchToEventManager: true, + + /** + Sets up event listeners for standard browser events. + + 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. + + @private + @method setup + @param addedEvents {Hash} + */ + setup: function(addedEvents, rootElement) { + var event, events = get(this, 'events'); + + merge(events, addedEvents || {}); + + if (!isNone(rootElement)) { + set(this, 'rootElement', rootElement); + } + + rootElement = jQuery(get(this, 'rootElement')); + + 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); + + rootElement.addClass('ember-application'); + + 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')); + + for (event in events) { + if (events.hasOwnProperty(event)) { + this.setupHandler(rootElement, event, events[event]); } - } else { - values.push(params[i]); } - } + }, - logger.apply(logger, values); - }; + /** + Registers an event listener on the rootElement. If the given event is + triggered, the provided event handler will be triggered on the target view. - /** - Execute the `debugger` statement in the current context. + 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. - ```handlebars - {{debugger}} - ``` + @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; - Before invoking the `debugger` statement, there - are a few helpful variables defined in the - body of this helper that you can inspect while - debugging that describe how and where this - helper was invoked: + rootElement.on(event + '.ember', '.ember-view', function(evt, triggeringManager) { + var view = View.views[this.id]; + var result = true; - - templateContext: this is most likely a controller - from which this template looks up / displays properties - - typeOfTemplateContext: a string description of - what the templateContext is + var manager = self.canDispatchToEventManager ? self._findNearestEventManager(view, eventName) : null; - For example, if you're wondering why a value `{{foo}}` - isn't rendering as expected within a template, you - could place a `{{debugger}}` statement, and when - the `debugger;` breakpoint is hit, you can inspect - `templateContext`, determine if it's the object you - expect, and/or evaluate expressions in the console - to perform property lookups on the `templateContext`: + if (manager && manager !== triggeringManager) { + result = self._dispatchEvent(manager, evt, eventName, view); + } else if (view) { + result = self._bubbleEvent(view, evt, eventName); + } - ``` - > templateContext.get('foo') // -> "" - ``` + return result; + }); - @method debugger - @for Ember.Handlebars.helpers - @param {String} property - */ - function debuggerHelper(options) { + rootElement.on(event + '.ember', '[data-ember-action]', function(evt) { + var actionId = jQuery(evt.currentTarget).attr('data-ember-action'); + var action = ActionManager.registeredActions[actionId]; - // These are helpful values you can inspect while debugging. - var templateContext = this; - var typeOfTemplateContext = inspect(templateContext); + // 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); + } + }); + }, - debugger; - } + _findNearestEventManager: function(view, eventName) { + var manager = null; - __exports__.logHelper = logHelper; - __exports__.debuggerHelper = debuggerHelper; + while (view) { + manager = get(view, 'eventManager'); + if (manager && manager[eventName]) { break; } + + view = get(view, 'parentView'); + } + + return manager; + }, + + _dispatchEvent: function(object, evt, eventName, view) { + var result = true; + + 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); + } + + return result; + }, + + _bubbleEvent: function(view, evt, eventName) { + return run.join(view, view.handleEvent, eventName, evt); + }, + + destroy: function() { + var rootElement = get(this, 'rootElement'); + jQuery(rootElement).off('.ember', '**').removeClass('ember-application'); + return this._super(); + }, + + toString: function() { + return '(EventDispatcher)'; + } + }); }); -define("ember-handlebars/helpers/each", - ["ember-metal/core","ember-handlebars-compiler","ember-runtime/system/string","ember-metal/property_get","ember-metal/property_set","ember-handlebars/views/metamorph_view","ember-views/views/collection_view","ember-metal/binding","ember-runtime/controllers/controller","ember-runtime/controllers/array_controller","ember-runtime/mixins/array","ember-runtime/copy","ember-metal/run_loop","ember-metal/observer","ember-metal/events","ember-handlebars/ext","ember-metal/computed","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __exports__) { +enifed("ember-views/system/ext", + ["ember-metal/run_loop"], + function(__dependency1__) { "use strict"; - /** @module ember - @submodule ember-handlebars + @submodule ember-views */ - var Ember = __dependency1__["default"]; - // Ember.assert;, Ember.K - // var emberAssert = Ember.assert, - var K = Ember.K; - var EmberHandlebars = __dependency2__["default"]; - var helpers = EmberHandlebars.helpers; + var run = __dependency1__["default"]; - var fmt = __dependency3__.fmt; - var get = __dependency4__.get; - var set = __dependency5__.set; - var _Metamorph = __dependency6__._Metamorph; - var _MetamorphView = __dependency6__._MetamorphView; - var CollectionView = __dependency7__["default"]; - var Binding = __dependency8__.Binding; - var ControllerMixin = __dependency9__.ControllerMixin; - var ArrayController = __dependency10__["default"]; - var EmberArray = __dependency11__["default"]; - var copy = __dependency12__["default"]; - var run = __dependency13__["default"]; - var addObserver = __dependency14__.addObserver; - var removeObserver = __dependency14__.removeObserver; - var addBeforeObserver = __dependency14__.addBeforeObserver; - var removeBeforeObserver = __dependency14__.removeBeforeObserver; - var on = __dependency15__.on; - var handlebarsGet = __dependency16__.handlebarsGet; - var computed = __dependency17__.computed; + // 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 handlebarsGet = __dependency16__.handlebarsGet; + // ES6TODO: the functions on EnumerableUtils need their own exports + var forEach = __dependency2__.forEach; - var EachView = CollectionView.extend(_Metamorph, { + /** + Ember Views - init: function() { - var itemController = get(this, 'itemController'); - var binding; + @module ember + @submodule ember-views + @requires ember-runtime + @main ember-views + */ - if (itemController) { - var controller = get(this, 'controller.container').lookupFactory('controller:array').create({ - _isVirtual: true, - parentController: get(this, 'controller'), - itemController: itemController, - target: get(this, 'controller'), - _eachView: this - }); + var jQuery = (Ember.imports && Ember.imports.jQuery) || (this && this.jQuery); + if (!jQuery && typeof eriuqer === 'function') { + jQuery = eriuqer('jquery'); + } - this.disableContentObservers(function() { - set(this, 'content', controller); - binding = new Binding('content', '_eachView.dataSource').oneWay(); - binding.connect(controller); - }); + 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)); - set(this, '_arrayController', controller); - } else { - this.disableContentObservers(function() { - binding = new Binding('content', 'dataSource').oneWay(); - binding.connect(this); - }); - } + /** + @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' + ]; - return this._super(); - }, + // 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'] + }; + }); + } - _assertArrayLike: function(content) { - Ember.assert(fmt("The value that #each loops over must be an Array. You " + - "passed %@, but it should have been an ArrayController", - [content.constructor]), - !ControllerMixin.detect(content) || - (content && content.isGenerated) || - content instanceof ArrayController); - Ember.assert(fmt("The value that #each loops over must be an Array. You passed %@", [(ControllerMixin.detect(content) && content.get('model') !== undefined) ? fmt("'%@' (wrapped in %@)", [content.get('model'), content]) : content]), EmberArray.detect(content)); - }, + __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 + */ - disableContentObservers: function(callback) { - removeBeforeObserver(this, 'content', null, '_contentWillChange'); - removeObserver(this, 'content', null, '_contentDidChange'); + var jQuery = __dependency1__["default"]; + var DOMHelper = __dependency2__.DOMHelper; + var Ember = __dependency3__["default"]; + var create = __dependency4__.create; + + // 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 + // - ``` + this.elementAttributes = null; + } - Take note that `"welcome"` is a string and not an object - reference. + if (props) { + for (prop in props) { + this.dom.setPropertyStrict(element, prop, props[prop]); + } - See [Ember.String.loc](/api/classes/Ember.String.html#method_loc) for how to - set up localized string references. + this.elementProperties = null; + } - @method loc - @for Ember.Handlebars.helpers - @param {String} str The string to format - @see {Ember.String#loc} - */ - function locHelper(str) { - return loc(str); - } + this._element = element; + }, - __exports__["default"] = locHelper; - }); -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; + /** + @method element + @return {DOMElement} The element corresponding to the generated HTML + of this buffer + */ + element: function() { + var content = this.innerContent(); + // No content means a text node buffer, with the content + // in _element. Ember._BoundView is an example. + if (content === null) { + return this._element; + } - var isNone = __dependency2__.isNone; - var handlebarsGet = __dependency3__.handlebarsGet; - var bind = __dependency4__.bind; + var contextualElement = this.innerContextualElement(content); + this.dom.detectNamespace(contextualElement); - /** - @module ember - @submodule ember-handlebars - */ + if (!this._element) { + this._element = document.createDocumentFragment(); + } - /** - The `partial` helper renders another template without - changing the template context: + if (content.nodeType) { + this._element.appendChild(content); + } else { + var nodes; + nodes = this.dom.parseHTML(content, contextualElement); + while (nodes[0]) { + this._element.appendChild(nodes[0]); + } + } + // This should only happen with legacy string buffers + if (this.childViews.length > 0) { + this.hydrateMorphs(contextualElement); + } - ```handlebars - {{foo}} - {{partial "nav"}} - ``` + return this._element; + }, - 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. + /** + Generates the HTML content for this buffer. - If a "_nav" template isn't found, the `partial` helper will - fall back to a template named "nav". + @method string + @return {String} The generated HTML + */ + string: function() { + if (this._element) { + // Firefox versions < 11 do not have support for element.outerHTML. + var thisElement = this.element(); + var outerHTML = thisElement.outerHTML; + if (typeof outerHTML === 'undefined') { + return jQuery('
    ').append(thisElement).html(); + } + return outerHTML; + } else { + return this.innerString(); + } + }, + + outerContextualElement: function() { + if (!this._outerContextualElement) { + Ember.deprecate("The render buffer expects an outer contextualElement to exist." + + " This ensures DOM that requires context is correctly generated (tr, SVG tags)." + + " Defaulting to document.body, but this will be removed in the future"); + this.outerContextualElement = document.body; + } + return this._outerContextualElement; + }, + + innerContextualElement: function(html) { + var innerContextualElement; + if (this._element && this._element.nodeType === 1) { + innerContextualElement = this._element; + } else { + innerContextualElement = this.outerContextualElement(); + } + + var omittedStartTag; + if (html) { + omittedStartTag = detectOmittedStartTag(html, innerContextualElement); + } + return omittedStartTag || innerContextualElement; + }, + + innerString: function() { + var content = this.innerContent(); + if (content && !content.nodeType) { + return content; + } + }, + + innerContent: function() { + return this.buffer; + } + }; + }); +enifed("ember-views/system/renderer", + ["ember-metal/core","ember-metal-views/renderer","ember-metal/platform","ember-views/system/render_buffer","ember-metal/run_loop","ember-metal/property_set","ember-metal/property_get","ember-metal/instrumentation","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + var Renderer = __dependency2__["default"]; + var create = __dependency3__.create; + var renderBuffer = __dependency4__["default"]; + var run = __dependency5__["default"]; + var set = __dependency6__.set; + var get = __dependency7__.get; + var _instrumentStart = __dependency8__._instrumentStart; + var subscribers = __dependency8__.subscribers; - ## Bound template names + function EmberRenderer() { + this.buffer = renderBuffer(); + this._super$constructor(); + } - The parameter supplied to `partial` can also be a path - to a property containing a template name, e.g.: + EmberRenderer.prototype = create(Renderer.prototype); + EmberRenderer.prototype.constructor = EmberRenderer; + EmberRenderer.prototype._super$constructor = Renderer; - ```handlebars - {{partial someTemplateName}} - ``` + EmberRenderer.prototype.scheduleRender = + function EmberRenderer_scheduleRender(ctx, fn) { + return run.scheduleOnce('render', ctx, fn); + }; - 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. + EmberRenderer.prototype.cancelRender = + function EmberRenderer_cancelRender(id) { + run.cancel(id); + }; - ## Setting the partial's context with `with` + EmberRenderer.prototype.createElement = + function EmberRenderer_createElement(view, contextualElement) { + // 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 = view.tagName; + if (tagName === undefined) { + tagName = get(view, 'tagName'); + Ember.deprecate('In the future using a computed property to define tagName will not be permitted. That value will be respected, but changing it will not update the element.', !tagName); + } + var classNameBindings = view.classNameBindings; + var taglessViewWithClassBindings = tagName === '' && (classNameBindings && classNameBindings.length > 0); - The `partial` helper can be used in conjunction with the `with` - helper to set a context that will be used by the partial: + if (tagName === null || tagName === undefined) { + tagName = 'div'; + } - ```handlebars - {{#with currentUser}} - {{partial "user_info"}} - {{/with}} - ``` + Ember.assert('You cannot use `classNameBindings` on a tag-less view: ' + view.toString(), !taglessViewWithClassBindings); - @method partial - @for Ember.Handlebars.helpers - @param {String} partialName the name of the template to render minus the leading underscore - */ + var buffer = view.buffer = this.buffer; + buffer.reset(tagName, contextualElement); - function partialHelper(name, options) { + if (view.beforeRender) { + view.beforeRender(buffer); + } - var context = (options.contexts && options.contexts.length) ? options.contexts[0] : this; + if (tagName !== '') { + if (view.applyAttributesToBuffer) { + view.applyAttributesToBuffer(buffer); + } + buffer.generateElement(); + } - options.helperName = options.helperName || 'partial'; + if (view.render) { + view.render(buffer); + } - 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); - }; + if (view.afterRender) { + view.afterRender(buffer); + } - return bind.call(context, name, options, true, exists); - } else { - // Render the partial right into parent template. - renderPartial(context, name, options); - } - } + var element = buffer.element(); - function exists(value) { - return !isNone(value); - } + view.buffer = null; + if (element && element.nodeType === 1) { + view.element = element; + } + return element; + }; - function renderPartial(context, name, options) { - var nameParts = name.split("/"); - var lastPart = nameParts[nameParts.length - 1]; + EmberRenderer.prototype.destroyView = function destroyView(view) { + view.removedFromDOM = true; + view.destroy(); + }; - nameParts[nameParts.length - 1] = "_" + lastPart; + EmberRenderer.prototype.childViews = function childViews(view) { + return view._childViews; + }; - var view = options.data.view; - var underscoredName = nameParts.join("/"); - var template = view.templateForName(underscoredName); - var deprecatedTemplate = !template && view.templateForName(name); + Renderer.prototype.willCreateElement = function (view) { + if (subscribers.length && view.instrumentDetails) { + view._instrumentEnd = _instrumentStart('render.'+view.instrumentName, function viewInstrumentDetails() { + var details = {}; + view.instrumentDetails(details); + return details; + }); + } + if (view._transitionTo) { + view._transitionTo('inBuffer'); + } + }; // inBuffer + Renderer.prototype.didCreateElement = function (view) { + if (view._transitionTo) { + view._transitionTo('hasElement'); + } + if (view._instrumentEnd) { + view._instrumentEnd(); + } + }; // hasElement + Renderer.prototype.willInsertElement = function (view) { + if (view.trigger) { view.trigger('willInsertElement'); } + }; // will place into DOM + Renderer.prototype.didInsertElement = function (view) { + if (view._transitionTo) { + view._transitionTo('inDOM'); + } + if (view.trigger) { view.trigger('didInsertElement'); } + }; // inDOM // placed into DOM - Ember.assert("Unable to find partial with name '"+name+"'.", template || deprecatedTemplate); + Renderer.prototype.willRemoveElement = function (view) {}; - template = template || deprecatedTemplate; + Renderer.prototype.willDestroyElement = function (view) { + if (view.trigger) { view.trigger('willDestroyElement'); } + if (view.trigger) { view.trigger('willClearRender'); } + }; - template(context, { data: options.data }); - } + Renderer.prototype.didDestroyElement = function (view) { + set(view, 'element', null); + if (view._transitionTo) { + view._transitionTo('preRender'); + } + }; // element destroyed so view.destroy shouldn't try to remove it removedFromDOM - __exports__["default"] = partialHelper; + __exports__["default"] = EmberRenderer; }); -define("ember-handlebars/helpers/shared", - ["ember-handlebars/ext","exports"], - function(__dependency1__, __exports__) { +enifed("ember-views/system/sanitize_attribute_value", + ["exports"], + function(__exports__) { "use strict"; - var handlebarsGet = __dependency1__.handlebarsGet; + /* jshint scripturl:true */ + + var parsingNode; + var badProtocols = { + 'javascript:': true, + 'vbscript:': true + }; + + var badTags = { + 'A': true, + 'BODY': true, + 'LINK': true, + 'IMG': true, + 'IFRAME': true + }; - function resolvePaths(options) { - var ret = [], - contexts = options.contexts, - roots = options.roots, - data = options.data; + var badAttributes = { + 'href': true, + 'src': true, + 'background': true + }; + __exports__.badAttributes = badAttributes; + __exports__["default"] = function sanitizeAttributeValue(element, attribute, value) { + var tagName; - for (var i=0, l=contexts.length; i - {{#with loggedInUser}} - Last Login: {{lastLogin}} - User Info: {{template "user_info"}} - {{/with}} - - ``` - - ```html - - ``` + function isSimpleClick(event) { + var modifier = event.shiftKey || event.metaKey || event.altKey || event.ctrlKey; + var secondaryClick = event.which > 1; // IE9 may return undefined - ```handlebars - {{#if isUser}} - {{template "user_info"}} - {{else}} - {{template "unlogged_user_info"}} - {{/if}} - ``` + return !modifier && !secondaryClick; + } - This helper looks for templates in the global `Ember.TEMPLATES` hash. If you - add ` + ``` - initialize: function(container, application) { - application.register('location:history-test', HistoryTestLocation); - } - }); - ``` + And associate it by name using a view's `templateName` property: - @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); + ```javascript + AView = Ember.View.extend({ + templateName: 'some-template' + }); + ``` - this.implementations[name] = implementation; - }, + If you have nested resources, your Handlebars template will look like this: - implementations: {}, - _location: window.location, + ```html + + ``` - /** - Returns the current `location.hash` by parsing location.href since browsers - inconsistently URL-decode `location.hash`. + And `templateName` property: - https://bugzilla.mozilla.org/show_bug.cgi?id=483304 + ```javascript + AView = Ember.View.extend({ + templateName: 'posts/new' + }); + ``` - @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('#'); + Using a value for `templateName` that does not have a Handlebars template + with a matching `data-template-name` attribute will throw an error. - if (hashIndex === -1) { - return ''; - } else { - return href.substr(hashIndex); - } - } - }; + 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: - __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; + ```javascript + AView = Ember.View.extend({ + defaultTemplate: Ember.Handlebars.compile('I was the default'), + template: null, + templateName: null + }); + ``` - var EmberLocation = __dependency4__["default"]; - var HistoryLocation = __dependency5__["default"]; - var HashLocation = __dependency6__["default"]; - var NoneLocation = __dependency7__["default"]; + Will result in instances with an HTML representation of: - /** - @module ember - @submodule ember-routing - */ + ```html +
    I was the default
    + ``` - /** - Ember.AutoLocation will select the best location option based off browser - support with the priority order: history, hash, none. + If a `template` or `templateName` is provided it will take precedence over + `defaultTemplate`: - Clean pushState paths accessed by hashchange-only browsers will be redirected - to the hash-equivalent and vice versa so future transitions are consistent. + ```javascript + AView = Ember.View.extend({ + defaultTemplate: Ember.Handlebars.compile('I was the default') + }); - 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. + aView = AView.create({ + template: Ember.Handlebars.compile('I was the template, not default') + }); + ``` - @class AutoLocation - @namespace Ember - @static - */ - var AutoLocation = { + Will result in the following HTML representation when rendered: - /** - @private + ```html +
    I was the template, not default
    + ``` - This property is used by router:main to know whether to cancel the routing - setup process, which is needed while we redirect the browser. + ## View Context - @since 1.5.1 - @property cancelRouterSetup - @default false - */ - cancelRouterSetup: false, + The default context of the compiled template is the view's controller: - /** - @private + ```javascript + AView = Ember.View.extend({ + template: Ember.Handlebars.compile('Hello {{excitedGreeting}}') + }); - Will be pre-pended to path upon state change. + aController = Ember.Object.create({ + firstName: 'Barry', + excitedGreeting: function() { + return this.get("content.firstName") + "!!!" + }.property() + }); - @since 1.5.1 - @property rootURL - @default '/' - */ - rootURL: '/', + aView = AView.create({ + controller: aController + }); + ``` - /** - @private + Will result in an HTML representation of: - Attached for mocking in tests + ```html +
    Hello Barry!!!
    + ``` - @since 1.5.1 - @property _window - @default window - */ - _window: window, + 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. - /** - @private + ## Layouts - Attached for mocking in tests + 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. - @property location - @default window.location - */ - _location: window.location, + Most typically in Ember a layout will be a compiled `Ember.Handlebars` + template. - /** - @private + A view's layout can be set directly with the `layout` property or reference + an existing Handlebars template by name with the `layoutName` property. - Attached for mocking in tests + 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: - @since 1.5.1 - @property _history - @default window.history - */ - _history: window.history, + ```javascript + AViewWithLayout = Ember.View.extend({ + layout: Ember.Handlebars.compile("
    {{yield}}
    "), + template: Ember.Handlebars.compile("I got wrapped") + }); + ``` - /** - @private + Will result in view instances with an HTML representation of: - Attached for mocking in tests + ```html +
    +
    + I got wrapped +
    +
    + ``` - @since 1.5.1 - @property _HistoryLocation - @default Ember.HistoryLocation - */ - _HistoryLocation: HistoryLocation, + See [Ember.Handlebars.helpers.yield](/api/classes/Ember.Handlebars.helpers.html#method_yield) + for more information. - /** - @private + ## Responding to Browser Events - Attached for mocking in tests + 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. - @since 1.5.1 - @property _HashLocation - @default Ember.HashLocation - */ - _HashLocation: HashLocation, + ### Method Implementation - /** - @private + 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. - Attached for mocking in tests + ```javascript + AView = Ember.View.extend({ + click: function(event) { + // will be called when when an instance's + // rendered element is clicked + } + }); + ``` - @since 1.5.1 - @property _NoneLocation - @default Ember.NoneLocation - */ - _NoneLocation: NoneLocation, + ### Event Managers - /** - @private + 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. + + ```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 + } + }) + }); + ``` - Returns location.origin or builds it if device doesn't support it. + An event defined for an event manager takes precedence over events of the + same name handled through methods on the view. - @method _getOrigin - */ - _getOrigin: function () { - var location = this._location, - origin = location.origin; + ```javascript + AView = Ember.View.extend({ + mouseEnter: function(event) { + // will never trigger. + }, + eventManager: Ember.Object.create({ + mouseEnter: function(event, view) { + // takes precedence over AView#mouseEnter + } + }) + }); + ``` - // Older browsers, especially IE, don't have origin - if (!origin) { - origin = location.protocol + '//' + location.hostname; + 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. - if (location.port) { - origin += ':' + location.port; + ```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 } + }) + }); + + 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 origin; - }, + ### Handlebars `{{action}}` Helper - /** - @private + See [Handlebars.helpers.action](/api/classes/Ember.Handlebars.helpers.html#method_action). - We assume that if the history object has a pushState method, the host should - support HistoryLocation. + ### Event Names - @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; + 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`. - // 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; - } + Touch events: - return !!(this._history && 'pushState' in this._history); - }, + * `touchStart` + * `touchMove` + * `touchEnd` + * `touchCancel` - /** - @private + Keyboard events - IE8 running in IE7 compatibility mode gives false positive, so we must also - check documentMode. + * `keyDown` + * `keyUp` + * `keyPress` - @method _getSupportsHashChange - */ - _getSupportsHashChange: function () { - var _window = this._window, - documentMode = _window.document.documentMode; + Mouse events - return ('onhashchange' in _window && (documentMode === undefined || documentMode > 7 )); - }, + * `mouseDown` + * `mouseUp` + * `contextMenu` + * `click` + * `doubleClick` + * `mouseMove` + * `focusIn` + * `focusOut` + * `mouseEnter` + * `mouseLeave` - /** - @private + Form events: - Redirects the browser using location.replace, prepending the locatin.origin - to prevent phishing attempts + * `submit` + * `change` + * `focusIn` + * `focusOut` + * `input` - @method _replacePath - */ - _replacePath: function (path) { - this._location.replace(this._getOrigin() + path); - }, + HTML5 drag and drop events: - /** - @since 1.5.1 - @private - @method _getRootURL - */ - _getRootURL: function () { - return this.rootURL; - }, + * `dragStart` + * `drag` + * `dragEnter` + * `dragLeave` + * `dragOver` + * `dragEnd` + * `drop` - /** - @private + ## Handlebars `{{view}}` Helper - Returns the current `location.pathname`, normalized for IE inconsistencies. + 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. - @method _getPath + @class View + @namespace Ember + @extends Ember.CoreView + */ + var View = CoreView.extend({ + + concatenatedProperties: ['classNames', 'classNameBindings', 'attributeBindings'], + + /** + @property isView + @type Boolean + @default true + @static */ - _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; - } + isView: true, - return pathname; - }, + // .......................................................... + // TEMPLATE SUPPORT + // /** - @private + The name of the template to lookup if no template is provided. - Returns normalized location.hash as an alias to Ember.Location._getHash + By default `Ember.View` will lookup a template with this name in + `Ember.TEMPLATES` (a shared global object). - @since 1.5.1 - @method _getHash + @property templateName + @type String + @default null */ - _getHash: EmberLocation._getHash, + templateName: null, /** - @private + The name of the layout to lookup if no layout is provided. - Returns location.search + By default `Ember.View` will lookup a template with this name in + `Ember.TEMPLATES` (a shared global object). - @since 1.5.1 - @method _getQuery + @property layoutName + @type String + @default null */ - _getQuery: function () { - return this._location.search; - }, + layoutName: null, /** - @private - - Returns the full pathname including query and hash + Used to identify this view during debugging - @method _getFullPath + @property instrumentDisplay + @type String */ - _getFullPath: function () { - return this._getPath() + this._getQuery() + this._getHash(); - }, + instrumentDisplay: computed(function() { + if (this.helperName) { + return '{{' + this.helperName + '}}'; + } + }), /** - @private + 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. - 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) + In general, you should set the `templateName` property instead of setting + the template yourself. - @method _getHistoryPath + @property template + @type Function */ - _getHistoryPath: function () { - var rootURL = this._getRootURL(), - path = this._getPath(), - hash = this._getHash(), - query = this._getQuery(), - rootURLIndex = path.indexOf(rootURL), - routeHash, hashParts; - - Ember.assert('Path ' + path + ' does not start with the provided rootURL ' + rootURL, rootURLIndex === 0); - - // 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(); + template: computed('templateName', function(key, value) { + if (value !== undefined) { return value; } - // 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); - } + var templateName = get(this, 'templateName'); + var template = this.templateForName(templateName, 'template'); - // This is the "expected" final order - path += routeHash; - path += query; + Ember.assert("You specified the templateName " + templateName + " for " + this + ", but it did not exist.", !templateName || !!template); - if (hashParts.length) { - path += '#' + hashParts.join('#'); - } - } else { - path += query; - path += hash; - } + return template || get(this, 'defaultTemplate'); + }), - return path; - }, + _controller: null, /** - @private - - Returns the current path as it should appear for HashLocation supported - browsers. This may very well differ from the real current path. + The controller managing this view. If this property is set, it will be + made available for use by the template. - @method _getHashPath + @property controller + @type Object */ - _getHashPath: function () { - var rootURL = this._getRootURL(), - path = rootURL, - historyPath = this._getHistoryPath(), - routePath = historyPath.substr(rootURL.length); - - if (routePath !== '') { - if (routePath.charAt(0) !== '/') { - routePath = '/' + routePath; - } + controller: computed(function(key, value) { + if (arguments.length === 2) { + this._controller = value; + return value; + } - path += '#' + routePath; + if (this._controller) { + return this._controller; } - return path; - }, + var parentView = get(this, '_parentView'); + return parentView ? get(parentView, 'controller') : null; + }), /** - Selects the best location option based off browser support and returns an - instance of that Location class. + 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. - @see Ember.AutoLocation - @method create + 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. + + @property layout + @type Function */ - 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; - } + layout: computed(function(key) { + var layoutName = get(this, 'layoutName'); + var layout = this.templateForName(layoutName, 'layout'); - var historyPath, hashPath, - cancelRouterSetup = false, - implementationClass = this._NoneLocation, - currentPath = this._getFullPath(); + Ember.assert("You specified the layoutName " + layoutName + " for " + this + ", but it did not exist.", !layoutName || !!layout); - if (this._getSupportsHistory()) { - historyPath = this._getHistoryPath(); + return layout || get(this, 'defaultLayout'); + }).property('layoutName'), - // 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); - } + _yield: function(context, options, morph) { + var template = get(this, 'template'); - } else if (this._getSupportsHashChange()) { - hashPath = this._getHashPath(); + if (template) { + var useHTMLBars = false; + + useHTMLBars = template.isHTMLBars; + - // 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; + if (useHTMLBars) { + return template.render(this, options, morph.contextualElement); } 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); + return template(context, options); } } - - var implementation = implementationClass.create.apply(implementationClass, arguments); - - if (cancelRouterSetup) { - set(implementation, 'cancelRouterSetup', true); - } - - return implementation; - } - }; - - __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; - - var EmberObject = __dependency5__["default"]; - var EmberLocation = __dependency6__["default"]; - var jQuery = __dependency7__["default"]; - - /** - @module ember - @submodule ember-routing - */ - - /** - `Ember.HashLocation` implements the location API using the browser's - hash. At present, it relies on a `hashchange` event existing in the - browser. - - @class HashLocation - @namespace Ember - @extends Ember.Object - */ - var HashLocation = EmberObject.extend({ - implementation: 'hash', - - init: function() { - set(this, 'location', get(this, '_location') || window.location); }, - /** - @private - - Returns normalized location.hash - - @since 1.5.1 - @method getHash - */ - getHash: EmberLocation._getHash, - - /** - Returns the current `location.hash`, minus the '#' at the front. + _blockArguments: EMPTY_ARRAY, - @private - @method getURL - */ - getURL: function() { - return this.getHash().substr(1); - }, + templateForName: function(name, type) { + if (!name) { return; } + Ember.assert("templateNames are not allowed to contain periods: "+name, name.indexOf('.') === -1); - /** - Set the `location.hash` and remembers what was set. This prevents - `onUpdateURL` callbacks from triggering when the hash was set by - `HashLocation`. + 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'); + } - @private - @method setURL - @param path {String} - */ - setURL: function(path) { - get(this, 'location').hash = path; - set(this, 'lastSetURL', path); + return this.container.lookup('template:' + name); }, /** - Uses location.replace to update the url without a page reload - or history modification. + The object from which templates should access properties. - @private - @method replaceURL - @param path {String} - */ - replaceURL: function(path) { - get(this, 'location').replace('#' + path); - set(this, 'lastSetURL', path); - }, + 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. - /** - 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. + By default, this will be the view's controller. - @private - @method onUpdateURL - @param callback {Function} + @property context + @type Object */ - onUpdateURL: function(callback) { - var self = this; - var guid = guidFor(this); - - jQuery(window).on('hashchange.ember-location-'+guid, function() { - run(function() { - var path = self.getURL(); - if (get(self, 'lastSetURL') === path) { return; } - - set(self, 'lastSetURL', null); - - callback(path); - }); - }); - }, + context: computed(function(key, value) { + if (arguments.length === 2) { + set(this, '_context', value); + return value; + } else { + return get(this, '_context'); + } + })["volatile"](), /** - Given a URL, formats it to be placed into the page as part - of an element's `href` attribute. + 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. - This is used, for example, when using the {{action}} helper - to generate a URL based on an event. + The context of a view is looked up as follows: - @private - @method formatURL - @param url {String} - */ - formatURL: function(url) { - return '#'+url; - }, + 1. Supplied context (usually by Handlebars) + 2. Specified controller + 3. `parentView`'s context (for a child of a ContainerView) - /** - Cleans up the HashLocation event listener. + 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. + @property _context @private - @method willDestroy */ - willDestroy: function() { - var guid = guidFor(this); - - jQuery(window).off('hashchange.ember-location-'+guid); - } - }); - - __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; - - var EmberObject = __dependency5__["default"]; - var jQuery = __dependency6__["default"]; - - /** - @module ember - @submodule ember-routing - */ - - var popstateFired = false; - var supportsHistoryState = window.history && 'state' in window.history; + _context: computed(function(key, value) { + if (arguments.length === 2) { + return value; + } - /** - Ember.HistoryLocation implements the location API using the browser's - history.pushState API. + var parentView, controller; - @class HistoryLocation - @namespace Ember - @extends Ember.Object - */ - var HistoryLocation = EmberObject.extend({ - implementation: 'history', + if (controller = get(this, 'controller')) { + return controller; + } - init: function() { - set(this, 'location', get(this, 'location') || window.location); - set(this, 'baseURL', jQuery('base').attr('href') || ''); - }, + parentView = this._parentView; + if (parentView) { + return get(parentView, '_context'); + } + + return null; + }), /** - Used to set state on first call to setURL + If a value that affects template rendering changes, the view should be + re-rendered to reflect the new value. + @method _contextDidChange @private - @method initState */ - initState: function() { - set(this, 'history', get(this, 'history') || window.history); - this.replaceState(this.formatURL(this.getURL())); - }, + _contextDidChange: observer('context', function() { + this.rerender(); + }), /** - Will be pre-pended to path upon state change + If `false`, the view will appear hidden in DOM. - @property rootURL - @default '/' + @property isVisible + @type Boolean + @default null */ - rootURL: '/', + isVisible: true, /** - Returns the current `location.pathname` without `rootURL` or `baseURL` + Array of child views. You should never edit this array directly. + Instead, use `appendChild` and `removeFromParent`. + @property childViews + @type Array + @default [] @private - @method getURL - @return url {String} */ - getURL: function() { - var rootURL = get(this, 'rootURL'), - location = get(this, 'location'), - path = location.pathname, - baseURL = get(this, 'baseURL'); + childViews: childViewsProperty, - rootURL = rootURL.replace(/\/$/, ''); - baseURL = baseURL.replace(/\/$/, ''); - var url = path.replace(baseURL, '').replace(rootURL, ''); + _childViews: EMPTY_ARRAY, - - return url; - }, + // 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'); } + } + }), + + // 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'); } + } + }), /** - Uses `history.pushState` to update the url without a page reload. + Return the nearest ancestor that is an instance of the provided + class. - @private - @method setURL - @param path {String} + @method nearestInstanceOf + @param {Class} klass Subclass of Ember.View (or Ember.View itself) + @return Ember.View + @deprecated */ - setURL: function(path) { - var state = this.getState(); - path = this.formatURL(path); + nearestInstanceOf: function(klass) { + Ember.deprecate("nearestInstanceOf is deprecated and will be removed from future releases. Use nearestOfType."); + var view = get(this, 'parentView'); - if (!state || state.path !== path) { - this.pushState(path); + while (view) { + if (view instanceof klass) { return view; } + view = get(view, 'parentView'); } }, /** - Uses `history.replaceState` to update the url without a page reload - or history modification. + Return the nearest ancestor that is an instance of the provided + class or mixin. - @private - @method replaceURL - @param path {String} + @method nearestOfType + @param {Class,Mixin} klass Subclass of Ember.View (or Ember.View itself), + or an instance of Ember.Mixin. + @return Ember.View */ - replaceURL: function(path) { - var state = this.getState(); - path = this.formatURL(path); + 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); }; - if (!state || state.path !== path) { - this.replaceState(path); + while (view) { + if (isOfType(view)) { return view; } + view = get(view, 'parentView'); } }, /** - 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 the nearest ancestor that has a given property. - @private - @method getState - @return state {Object} + @method nearestWithProperty + @param {String} property A property name + @return Ember.View */ - getState: function() { - return supportsHistoryState ? get(this, 'history').state : this._historyState; + nearestWithProperty: function(property) { + var view = get(this, 'parentView'); + + while (view) { + if (property in view) { return view; } + view = get(view, 'parentView'); + } }, /** - Pushes a new state. + Return the nearest ancestor whose parent is an instance of + `klass`. - @private - @method pushState - @param path {String} + @method nearestChildOf + @param {Class} klass Subclass of Ember.View (or Ember.View itself) + @return Ember.View */ - pushState: function(path) { - var state = { path: path }; - - get(this, 'history').pushState(state, null, path); + nearestChildOf: function(klass) { + var view = get(this, 'parentView'); - // store state if browser doesn't support `history.state` - if (!supportsHistoryState) { - this._historyState = state; + while (view) { + if (get(view, 'parentView') instanceof klass) { return view; } + view = get(view, 'parentView'); } - - // used for webkit workaround - this._previousURL = this.getURL(); }, /** - Replaces the current state. + When the parent view changes, recursively invalidate `controller` - @private - @method replaceState - @param path {String} + @method _parentViewDidChange + @private */ - replaceState: function(path) { - var state = { path: path }; + _parentViewDidChange: observer('_parentView', function() { + if (this.isDestroying) { return; } - get(this, 'history').replaceState(state, null, path); + this._setupKeywords(); + this.trigger('parentViewDidChange'); - // store state if browser doesn't support `history.state` - if (!supportsHistoryState) { - this._historyState = state; + if (get(this, 'parentView.controller') && !get(this, 'controller')) { + this.notifyPropertyChange('controller'); } + }), - // used for webkit workaround - this._previousURL = this.getURL(); - }, - - /** - Register a callback to be invoked whenever the browser - history changes, including using forward and back buttons. + _controllerDidChange: observer('controller', function() { + if (this.isDestroying) { return; } - @private - @method onUpdateURL - @param callback {Function} - */ - onUpdateURL: function(callback) { - var guid = guidFor(this), - self = this; + this.rerender(); - 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()); + this.forEachChildView(function(view) { + view.propertyDidChange('controller'); }); - }, + }), - /** - Used when using `{{action}}` helper. The url is always appended to the rootURL. + _setupKeywords: function() { + var keywords = this._keywords; + var contextView = this._contextView || this._parentView; - @private - @method formatURL - @param url {String} - @return formatted url {String} - */ - formatURL: function(url) { - var rootURL = get(this, 'rootURL'), - baseURL = get(this, 'baseURL'); + if (contextView) { + var parentKeywords = contextView._keywords; - if (url !== '') { - rootURL = rootURL.replace(/\/$/, ''); - baseURL = baseURL.replace(/\/$/, ''); - } else if(baseURL.match(/^\//) && rootURL.match(/^\//)) { - baseURL = baseURL.replace(/\/$/, ''); - } + keywords.view.setSource(this.isVirtual ? parentKeywords.view : this); - return baseURL + rootURL + url; + for (var name in parentKeywords) { + if (keywords[name]) continue; + keywords[name] = parentKeywords[name]; + } + } else { + keywords.view.setSource(this.isVirtual ? null : this); + } }, /** - Cleans up the HistoryLocation event listener. - - @private - @method willDestroy - */ - willDestroy: function() { - var guid = guidFor(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. - jQuery(window).off('popstate.ember-location-'+guid); - } - }); + 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. - __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 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'); - /** - @module ember - @submodule ember-routing - */ + if (template) { + var context = get(this, 'context'); + var output; - /** - 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). + var data = { + view: this, + buffer: buffer, + isRenderData: true + }; - @class NoneLocation - @namespace Ember - @extends Ember.Object - */ - var NoneLocation = EmberObject.extend({ - implementation: 'none', - path: '', + // 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. - /** - Returns the current path. + // The template should write directly to the render buffer instead + // of returning a string. + var options = { data: data }; + var useHTMLBars = false; - @private - @method getURL - @return {String} path - */ - getURL: function() { - return get(this, 'path'); - }, + + useHTMLBars = template.isHTMLBars; + - /** - Set the path and remembers what was set. Using this method - to change the path will not invoke the `updateURL` callback. + 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); + } - @private - @method setURL - @param path {String} - */ - setURL: function(path) { - set(this, 'path', path); + // If the template returned a string instead of writing to the buffer, + // push the string onto the buffer. + if (output !== undefined) { buffer.push(output); } + } }, /** - 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. + 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. - @private - @method onUpdateURL - @param callback {Function} - */ - onUpdateURL: function(callback) { - this.updateCallback = callback; - }, + 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`. - /** - Sets the path and calls the `updateURL` callback. + 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. - @private - @method handleURL - @param callback {Function} + @method rerender */ - handleURL: function(url) { - set(this, 'path', url); - this.updateCallback(url); + rerender: function() { + return this.currentState.rerender(this); }, /** - 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. + 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. + @method _applyClassNameBindings @private - @method formatURL - @param url {String} - @return {String} url */ - 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; - } - }); - - __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'); - - /** - Ember Routing - - @module ember - @submodule ember-routing - @requires ember-views - */ - - var EmberHandlebars = __dependency1__["default"]; - var Ember = __dependency2__["default"]; - - // ES6TODO: Cleanup modules with side-effects below - - 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; - - - Ember.Location = EmberLocation; - Ember.AutoLocation = AutoLocation; - Ember.HashLocation = HashLocation; - Ember.HistoryLocation = HistoryLocation; - Ember.NoneLocation = NoneLocation; - - Ember.controllerFor = controllerFor; - Ember.generateControllerFactory = generateControllerFactory; - Ember.generateController = generateController; - Ember.RouterDSL = RouterDSL; - Ember.Router = Router; - Ember.Route = Route; - Ember.LinkView = LinkView; - - Router.resolveParams = resolveParams; - Router.resolvePaths = resolvePaths; - - EmberHandlebars.ActionHelper = ActionHelper; - EmberHandlebars.OutletView = OutletView; - - EmberHandlebars.registerHelper('render', renderHelper) - EmberHandlebars.registerHelper('action', actionHelper); - EmberHandlebars.registerHelper('outlet', outletHelper); - EmberHandlebars.registerHelper('link-to', linkToHelper); - EmberHandlebars.registerHelper('linkTo', deprecatedLinkToHelper); - - __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; + _applyClassNameBindings: function(classBindings) { + var classNames = this.classNames; + var elem, newClass, dasherizedClass; - /** - @module ember - @submodule ember-routing - */ + // 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) { - /** + var boundBinding; + if (isStream(binding)) { + boundBinding = binding; + } else { + boundBinding = streamifyClassNameBinding(this, binding, '_view.'); + } - Finds a controller instance. + // 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; - @for Ember - @method controllerFor - @private - */ - var controllerFor = function(container, controllerName, lookupOptions) { - return container.lookup('controller:' + controllerName, lookupOptions); - }; + // 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); - /** - Generates a controller factory + // 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); + } - 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. + // 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; + } + }); - You can customize your generated controllers by defining - `App.ObjectController` or `App.ArrayController`. + // Get the class name for the property at its current value + dasherizedClass = read(boundBinding); - @for Ember - @method generateControllerFactory - @private - */ - var generateControllerFactory = function(container, controllerName, context) { - var Factory, fullName, instance, name, factoryName, controllerType; + if (dasherizedClass) { + // Ensure that it gets into the classNames array + // so it is displayed when we render. + addObject(classNames, dasherizedClass); - if (context && isArray(context)) { - controllerType = 'array'; - } else if (context) { - controllerType = 'object'; - } else { - controllerType = 'basic'; - } + // 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; + } - factoryName = 'controller:' + controllerType; + 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; + } + }); - Factory = container.lookupFactory(factoryName).extend({ - isGenerated: true, - toString: function() { - return "(generated " + controllerName + " controller)"; - } - }); + }, this); + }, - fullName = 'controller:' + controllerName; + _unspecifiedAttributeBindings: null, - container.register(fullName, Factory); + /** + 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. - return Factory; - }; + @method _applyAttributeBindings + @param {Ember.RenderBuffer} buffer + @private + */ + _applyAttributeBindings: function(buffer, attributeBindings) { + var attributeValue; + var unspecifiedAttributeBindings = this._unspecifiedAttributeBindings = this._unspecifiedAttributeBindings || {}; - /** - Generates and instantiates a controller. + forEach(attributeBindings, function(binding) { + var split = binding.split(':'); + var property = split[0]; + var attributeName = split[1] || property; - 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. + Ember.assert('You cannot use class as an attributeBinding, use classNameBindings instead.', attributeName !== 'class'); - @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 (property in this) { + this._setupAttributeBindingObservation(property, attributeName); - if (get(instance, 'namespace.LOG_ACTIVE_GENERATION')) { - Ember.Logger.info("generated -> " + fullName, { fullName: fullName }); - } + // 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); - return instance; - }; + // Lazily setup setUnknownProperty after attributeBindings are initially applied + this.setUnknownProperty = this._setUnknownProperty; + }, - __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 + _setupAttributeBindingObservation: function(property, attributeName) { + var attributeValue, elem; - /** - @module ember - @submodule ember-routing - */ + // Create an observer to add/remove/change the attribute if the + // JavaScript property changes. + var observer = function() { + elem = this.$(); - function DSL(name) { - this.parent = name; - this.matches = []; - } + attributeValue = get(this, property); - DSL.prototype = { - resource: function(name, options, callback) { - Ember.assert("'basic' cannot be used as a resource name.", name !== 'basic'); + View.applyAttributeBindings(elem, attributeName, attributeValue); + }; - if (arguments.length === 2 && typeof options === 'function') { - callback = options; - options = {}; - } + this.registerObserver(this, property, observer); + }, - if (arguments.length === 1) { - options = {}; - } + /** + We're using setUnknownProperty as a hook to setup attributeBinding observers for + properties that aren't defined on a view at initialization time. - if (typeof options.path !== 'string') { - options.path = "/" + name; - } + Note: setUnknownProperty will only be called once for each property. - 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); + @method setUnknownProperty + @param key + @param value + @private + */ + setUnknownProperty: null, // Gets defined after initialization by _applyAttributeBindings + + _setUnknownProperty: function(key, value) { + var attributeName = this._unspecifiedAttributeBindings && this._unspecifiedAttributeBindings[key]; + if (attributeName) { + this._setupAttributeBindingObservation(key, attributeName); } + defineProperty(this, key); + return set(this, key, value); + }, - }, + /** + Given a property name, returns a dasherized version of that + property name if the property evaluates to a non-falsy value. - push: function(url, name, callback) { - var parts = name.split('.'); - if (url === "" || url === "/" || parts[parts.length-1] === "index") { this.explicitIndex = true; } + For example, if the view has property `isUrgent` that evaluates to true, + passing `isUrgent` to this method will return `"is-urgent"`. - this.matches.push([url, name, callback]); + @method _classStringForProperty + @param property + @private + */ + _classStringForProperty: function(parsedPath) { + return View._classStringForValue(parsedPath.path, parsedPath.stream.value(), parsedPath.className, parsedPath.falsyClassName); }, - route: function(name, options) { - Ember.assert("'basic' cannot be used as a route name.", name !== 'basic'); + // .......................................................... + // ELEMENT SUPPORT + // - route(this, name, options); - }, + /** + Returns the current DOM element for the view. - generate: function() { - var dslMatches = this.matches; + @property element + @type DOMElement + */ + element: null, - if (!this.explicitIndex) { - this.route("index", { path: "/" }); - } + /** + 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. - return function(match) { - for (var i=0, l=dslMatches.length; i= 0) { + view = childViews[idx]; + callback(this, view, idx); + } - if (dsl.parent && dsl.parent !== 'application') { - name = dsl.parent + "." + name; - } + return this; + }, - dsl.push(options.path, name, null); - } + forEachChildView: function(callback) { + var childViews = this._childViews; - DSL.map = function(callback) { - var dsl = new DSL(); - callback.call(dsl); - return dsl; - }; + if (!childViews) { return this; } - __exports__["default"] = DSL; - }); -define("ember-routing/system/route", - ["ember-metal/core","ember-metal/error","ember-metal/property_get","ember-metal/property_set","ember-metal/get_properties","ember-metal/enumerable_utils","ember-metal/is_none","ember-metal/computed","ember-metal/utils","ember-metal/run_loop","ember-runtime/keys","ember-runtime/copy","ember-runtime/system/string","ember-runtime/system/object","ember-runtime/mixins/action_handler","ember-routing/system/controller_for","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // FEATURES, K, A, deprecate, assert, Logger - var EmberError = __dependency2__["default"]; - var get = __dependency3__.get; - var set = __dependency4__.set; - var getProperties = __dependency5__["default"]; - var EnumerableUtils = __dependency6__["default"]; - var isNone = __dependency7__.isNone; - var computed = __dependency8__.computed; - var typeOf = __dependency9__.typeOf; - var run = __dependency10__["default"]; + var len = childViews.length; + var view, idx; - var keys = __dependency11__["default"]; - var copy = __dependency12__["default"]; - var classify = __dependency13__.classify; - var fmt = __dependency13__.fmt; - var EmberObject = __dependency14__["default"]; - var ActionHandler = __dependency15__["default"]; - var generateController = __dependency16__.generateController; + for (idx = 0; idx < len; idx++) { + view = childViews[idx]; + callback(view); + } - /** - @module ember - @submodule ember-routing - */ + return this; + }, - var a_forEach = EnumerableUtils.forEach, - a_replace = EnumerableUtils.replace; + /** + Appends the view's element to the specified parent element. - /** - The `Ember.Route` class is used to define individual routes. Refer to - the [routing guide](http://emberjs.com/guides/routing/) for documentation. + If the view does not have an HTML representation yet, `createElement()` + will be called automatically. - @class Route - @namespace Ember - @extends Ember.Object - @uses Ember.ActionHandler - */ - var Route = EmberObject.extend(ActionHandler, { + 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. - /** - @private + 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 exit + @method appendTo + @param {String|DOMElement|jQuery} A selector, element, HTML string, or jQuery object + @return {Ember.View} receiver */ - exit: function() { - this.deactivate(); - this.teardownViews(); + appendTo: function(selector) { + var target = jQuery(selector); + + 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')); + + this.constructor.renderer.appendTo(this, target[0]); + + return this; }, /** - @private + 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. - @method enter + 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 + + @method replaceIn + @param {String|DOMElement|jQuery} target A selector, element, HTML string, or jQuery object + @return {Ember.View} received */ - enter: function() { - this.activate(); + replaceIn: function(selector) { + var target = jQuery(selector); + + 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')); + + this.constructor.renderer.replaceIn(this, target[0]); + + return this; }, /** - The name of the view to use by default when rendering this routes template. + 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. - When rendering a template, the route will, by default, determine the - template and view to use from the name of the route itself. If you need to - define a specific view, set this property. + If your application uses the `rootElement` property, you must append + the view within that element. Rendering views outside of the `rootElement` + is not supported. - This is useful when multiple routes would benefit from using the same view - because it doesn't require a custom `renderTemplate` method. For example, - the following routes will all render using the `App.PostsListView` view: + 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. - ```js - var PostsList = Ember.Route.extend({ - viewName: 'postsList', - }); + @method append + @return {Ember.View} receiver + */ + append: function() { + return this.appendTo(document.body); + }, - App.PostsIndexRoute = PostsList.extend(); - App.PostsArchivedRoute = PostsList.extend(); - ``` + /** + Removes the view's element from the element to which it is attached. - @property viewName - @type String - @default null - @since 1.4.0 + @method remove + @return {Ember.View} receiver */ - viewName: null, + 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(); } + }, /** - The name of the template to use by default when rendering this routes - template. + 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): - This is similar with `viewName`, but is useful when you just want a custom - template without a view. + ```handlebars + {{my-component elementId="a-really-cool-id"}} + ``` - ```js - var PostsList = Ember.Route.extend({ - templateName: 'posts/list' - }); + If not manually set a default value will be provided by the framework. - App.PostsIndexRoute = PostsList.extend(); - App.PostsArchivedRoute = PostsList.extend(); - ``` + Once rendered an element's `elementId` is considered immutable and you + should never change it. - @property templateName + @property elementId @type String - @default null - @since 1.4.0 */ - templateName: null, + elementId: null, /** - The name of the controller to associate with this route. + 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. - By default, Ember will lookup a route's controller that matches the name - of the route (i.e. `App.PostController` for `App.PostRoute`). However, - if you would like to define a specific controller to use, you can do so - using this property. + @method findElementInParentElement + @param {DOMElement} parentElement The parent's DOM element + @return {DOMElement} The discovered element + */ + findElementInParentElement: function(parentElem) { + var id = "#" + this.elementId; + return jQuery(id)[0] || jQuery(id, parentElem)[0]; + }, - This is useful in many ways, as the controller specified will be: + /** + Creates a DOM representation of the view and all of its child views by + recursively calling the `render()` method. - * passed to the `setupController` method. - * used as the controller for the view being rendered by the route. - * returned from a call to `controllerFor` for the route. + After the element has been inserted into the DOM, `didInsertElement` will + be called on this view and all of its child views. - @property controllerName - @type String - @default null - @since 1.4.0 + @method createElement + @return {Ember.View} receiver */ - controllerName: null, - - /** - The `willTransition` action is fired at the beginning of any - attempted transition with a `Transition` object as the sole - argument. This action can be used for aborting, redirecting, - or decorating the transition from the currently active routes. + createElement: function() { + if (this.element) { return this; } - A good example is preventing navigation when a form is - half-filled out: + this._didCreateElementWithoutMorph = true; + this.constructor.renderer.renderTree(this); - ```js - App.ContactFormRoute = Ember.Route.extend({ - actions: { - willTransition: function(transition) { - if (this.controller.get('userHasEnteredData')) { - this.controller.displayNavigationConfirm(); - transition.abort(); - } - } - } - }); - ``` + return this; + }, - You can also redirect elsewhere by calling - `this.transitionTo('elsewhere')` from within `willTransition`. - Note that `willTransition` will not be fired for the - redirecting `transitionTo`, since `willTransition` doesn't - fire when there is already a transition underway. If you want - subsequent `willTransition` actions to fire for the redirecting - transition, you must first explicitly call - `transition.abort()`. + /** + Called when a view is going to insert an element into the DOM. - @event willTransition - @param {Transition} transition + @event willInsertElement */ + willInsertElement: K, /** - The `didTransition` action is fired after a transition has - successfully been completed. This occurs after the normal model - hooks (`beforeModel`, `model`, `afterModel`, `setupController`) - have resolved. The `didTransition` action has no arguments, - however, it can be useful for tracking page views or resetting - state on the controller. + 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. - ```js - App.LoginRoute = Ember.Route.extend({ - actions: { - didTransition: function() { - this.controller.get('errors.base').clear(); - return true; // Bubble the didTransition event - } - } - }); - ``` + When a view has children, didInsertElement will be called on the + child view(s) first, bubbling upwards through the hierarchy. - @event didTransition - @since 1.2.0 + @event didInsertElement */ + didInsertElement: K, /** - The `loading` action is fired on the route when a route's `model` - hook returns a promise that is not already resolved. The current - `Transition` object is the first parameter and the route that - triggered the loading event is the second parameter. - - ```js - App.ApplicationRoute = Ember.Route.extend({ - actions: { - loading: function(transition, route) { - var view = Ember.View.create({ - classNames: ['app-loading'] - }) - .append(); - - this.router.one('didTransition', function () { - view.destroy(); - }); - return true; // Bubble the loading event - } - } - }); - ``` + 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 - @event loading - @param {Transition} transition - @param {Ember.Route} route The route that triggered the loading event - @since 1.2.0 + @event willClearRender */ + willClearRender: K, /** - When attempting to transition into a route, any of the hooks - may return a promise that rejects, at which point an `error` - action will be fired on the partially-entered routes, allowing - for per-route error handling logic, or shared error handling - logic defined on a parent route. + 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. - Here is an example of an error handler that will be invoked - for rejected promises from the various hooks on the route, - as well as any unhandled errors from child routes: + 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. - ```js - App.AdminRoute = Ember.Route.extend({ - beforeModel: function() { - return Ember.RSVP.reject("bad things!"); - }, + If you write a `willDestroyElement()` handler, you can assume that your + `didInsertElement()` handler was called earlier for the same element. - actions: { - error: function(error, transition) { - // Assuming we got here due to the error in `beforeModel`, - // we can expect that error === "bad things!", - // but a promise model rejecting would also - // call this hook, as would any errors encountered - // in `afterModel`. + You should not call or override this method yourself, but you may + want to implement the above callbacks. - // The `error` hook is also provided the failed - // `transition`, which can be stored and later - // `.retry()`d if desired. + @method destroyElement + @return {Ember.View} receiver + */ + destroyElement: function() { + return this.currentState.destroyElement(this); + }, - this.transitionTo('login'); - } - } - }); - ``` + /** + 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. - `error` actions that bubble up all the way to `ApplicationRoute` - will fire a default error handler that logs the error. You can - specify your own global default error handler by overriding the - `error` handler on `ApplicationRoute`: + Please note: any property changes made during this event will have no + effect on object observers. - ```js - App.ApplicationRoute = Ember.Route.extend({ - actions: { - error: function(error, transition) { - this.controllerFor('banner').displayError(error.message); - } - } - }); - ``` - @event error - @param {Error} error - @param {Transition} transition + @event willDestroyElement */ + willDestroyElement: K, /** - The controller associated with this route. + Called when the parentView property has changed. - Example + @event parentViewDidChange + */ + parentViewDidChange: K, - ```javascript - App.FormRoute = Ember.Route.extend({ - actions: { - willTransition: function(transition) { - if (this.controller.get('userHasEnteredData') && - !confirm("Are you sure you want to abandon progress?")) { - transition.abort(); - } else { - // Bubble the `willTransition` action so that - // parent routes can decide whether or not to abort. - return true; - } - } - } - }); - ``` + instrumentName: 'view', - @property controller - @type Ember.Controller - @since 1.6.0 - */ + instrumentDetails: function(hash) { + hash.template = get(this, 'templateName'); + this._super(hash); + }, - _actions: { + beforeRender: function(buffer) {}, - queryParamsDidChange: function(changed, totalPresent, removed) { - }, + afterRender: function(buffer) {}, - finalizeQueryParamChange: function(params, finalParams, transition) { - } + 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); + } + + // 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); + + var role = get(this, 'ariaRole'); + if (role) { + buffer.attr('role', role); + } + + if (get(this, 'isVisible') === false) { + buffer.style('display', 'none'); + } }, + // .......................................................... + // STANDARD RENDER PROPERTIES + // + /** - @deprecated + 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. - Please use `actions` instead. - @method events + By default, the render buffer will use a `
    ` tag for views. + + @property tagName + @type String + @default null */ - events: null, - mergedProperties: ['events'], + // We leave this null by default so we can tell the difference between + // the default case and a user-specified tag. + tagName: null, /** - This hook is executed when the router completely exits this route. It is - not executed when the model for the route changes. + 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. - @method deactivate + 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) + + @property ariaRole + @type String + @default null */ - deactivate: Ember.K, + ariaRole: null, /** - This hook is executed when the router enters the route. It is not executed - when the model for the route changes. + 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 activate + @property classNames + @type Array + @default ['ember-view'] */ - activate: Ember.K, + classNames: ['ember-view'], /** - Transition the application into another route. The route may - be either a single route or route path: + 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 - this.transitionTo('blogPosts'); - this.transitionTo('blogPosts.recentEntries'); + // Applies the 'high' class to the view element + Ember.View.extend({ + classNameBindings: ['priority'] + priority: 'high' + }); ``` - Optionally supply a model for the route in question. The model - will be serialized into the URL using the `serialize` hook of - the route: + If the value of the property is a Boolean, the name of that property is + added as a dasherized class name. ```javascript - this.transitionTo('blogPost', aPost); + // Applies the 'is-urgent' class to the view element + Ember.View.extend({ + classNameBindings: ['isUrgent'] + isUrgent: true + }); ``` - If a literal is passed (such as a number or a string), it will - be treated as an identifier instead. In this case, the `model` - hook of the route will be triggered: + If you would prefer to use a custom value instead of the dasherized + property name, you can pass a binding like this: ```javascript - this.transitionTo('blogPost', 1); + // Applies the 'urgent' class to the view element + Ember.View.extend({ + classNameBindings: ['isUrgent:urgent'] + isUrgent: true + }); ``` - Multiple models will be applied last to first recursively up the - resource tree. + This list of properties is inherited from the view's superclasses as well. + + @property classNameBindings + @type Array + @default [] + */ + classNameBindings: EMPTY_ARRAY, + + /** + 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. ```javascript - App.Router.map(function() { - this.resource('blogPost', {path:':blogPostId'}, function(){ - this.resource('blogComment', {path: ':blogCommentId'}); - }); + // Applies the type attribute to the element + // with the value "button", like
    + Ember.View.extend({ + attributeBindings: ['type'], + type: 'button' }); - - this.transitionTo('blogComment', aPost, aComment); - this.transitionTo('blogComment', 1, 13); ``` - It is also possible to pass a URL (a string that starts with a - `/`). This is intended for testing and debugging purposes and - should rarely be used in production code. + If the value of the property is a Boolean, the name of that property is + added as an attribute. ```javascript - this.transitionTo('/'); - this.transitionTo('/blog/post/1/comment/13'); + // Renders something like
    + Ember.View.extend({ + attributeBindings: ['enabled'], + enabled: true + }); ``` - See also 'replaceWith'. - - Simple Transition Example + @property attributeBindings + */ + attributeBindings: EMPTY_ARRAY, - ```javascript - App.Router.map(function() { - this.route("index"); - this.route("secret"); - this.route("fourOhFour", { path: "*:"}); - }); + // ....................................................... + // CORE DISPLAY METHODS + // - App.IndexRoute = Ember.Route.extend({ - actions: { - moveToSecret: function(context){ - if (authorized()){ - this.transitionTo('secret', context); - } - this.transitionTo('fourOhFour'); - } - } - }); - ``` + /** + Setup a view, but do not finish waking it up. - Transition to a nested route + * configure `childViews` + * register the view with the global views hash, which is used for event + dispatch - ```javascript - App.Router.map(function() { - this.resource('articles', { path: '/articles' }, function() { - this.route('new'); - }); - }); + @method init + @private + */ + init: function() { + if (!this.isVirtual && !this.elementId) { + this.elementId = guidFor(this); + } - App.IndexRoute = Ember.Route.extend({ - actions: { - transitionToNewArticle: function() { - this.transitionTo('articles.new'); - } - } - }); - ``` + this._super(); - Multiple Models Example + // 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; - ```javascript - App.Router.map(function() { - this.route("index"); - this.resource('breakfast', {path:':breakfastId'}, function(){ - this.resource('cereal', {path: ':cerealId'}); - }); - }); + if (!this._keywords) { + this._keywords = create(null); + } + this._keywords.view = new SimpleStream(); + this._keywords._view = this; + this._keywords.controller = new KeyStream(this, 'controller'); + this._setupKeywords(); - App.IndexRoute = Ember.Route.extend({ - actions: { - moveToChocolateCereal: function(){ - var cereal = { cerealId: "ChocolateYumminess"}, - breakfast = {breakfastId: "CerealAndMilk"}; + Ember.assert("Only arrays are allowed for 'classNameBindings'", typeOf(this.classNameBindings) === 'array'); + this.classNameBindings = emberA(this.classNameBindings.slice()); - this.transitionTo('cereal', breakfast, cereal); - } - } - }); - ``` + Ember.assert("Only arrays are allowed for 'classNames'", typeOf(this.classNames) === 'array'); + this.classNames = emberA(this.classNames.slice()); + }, - @method transitionTo - @param {String} name the name of the route or a URL - @param {...Object} models the model(s) or identifier(s) to be used while - transitioning to the route. - @return {Transition} the transition object associated with this - attempted transition - */ - transitionTo: function(name, context) { - var router = this.router; - return router.transitionTo.apply(router, arguments); + appendChild: function(view, options) { + return this.currentState.appendChild(this, view, options); }, /** - Perform a synchronous transition into another route without attempting - to resolve promises, update the URL, or abort any currently active - asynchronous transitions (i.e. regular transitions caused by - `transitionTo` or URL changes). + Removes the child view from the parent view. - This method is handy for performing intermediate transitions on the - way to a final destination route, and is called internally by the - default implementations of the `error` and `loading` handlers. + @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; } - @method intermediateTransitionTo - @param {String} name the name of the route - @param {...Object} models the model(s) to be used while transitioning - to the route. - @since 1.2.0 - */ - intermediateTransitionTo: function() { - var router = this.router; - router.intermediateTransitionTo.apply(router, arguments); - }, + // update parent node + set(view, '_parentView', null); - /** - Refresh the model on this route and any child routes, firing the - `beforeModel`, `model`, and `afterModel` hooks in a similar fashion - to how routes are entered when transitioning in from other route. - The current route params (e.g. `article_id`) will be passed in - to the respective model hooks, and if a different model is returned, - `setupController` and associated route hooks will re-fire as well. + // remove view from childViews array. + var childViews = this._childViews; - An example usage of this method is re-querying the server for the - latest information using the same parameters as when the route - was first entered. + removeObject(childViews, view); - Note that this will cause `model` hooks to fire even on routes - that were provided a model object when the route was initially - entered. + this.propertyDidChange('childViews'); // HUH?! what happened to will change? - @method refresh - @return {Transition} the transition object associated with this - attempted transition - @since 1.4.0 - */ - refresh: function() { - return this.router.router.refresh(this); + return this; }, /** - Transition into another route while replacing the current URL, if possible. - This will replace the current history entry instead of adding a new one. - Beside that, it is identical to `transitionTo` in all other respects. See - 'transitionTo' for additional information regarding multiple models. - - Example + Removes all children from the `parentView`. - ```javascript - App.Router.map(function() { - this.route("index"); - this.route("secret"); + @method removeAllChildren + @return {Ember.View} receiver + */ + removeAllChildren: function() { + return this.mutateChildViews(function(parentView, view) { + parentView.removeChild(view); }); + }, - App.SecretRoute = Ember.Route.extend({ - afterModel: function() { - if (!authorized()){ - this.replaceWith('index'); - } - } + destroyAllChildren: function() { + return this.mutateChildViews(function(parentView, view) { + view.destroy(); }); - ``` - - @method replaceWith - @param {String} name the name of the route or a URL - @param {...Object} models the model(s) or identifier(s) to be used while - transitioning to the route. - @return {Transition} the transition object associated with this - attempted transition - */ - replaceWith: function() { - var router = this.router; - return router.replaceWith.apply(router, arguments); }, /** - Sends an action to the router, which will delegate it to the currently - active route hierarchy per the bubbling rules explained under `actions`. + Removes the view from its `parentView`, if one is found. Otherwise + does nothing. - Example + @method removeFromParent + @return {Ember.View} receiver + */ + removeFromParent: function() { + var parent = this._parentView; - ```javascript - App.Router.map(function() { - this.route("index"); - }); + // Remove DOM element from parent + this.remove(); - App.ApplicationRoute = Ember.Route.extend({ - actions: { - track: function(arg) { - console.log(arg, 'was clicked'); - } - } - }); + if (parent) { parent.removeChild(this); } + return this; + }, - App.IndexRoute = Ember.Route.extend({ - actions: { - trackIfDebug: function(arg) { - if (debug) { - this.send('track', arg); - } - } - } - }); - ``` + /** + You must call `destroy` on a view to destroy the view (and all of its + child views). This will remove the view from any parent node, then make + sure that the DOM element managed by the view can be released by the + memory manager. - @method send - @param {String} name the name of the action to trigger - @param {...*} args + @method destroy */ - send: function() { - return this.router.send.apply(this.router, arguments); + destroy: function() { + // get parentView before calling super because it'll be destroyed + var nonVirtualParentView = get(this, 'parentView'); + var viewName = this.viewName; + + if (!this._super()) { return; } + + // remove from non-virtual parent view if viewName was specified + if (viewName && nonVirtualParentView) { + nonVirtualParentView.set(viewName, null); + } + + return this; }, /** - This hook is the entry point for router.js + 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. - @private - @method setup + @method createChildView + @param {Class|String} viewClass + @param {Hash} [attrs] Attributes to add + @return {Ember.View} new instance */ - setup: function(context, transition) { - var controllerName = this.controllerName || this.routeName, - controller = this.controllerFor(controllerName, true); - if (!controller) { - controller = this.generateController(controllerName, context); + createChildView: function(view, attrs) { + if (!view) { + throw new TypeError("createChildViews first argument must exist"); } - // Assign the route's controller so that it can more easily be - // referenced in action handlers - this.controller = controller; + if (view.isView && view._parentView === this && view.container === this.container) { + return view; + } - - if (this.setupControllers) { - Ember.deprecate("Ember.Route.setupControllers is deprecated. Please use Ember.Route.setupController(controller, model) instead."); - this.setupControllers(controller, context); - } else { + attrs = attrs || {}; + attrs._parentView = this; - - this.setupController(controller, context); - - } + if (CoreView.detect(view)) { + attrs.container = this.container; + view = view.create(attrs); - if (this.renderTemplates) { - Ember.deprecate("Ember.Route.renderTemplates is deprecated. Please use Ember.Route.renderTemplate(controller, model) instead."); - this.renderTemplates(context); + // 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); + + Ember.assert("Could not find view: '" + fullName + "'", !!ViewKlass); + + view = ViewKlass.create(attrs); } else { - this.renderTemplate(controller, context); + Ember.assert('You must pass instance or subclass of View', view.isView); + + attrs.container = this.container; + setProperties(view, attrs); } + + return view; }, + becameVisible: K, + becameHidden: K, + /** - This hook is the first of the route entry validation hooks - called when an attempt is made to transition into a route - or one of its children. It is called before `model` and - `afterModel`, and is appropriate for cases when: + When the view's `isVisible` property changes, toggle the visibility + element of the actual DOM element. - 1) A decision can be made to redirect elsewhere without - needing to resolve the model first. - 2) Any async operations need to occur first before the - model is attempted to be resolved. + @method _isVisibleDidChange + @private + */ + _isVisibleDidChange: observer('isVisible', function() { + if (this._isVisible === get(this, 'isVisible')) { return ; } + run.scheduleOnce('render', this, this._toggleVisibility); + }), - This hook is provided the current `transition` attempt - as a parameter, which can be used to `.abort()` the transition, - save it for a later `.retry()`, or retrieve values set - on it from a previous hook. You can also just call - `this.transitionTo` to another route to implicitly - abort the `transition`. + _toggleVisibility: function() { + var $el = this.$(); + var isVisible = get(this, 'isVisible'); - You can return a promise from this hook to pause the - transition until the promise resolves (or rejects). This could - be useful, for instance, for retrieving async code from - the server that is required to enter a route. + if (this._isVisible === isVisible) { return ; } - ```js - App.PostRoute = Ember.Route.extend({ - beforeModel: function(transition) { - if (!App.Post) { - return Ember.$.getScript('/models/post.js'); - } - } - }); - ``` + // It's important to keep these in sync, even if we don't yet have + // an element in the DOM to manipulate: + this._isVisible = isVisible; - If `App.Post` doesn't exist in the above example, - `beforeModel` will use jQuery's `getScript`, which - returns a promise that resolves after the server has - successfully retrieved and executed the code from the - server. Note that if an error were to occur, it would - be passed to the `error` hook on `Ember.Route`, but - it's also possible to handle errors specific to - `beforeModel` right from within the hook (to distinguish - from the shared error handling behavior of the `error` - hook): + if (!$el) { return; } - ```js - App.PostRoute = Ember.Route.extend({ - beforeModel: function(transition) { - if (!App.Post) { - var self = this; - return Ember.$.getScript('post.js').then(null, function(e) { - self.transitionTo('help'); + $el.toggle(isVisible); - // Note that the above transitionTo will implicitly - // halt the transition. If you were to return - // nothing from this promise reject handler, - // according to promise semantics, that would - // convert the reject into a resolve and the - // transition would continue. To propagate the - // error so that it'd be handled by the `error` - // hook, you would have to either - return Ember.RSVP.reject(e); - }); - } - } - }); - ``` + if (this._isAncestorHidden()) { return; } - @method beforeModel - @param {Transition} transition - @param {Object} queryParams the active query params for this route - @return {Promise} if the value returned from this hook is - a promise, the transition will pause until the transition - resolves. Otherwise, non-promise return values are not - utilized in any way. - */ - beforeModel: Ember.K, + if (isVisible) { + this._notifyBecameVisible(); + } else { + this._notifyBecameHidden(); + } + }, - /** - This hook is called after this route's model has resolved. - It follows identical async/promise semantics to `beforeModel` - but is provided the route's resolved model in addition to - the `transition`, and is therefore suited to performing - logic that can only take place after the model has already - resolved. + _notifyBecameVisible: function() { + this.trigger('becameVisible'); - ```js - App.PostsRoute = Ember.Route.extend({ - afterModel: function(posts, transition) { - if (posts.get('length') === 1) { - this.transitionTo('post.show', posts.get('firstObject')); - } + this.forEachChildView(function(view) { + var isVisible = get(view, 'isVisible'); + + if (isVisible || isVisible === null) { + view._notifyBecameVisible(); + } + }); + }, + + _notifyBecameHidden: function() { + this.trigger('becameHidden'); + this.forEachChildView(function(view) { + var isVisible = get(view, 'isVisible'); + + if (isVisible || isVisible === null) { + view._notifyBecameHidden(); } }); - ``` + }, - Refer to documentation for `beforeModel` for a description - of transition-pausing semantics when a promise is returned - from this hook. + _isAncestorHidden: function() { + var parent = get(this, 'parentView'); - @method afterModel - @param {Object} resolvedModel the value returned from `model`, - or its resolved value if it was a promise - @param {Transition} transition - @param {Object} queryParams the active query params for this handler - @return {Promise} if the value returned from this hook is - a promise, the transition will pause until the transition - resolves. Otherwise, non-promise return values are not - utilized in any way. - */ - afterModel: Ember.K, + while (parent) { + if (get(parent, 'isVisible') === false) { return true; } - /** - A hook you can implement to optionally redirect to another route. + parent = get(parent, 'parentView'); + } - If you call `this.transitionTo` from inside of this hook, this route - will not be entered in favor of the other hook. + return false; + }, + transitionTo: function(state, children) { + Ember.deprecate("Ember.View#transitionTo has been deprecated, it is for internal use only"); + this._transitionTo(state, children); + }, + _transitionTo: function(state, children) { + var priorState = this.currentState; + var currentState = this.currentState = this._states[state]; + this._state = state; - `redirect` and `afterModel` behave very similarly and are - called almost at the same time, but they have an important - distinction in the case that, from one of these hooks, a - redirect into a child route of this route occurs: redirects - from `afterModel` essentially invalidate the current attempt - to enter this route, and will result in this route's `beforeModel`, - `model`, and `afterModel` hooks being fired again within - the new, redirecting transition. Redirects that occur within - the `redirect` hook, on the other hand, will _not_ cause - these hooks to be fired again the second time around; in - other words, by the time the `redirect` hook has been called, - both the resolved model and attempted entry into this route - are considered to be fully validated. + if (priorState && priorState.exit) { priorState.exit(this); } + if (currentState.enter) { currentState.enter(this); } + }, - @method redirect - @param {Object} model the model for this route - */ - redirect: Ember.K, + // ....................................................... + // EVENT HANDLING + // /** - Called when the context is changed by router.js. + Handle events from `Ember.EventDispatcher` + @method handleEvent + @param eventName {String} + @param evt {Event} @private - @method contextDidChange */ - contextDidChange: function() { - this.currentModel = this.context; + handleEvent: function(eventName, evt) { + return this.currentState.handleEvent(this, eventName, evt); }, - /** - A hook you can implement to convert the URL into the model for - this route. + registerObserver: function(root, path, target, observer) { + if (!observer && 'function' === typeof target) { + observer = target; + target = null; + } - ```js - App.Router.map(function() { - this.resource('post', {path: '/posts/:post_id'}); - }); - ``` + if (!root || typeof root !== 'object') { + return; + } - The model for the `post` route is `App.Post.find(params.post_id)`. + var scheduledObserver = this._wrapAsScheduled(observer); - By default, if your route has a dynamic segment ending in `_id`: + addObserver(root, path, target, scheduledObserver); - * The model class is determined from the segment (`post_id`'s - class is `App.Post`) - * The find method is called on the model class with the value of - the dynamic segment. + this.one('willClearRender', function() { + removeObserver(root, path, target, scheduledObserver); + }); + }, - Note that for routes with dynamic segments, this hook is only - executed when entered via the URL. If the route is entered - through a transition (e.g. when using the `link-to` Handlebars - helper), then a model context is already provided and this hook - is not called. Routes without dynamic segments will always - execute the model hook. + _wrapAsScheduled: function(fn) { + var view = this; + var stateCheckedFn = function() { + view.currentState.invokeObserver(this, fn); + }; + var scheduledFn = function() { + run.scheduleOnce('render', this, stateCheckedFn); + }; + return scheduledFn; + }, - This hook follows the asynchronous/promise semantics - described in the documentation for `beforeModel`. In particular, - if a promise returned from `model` fails, the error will be - handled by the `error` hook on `Ember.Route`. + getStream: function(path) { + var stream = this._getContextStream().get(path); - Example + stream._label = path; - ```js - App.PostRoute = Ember.Route.extend({ - model: function(params) { - return App.Post.find(params.post_id); - } - }); - ``` + return stream; + }, - @method model - @param {Object} params the parameters extracted from the URL - @param {Transition} transition - @param {Object} queryParams the query params for this route - @return {Object|Promise} the model for this route. If - a promise is returned, the transition will pause until - the promise resolves, and the resolved value of the promise - will be used as the model for this route. - */ - model: function(params, transition) { - var match, name, sawParams, value; + _getBindingForStream: function(pathOrStream) { + if (this._streamBindings === undefined) { + this._streamBindings = create(null); + this.one('willDestroyElement', this, this._destroyStreamBindings); + } - for (var prop in params) { - if (prop === 'queryParams') { continue; } + var path = pathOrStream; + if (isStream(pathOrStream)) { + path = pathOrStream._label; - if (match = prop.match(/^(.*)_id$/)) { - name = match[1]; - value = params[prop]; + if (!path) { + // if no _label is present on the provided stream + // it is likely a subexpr and cannot be set (so it + // does not need a StreamBinding) + return pathOrStream; } - sawParams = true; } - if (!name && sawParams) { return copy(params); } - else if (!name) { - if (transition.resolveIndex !== transition.state.handlerInfos.length-1) { return; } + if (this._streamBindings[path] !== undefined) { + return this._streamBindings[path]; + } else { + var stream = this._getContextStream().get(path); + var streamBinding = new StreamBinding(stream); - var parentModel = transition.state.handlerInfos[transition.resolveIndex-1].context; + streamBinding._label = path; - return parentModel; + return this._streamBindings[path] = streamBinding; } + }, - return this.findModel(name, value); + _destroyStreamBindings: function() { + var streamBindings = this._streamBindings; + for (var path in streamBindings) { + streamBindings[path].destroy(); + } + this._streamBindings = undefined; }, - /** - @private - @method deserialize - @param {Object} params the parameters extracted from the URL - @param {Transition} transition - @return {Object|Promise} the model for this route. + _getContextStream: function() { + if (this._contextStream === undefined) { + this._baseContext = new KeyStream(this, 'context'); + this._contextStream = new ContextStream(this); + this.one('willDestroyElement', this, this._destroyContextStream); + } - Router.js hook. - */ - deserialize: function(params, transition) { - - return this.model(params, transition); - + return this._contextStream; }, - /** - - @method findModel - @param {String} type the model type - @param {Object} value the value passed to find - */ - findModel: function(){ - var store = get(this, 'store'); - return store.find.apply(store, arguments); + _destroyContextStream: function() { + this._baseContext.destroy(); + this._baseContext = undefined; + this._contextStream.destroy(); + this._contextStream = undefined; }, - /** - Store property provides a hook for data persistence libraries to inject themselves. + _unsubscribeFromStreamBindings: function() { + for (var key in this._streamBindingSubscriptions) { + var streamBinding = this[key + 'Binding']; + var callback = this._streamBindingSubscriptions[key]; + streamBinding.unsubscribe(callback); + } + } + }); - By default, this store property provides the exact same functionality previously - in the model hook. + deprecateProperty(View.prototype, 'state', '_state'); + deprecateProperty(View.prototype, 'states', '_states'); - Currently, the required interface is: + /* + Describe how the specified actions should behave in the various + states that a view can exist in. Possible states: - `store.find(modelName, findArguments)` + * 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. + */ - @method store - @param {Object} store - */ - store: computed(function(){ - var container = this.container; - var routeName = this.routeName; - var namespace = get(this, 'router.namespace'); + // in the destroyed state, everything is illegal - return { - find: function(name, value) { - var modelClass = container.lookupFactory('model:' + name); + // before rendering has begun, all legal manipulations are noops. - Ember.assert("You used the dynamic segment " + name + "_id in your route " + - routeName + ", but " + namespace + "." + classify(name) + - " did not exist and you did not override your route's `model` " + - "hook.", modelClass); + // inside the buffer, legal manipulations are done on the buffer - if (!modelClass) { return; } + // once the view has been inserted into the DOM, legal manipulations + // are done on the DOM element. - Ember.assert(classify(name) + ' has no method `find`.', typeof modelClass.find === 'function'); + var mutation = EmberObject.extend(Evented).create(); + // TODO MOVE TO RENDERER HOOKS + View.addMutationListener = function(callback) { + mutation.on('change', callback); + }; - return modelClass.find(value); - } - }; - }), + View.removeMutationListener = function(callback) { + mutation.off('change', callback); + }; - /** - A hook you can implement to convert the route's model into parameters - for the URL. + View.notifyMutationListeners = function() { + mutation.trigger('change'); + }; - ```js - App.Router.map(function() { - this.resource('post', {path: '/posts/:post_id'}); - }); + /** + Global views hash - App.PostRoute = Ember.Route.extend({ - model: function(params) { - // the server returns `{ id: 12 }` - return jQuery.getJSON("/posts/" + params.post_id); - }, + @property views + @static + @type Hash + */ + View.views = {}; - serialize: function(model) { - // this will make the URL `/posts/12` - return { post_id: model.id }; + // 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; + + // Used by Handlebars helpers, view element attributes + View.applyAttributeBindings = function(elem, name, initialValue) { + var value = sanitizeAttributeValue(elem[0], name, initialValue); + 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); + // In IE8 `prop` couldn't remove attribute when name is `required`. + if (name === 'required') { + elem.removeProp(name); + } else { + elem.prop(name, ''); } - }); - ``` + } else if (value !== elem.prop(name)) { + // value should always be properties + elem.prop(name, value); + } + } else if (!value) { + elem.removeAttr(name); + } + }; - The default `serialize` method will insert the model's `id` into the - route's dynamic segment (in this case, `:post_id`) if the segment contains '_id'. - If the route has multiple dynamic segments or does not contain '_id', `serialize` - will return `Ember.getProperties(model, params)` + __exports__["default"] = View; + }); +enifed("ember-views/views/with_view", + ["ember-metal/property_set","ember-metal/utils","ember-views/views/bound_view","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; - This method is called when `transitionTo` is called with a context - in order to populate the URL. + /** + @module ember + @submodule ember-views + */ - @method serialize - @param {Object} model the route's model - @param {Array} params an Array of parameter names for the current - route (in the example, `['post_id']`. - @return {Object} the serialized parameters - */ - serialize: function(model, params) { - if (params.length < 1) { return; } - if (!model) { return; } + var set = __dependency1__.set; + var apply = __dependency2__.apply; + var BoundView = __dependency3__["default"]; - var name = params[0], object = {}; + __exports__["default"] = BoundView.extend({ + init: function() { + apply(this, this._super, arguments); - if (/_id$/.test(name) && params.length === 1) { - object[name] = get(model, "id"); + var controllerName = this.templateHash.controller; + + if (controllerName) { + var previousContext = this.previousContext; + var controller = this.container.lookupFactory('controller:'+controllerName).create({ + parentController: previousContext, + target: previousContext + }); + + this._generatedController = controller; + + if (this.preserveContext) { + this._blockArguments = [ controller ]; + this.lazyValue.subscribe(function(modelStream) { + set(controller, 'model', modelStream.value()); + }); + } else { + set(this, 'controller', controller); + this.valueNormalizerFunc = function(result) { + controller.set('model', result); + return controller; + }; + } + + set(controller, 'model', this.lazyValue.value()); } else { - object = getProperties(model, params); + if (this.preserveContext) { + this._blockArguments = [ this.lazyValue ]; + } } - - return object; }, - /** - A hook you can use to setup the controller for the current route. + willDestroy: function() { + this._super(); - This method is called with the controller for the current route and the - model supplied by the `model` hook. + if (this._generatedController) { + this._generatedController.destroy(); + } + } + }); + }); +enifed("ember", + ["ember-metal","ember-runtime","ember-views","ember-routing","ember-application","ember-extension-support","ember-htmlbars","ember-routing-htmlbars"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { + "use strict"; + /* global navigator */ + // require the main entry points for each of these packages + // this is so that the global exports occur properly - By default, the `setupController` hook sets the `content` property of - the controller to the `model`. + // do this to ensure that Ember.Test is defined properly on the global + // if it is present. + if (Ember.__loader.registry['ember-testing']) { + requireModule('ember-testing'); + } - 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`: + /** + Ember - ```js - App.PhotosRoute = Ember.Route.extend({ - model: function() { - return App.Photo.find(); - }, + @module ember + */ - setupController: function (controller, model) { - // Call _super for default behavior - this._super(controller, model); - // Implement your custom setup after - this.controllerFor('application').set('showingPhotos', true); - } - }); - ``` + Ember.deprecate('Usage of Ember is deprecated for Internet Explorer 6 and 7, support will be removed in the next major version.', !navigator.userAgent.match(/MSIE [67]/)); + }); +enifed("htmlbars-compiler", + ["./htmlbars-compiler/compiler","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var compile = __dependency1__.compile; + var compilerSpec = __dependency1__.compilerSpec; - 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. + __exports__.compile = compile; + __exports__.compilerSpec = compilerSpec; + }); +enifed("htmlbars-compiler/compiler", + ["../htmlbars-syntax/parser","./template-compiler","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + /*jshint evil:true*/ + var preprocess = __dependency1__.preprocess; + var TemplateCompiler = __dependency2__["default"]; - The provided controller will be one resolved based on the name - of this route. + /* + * Compile a string into a template rendering function + * + * Example usage: + * + * // Template is the hydration portion of the compiled template + * var template = compile("Howdy {{name}}"); + * + * // Template accepts three arguments: + * // + * // 1. A context object + * // 2. An env object + * // 3. A contextualElement (optional, document.body is the default) + * // + * // The env object *must* have at least these two properties: + * // + * // 1. `hooks` - Basic hooks for rendering a template + * // 2. `dom` - An instance of DOMHelper + * // + * import {hooks} from 'htmlbars-runtime'; + * import {DOMHelper} from 'morph'; + * var context = {name: 'whatever'}, + * env = {hooks: hooks, dom: new DOMHelper()}, + * contextualElement = document.body; + * var domFragment = template(context, env, contextualElement); + * + * @method compile + * @param {String} string An htmlbars template string + * @param {Object} options A set of options to provide to the compiler + * @return {Function} A function for rendering the template + */ + function compile(string, options) { + var program = compileSpec(string, options); + return new Function("return " + program)(); + } - If no explicit controller is defined, Ember will automatically create - an appropriate controller for the model. + __exports__.compile = compile;/* + * Compile a string into a template spec string. The template spec is a string + * representation of a template. Usually, you would use compileSpec for + * pre-compilation of a template on the server. + * + * Example usage: + * + * var templateSpec = compileSpec("Howdy {{name}}"); + * // This next step is basically what plain compile does + * var template = new Function("return " + templateSpec)(); + * + * @method compileSpec + * @param {String} string An htmlbars template string + * @return {Function} A template spec string + */ + function compileSpec(string, options) { + var ast = preprocess(string, options); + var compiler = new TemplateCompiler(options); + var program = compiler.compile(ast); + return program; + } - * 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`. + __exports__.compileSpec = compileSpec; + }); +enifed("htmlbars-compiler/fragment-javascript-compiler", + ["./utils","../htmlbars-util/quoting","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var processOpcodes = __dependency1__.processOpcodes; + var string = __dependency2__.string; - As an example, consider the router: + var svgNamespace = "http://www.w3.org/2000/svg", + // http://www.w3.org/html/wg/drafts/html/master/syntax.html#html-integration-point + svgHTMLIntegrationPoints = {'foreignObject':true, 'desc':true, 'title':true}; - ```js - App.Router.map(function() { - this.resource('post', {path: '/posts/:post_id'}); - }); - ``` - 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. + function FragmentJavaScriptCompiler() { + this.source = []; + this.depth = -1; + } - Example + __exports__["default"] = FragmentJavaScriptCompiler; - ```js - App.PostRoute = Ember.Route.extend({ - setupController: function(controller, model) { - controller.set('model', model); - } - }); - ``` + FragmentJavaScriptCompiler.prototype.compile = function(opcodes, options) { + this.source.length = 0; + this.depth = -1; + this.indent = (options && options.indent) || ""; + this.namespaceFrameStack = [{namespace: null, depth: null}]; + this.domNamespace = null; - @method setupController - @param {Controller} controller instance - @param {Object} model - */ - setupController: function(controller, context, transition) { - if (controller && (context !== undefined)) { - set(controller, 'model', context); - } - }, + this.source.push('function build(dom) {\n'); + processOpcodes(this, opcodes); + this.source.push(this.indent+'}'); - /** - Returns the controller for a particular route or name. + return this.source.join(''); + }; - The controller instance must already have been created, either through entering the - associated route or using `generateController`. + FragmentJavaScriptCompiler.prototype.createFragment = function() { + var el = 'el'+(++this.depth); + this.source.push(this.indent+' var '+el+' = dom.createDocumentFragment();\n'); + }; - ```js - App.PostRoute = Ember.Route.extend({ - setupController: function(controller, post) { - this._super(controller, post); - this.controllerFor('posts').set('currentPost', post); - } - }); - ``` + FragmentJavaScriptCompiler.prototype.createElement = function(tagName) { + var el = 'el'+(++this.depth); + if (tagName === 'svg') { + this.pushNamespaceFrame({namespace: svgNamespace, depth: this.depth}); + } + this.ensureNamespace(); + this.source.push(this.indent+' var '+el+' = dom.createElement('+string(tagName)+');\n'); + if (svgHTMLIntegrationPoints[tagName]) { + this.pushNamespaceFrame({namespace: null, depth: this.depth}); + } + }; - @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; + FragmentJavaScriptCompiler.prototype.createText = function(str) { + var el = 'el'+(++this.depth); + this.source.push(this.indent+' var '+el+' = dom.createTextNode('+string(str)+');\n'); + }; - if (route && route.controllerName) { - name = route.controllerName; - } + FragmentJavaScriptCompiler.prototype.createComment = function(str) { + var el = 'el'+(++this.depth); + this.source.push(this.indent+' var '+el+' = dom.createComment('+string(str)+');\n'); + }; - controller = container.lookup('controller:' + name); + FragmentJavaScriptCompiler.prototype.returnNode = function() { + var el = 'el'+this.depth; + this.source.push(this.indent+' return '+el+';\n'); + }; - // 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); + FragmentJavaScriptCompiler.prototype.setAttribute = function(name, value) { + var el = 'el'+this.depth; + this.source.push(this.indent+' dom.setProperty('+el+','+string(name)+','+string(value)+');\n'); + }; - return controller; - }, + FragmentJavaScriptCompiler.prototype.appendChild = function() { + if (this.depth === this.getCurrentNamespaceFrame().depth) { + this.popNamespaceFrame(); + } + var child = 'el'+(this.depth--); + var el = 'el'+this.depth; + this.source.push(this.indent+' dom.appendChild('+el+', '+child+');\n'); + }; - /** - Generates a controller for a route. + FragmentJavaScriptCompiler.prototype.getCurrentNamespaceFrame = function() { + return this.namespaceFrameStack[this.namespaceFrameStack.length-1]; + }; - If the optional model is passed then the controller type is determined automatically, - e.g., an ArrayController for arrays. + FragmentJavaScriptCompiler.prototype.pushNamespaceFrame = function(frame) { + this.namespaceFrameStack.push(frame); + }; - Example + FragmentJavaScriptCompiler.prototype.popNamespaceFrame = function() { + return this.namespaceFrameStack.pop(); + }; - ```js - App.PostRoute = Ember.Route.extend({ - setupController: function(controller, post) { - this._super(controller, post); - this.generateController('posts', post); - } - }); - ``` + FragmentJavaScriptCompiler.prototype.ensureNamespace = function() { + var correctNamespace = this.getCurrentNamespaceFrame().namespace; + if (this.domNamespace !== correctNamespace) { + this.source.push(this.indent+' dom.setNamespace('+(correctNamespace ? string(correctNamespace) : 'null')+');\n'); + this.domNamespace = correctNamespace; + } + }; + }); +enifed("htmlbars-compiler/fragment-opcode-compiler", + ["./template-visitor","./utils","../htmlbars-util/array-utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var TemplateVisitor = __dependency1__["default"]; + var processOpcodes = __dependency2__.processOpcodes; + var forEach = __dependency3__.forEach; - @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; + function FragmentOpcodeCompiler() { + this.opcodes = []; + } + + __exports__["default"] = FragmentOpcodeCompiler; + + FragmentOpcodeCompiler.prototype.compile = function(ast) { + var templateVisitor = new TemplateVisitor(); + templateVisitor.visit(ast); + + processOpcodes(this, templateVisitor.actions); + + return this.opcodes; + }; + + FragmentOpcodeCompiler.prototype.opcode = function(type, params) { + this.opcodes.push([type, params]); + }; + + FragmentOpcodeCompiler.prototype.text = function(text, childIndex, childCount, isSingleRoot) { + this.opcode('createText', [text.chars]); + if (!isSingleRoot) { this.opcode('appendChild'); } + }; + + FragmentOpcodeCompiler.prototype.comment = function(comment, childIndex, childCount, isSingleRoot) { + this.opcode('createComment', [comment.value]); + if (!isSingleRoot) { this.opcode('appendChild'); } + }; + + FragmentOpcodeCompiler.prototype.openElement = function(element) { + this.opcode('createElement', [element.tag]); + forEach(element.attributes, this.attribute, this); + }; + + FragmentOpcodeCompiler.prototype.closeElement = function(element, childIndex, childCount, isSingleRoot) { + if (!isSingleRoot) { this.opcode('appendChild'); } + }; + + FragmentOpcodeCompiler.prototype.startProgram = function(program) { + this.opcodes.length = 0; + if (program.body.length !== 1) { + this.opcode('createFragment'); + } + }; + + FragmentOpcodeCompiler.prototype.endProgram = function(/* program */) { + this.opcode('returnNode'); + }; + + FragmentOpcodeCompiler.prototype.mustache = function () {}; + + FragmentOpcodeCompiler.prototype.component = function () {}; + + FragmentOpcodeCompiler.prototype.block = function () {}; + + FragmentOpcodeCompiler.prototype.attribute = function(attr) { + if (attr.value.type === 'TextNode') { + this.opcode('setAttribute', [attr.name, attr.value.chars]); + } + }; + + FragmentOpcodeCompiler.prototype.setNamespace = function(namespace) { + this.opcode('setNamespace', [namespace]); + }; + }); +enifed("htmlbars-compiler/hydration-javascript-compiler", + ["./utils","../htmlbars-util/quoting","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var processOpcodes = __dependency1__.processOpcodes; + var string = __dependency2__.string; + var array = __dependency2__.array; + + function HydrationJavaScriptCompiler() { + this.stack = []; + this.source = []; + this.mustaches = []; + this.parents = [['fragment']]; + this.parentCount = 0; + this.morphs = []; + this.fragmentProcessing = []; + this.hooks = undefined; + } + + __exports__["default"] = HydrationJavaScriptCompiler; + + var prototype = HydrationJavaScriptCompiler.prototype; + + prototype.compile = function(opcodes, options) { + this.stack.length = 0; + this.mustaches.length = 0; + this.source.length = 0; + this.parents.length = 1; + this.parents[0] = ['fragment']; + this.morphs.length = 0; + this.fragmentProcessing.length = 0; + this.parentCount = 0; + this.indent = (options && options.indent) || ""; + this.hooks = {}; + + processOpcodes(this, opcodes); + + var i, l; + if (this.morphs.length) { + var morphs = ""; + for (i = 0, l = this.morphs.length; i < l; ++i) { + var morph = this.morphs[i]; + morphs += this.indent+' var '+morph[0]+' = '+morph[1]+';\n'; + } + this.source.unshift(morphs); + } + + if (this.fragmentProcessing.length) { + var processing = ""; + for (i = 0, l = this.fragmentProcessing.length; i < l; ++i) { + processing += this.indent+' '+this.fragmentProcessing[i]+'\n'; + } + this.source.unshift(processing); + } - model = model || this.modelFor(name); + return this.source.join(''); + }; - return generateController(container, name, model); - }, + prototype.prepareArray = function(length) { + var values = []; - /** - 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. + for (var i = 0; i < length; i++) { + values.push(this.stack.pop()); + } - Example + this.stack.push('[' + values.join(', ') + ']'); + }; - ```js - App.Router.map(function() { - this.resource('post', { path: '/post/:post_id' }, function() { - this.resource('comments'); - }); - }); + prototype.prepareObject = function(size) { + var pairs = []; - App.CommentsRoute = Ember.Route.extend({ - afterModel: function() { - this.set('post', this.modelFor('post')); - } - }); - ``` + for (var i = 0; i < size; i++) { + pairs.push(this.stack.pop() + ': ' + this.stack.pop()); + } - @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; + this.stack.push('{' + pairs.join(', ') + '}'); + }; - // 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]; - } - } + prototype.pushRaw = function(value) { + this.stack.push(value); + }; - return route && route.currentModel; - }, + prototype.pushLiteral = function(value) { + if (typeof value === 'string') { + this.stack.push(string(value)); + } else { + this.stack.push(value.toString()); + } + }; + + prototype.pushHook = function(name, args) { + this.hooks[name] = true; + this.stack.push(name + '(' + args.join(', ') + ')'); + }; + + prototype.pushGetHook = function(path) { + this.pushHook('get', [ + 'env', + 'context', + string(path) + ]); + }; + + prototype.pushSexprHook = function() { + this.pushHook('subexpr', [ + 'env', + 'context', + this.stack.pop(), // path + this.stack.pop(), // params + this.stack.pop() // hash + ]); + }; - /** - A hook you can use to render the template for the current route. + prototype.pushConcatHook = function() { + this.pushHook('concat', [ + 'env', + this.stack.pop() // parts + ]); + }; - 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. + prototype.printHook = function(name, args) { + this.hooks[name] = true; + this.source.push(this.indent + ' ' + name + '(' + args.join(', ') + ');\n'); + }; + + prototype.printSetHook = function(name, index) { + this.printHook('set', [ + 'env', + 'context', + string(name), + 'blockArguments[' + index + ']' + ]); + }; + + prototype.printBlockHook = function(morphNum, templateId, inverseId) { + this.printHook('block', [ + 'env', + 'morph' + morphNum, + 'context', + this.stack.pop(), // path + this.stack.pop(), // params + this.stack.pop(), // hash + templateId === null ? 'null' : 'child' + templateId, + inverseId === null ? 'null' : 'child' + inverseId + ]); + }; - This method can be overridden to set up and render additional or - alternative templates. + prototype.printInlineHook = function(morphNum) { + this.printHook('inline', [ + 'env', + 'morph' + morphNum, + 'context', + this.stack.pop(), // path + this.stack.pop(), // params + this.stack.pop() // hash + ]); + }; - ```js - App.PostsRoute = Ember.Route.extend({ - renderTemplate: function(controller, model) { - var favController = this.controllerFor('favoritePost'); + prototype.printContentHook = function(morphNum) { + this.printHook('content', [ + 'env', + 'morph' + morphNum, + 'context', + this.stack.pop() // path + ]); + }; - // Render the `favoritePost` template into - // the outlet `posts`, and display the `favoritePost` - // controller. - this.render('favoritePost', { - outlet: 'posts', - controller: favController - }); - } - }); - ``` + prototype.printComponentHook = function(morphNum, templateId) { + this.printHook('component', [ + 'env', + 'morph' + morphNum, + 'context', + this.stack.pop(), // path + this.stack.pop(), // attrs + templateId === null ? 'null' : 'child' + templateId + ]); + }; - @method renderTemplate - @param {Object} controller the route's controller - @param {Object} model the route's model - */ - renderTemplate: function(controller, model) { - this.render(); - }, + prototype.printAttributeHook = function(attrMorphNum, elementNum) { + this.printHook('attribute', [ + 'env', + 'attrMorph' + attrMorphNum, + 'element' + elementNum, + this.stack.pop(), // name + this.stack.pop() // value + ]); + }; - /** - Renders a template into an outlet. + prototype.printElementHook = function(elementNum) { + this.printHook('element', [ + 'env', + 'element' + elementNum, + 'context', + this.stack.pop(), // path + this.stack.pop(), // params + this.stack.pop() // hash + ]); + }; - This method has a number of defaults, based on the name of the - route specified in the router. + prototype.createMorph = function(morphNum, parentPath, startIndex, endIndex, escaped) { + var isRoot = parentPath.length === 0; + var parent = this.getParent(); - For example: + var morphMethod = escaped ? 'createMorphAt' : 'createUnsafeMorphAt'; + var morph = "dom."+morphMethod+"("+parent+ + ","+(startIndex === null ? "-1" : startIndex)+ + ","+(endIndex === null ? "-1" : endIndex)+ + (isRoot ? ",contextualElement)" : ")"); - ```js - App.Router.map(function() { - this.route('index'); - this.resource('post', {path: '/posts/:post_id'}); - }); + this.morphs.push(['morph' + morphNum, morph]); + }; - App.PostRoute = App.Route.extend({ - renderTemplate: function() { - this.render(); - } - }); - ``` + prototype.createAttrMorph = function(attrMorphNum, elementNum, name, escaped) { + var morphMethod = escaped ? 'createAttrMorph' : 'createUnsafeAttrMorph'; + var morph = "dom."+morphMethod+"(element"+elementNum+", '"+name+"')"; + this.morphs.push(['attrMorph' + attrMorphNum, morph]); + }; - The name of the `PostRoute`, as defined by the router, is `post`. + prototype.repairClonedNode = function(blankChildTextNodes, isElementChecked) { + var parent = this.getParent(), + processing = 'dom.repairClonedNode('+parent+','+ + array(blankChildTextNodes)+ + ( isElementChecked ? ',true' : '' )+ + ');'; + this.fragmentProcessing.push( + processing + ); + }; - By default, render will: + prototype.shareElement = function(elementNum){ + var elementNodesName = "element" + elementNum; + this.fragmentProcessing.push('var '+elementNodesName+' = '+this.getParent()+';'); + this.parents[this.parents.length-1] = [elementNodesName]; + }; - * 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 + prototype.consumeParent = function(i) { + var newParent = this.lastParent().slice(); + newParent.push(i); - You can override this behavior: + this.parents.push(newParent); + }; - ```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 - }); - } - }); - ``` + prototype.popParent = function() { + this.parents.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)`. + prototype.getParent = function() { + var last = this.lastParent().slice(); + var frag = last.shift(); - @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); + if (!last.length) { + return frag; + } - var namePassed = typeof name === 'string' && !!name; + return 'dom.childAt(' + frag + ', [' + last.join(', ') + '])'; + }; - if (typeof name === 'object' && !options) { - options = name; - name = this.routeName; + prototype.lastParent = function() { + return this.parents[this.parents.length-1]; + }; + }); +enifed("htmlbars-compiler/hydration-opcode-compiler", + ["./template-visitor","./utils","../htmlbars-util/array-utils","../htmlbars-syntax/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + var TemplateVisitor = __dependency1__["default"]; + var processOpcodes = __dependency2__.processOpcodes; + var forEach = __dependency3__.forEach; + var isHelper = __dependency4__.isHelper; + + function unwrapMustache(mustache) { + if (isHelper(mustache.sexpr)) { + return mustache.sexpr; + } else { + return mustache.sexpr.path; + } + } + + function detectIsElementChecked(element){ + for (var i=0, len=element.attributes.length;i 0) { + this.opcode('repairClonedNode', blankChildTextNodes); + } + }; - You may pass any or all of the following options to `disconnectOutlet`: + HydrationOpcodeCompiler.prototype.endProgram = function(/* program */) { + distributeMorphs(this.morphs, this.opcodes); + }; - * `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) + HydrationOpcodeCompiler.prototype.text = function(/* string, pos, len */) { + ++this.currentDOMChildIndex; + }; - Example: + HydrationOpcodeCompiler.prototype.comment = function(/* string, pos, len */) { + ++this.currentDOMChildIndex; + }; - ```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' - }); - } - } - }); - ``` + HydrationOpcodeCompiler.prototype.openElement = function(element, pos, len, isSingleRoot, mustacheCount, blankChildTextNodes) { + distributeMorphs(this.morphs, this.opcodes); + ++this.currentDOMChildIndex; - Alternatively, you can pass the `outlet` name directly as a string. + this.element = this.currentDOMChildIndex; - Example: + if (!isSingleRoot) { + this.opcode('consumeParent', this.currentDOMChildIndex); - ```js - hideModal: function(evt) { - this.disconnectOutlet('modal'); + // 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 ); + } - @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'; + this.paths.push(this.currentDOMChildIndex); + this.currentDOMChildIndex = -1; - var parentView = this.router._lookupActiveView(options.parentView); - if (parentView) { parentView.disconnectOutlet(options.outlet); } - }, + forEach(element.attributes, this.attribute, this); + forEach(element.helpers, this.elementHelper, this); + }; - willDestroy: function() { - this.teardownViews(); - }, + HydrationOpcodeCompiler.prototype.closeElement = function(element, pos, len, isSingleRoot) { + distributeMorphs(this.morphs, this.opcodes); + if (!isSingleRoot) { this.opcode('popParent'); } + this.currentDOMChildIndex = this.paths.pop(); + }; - /** - @private + HydrationOpcodeCompiler.prototype.block = function(block, childIndex, childrenLength) { + var sexpr = block.sexpr; - @method teardownViews - */ - teardownViews: function() { - // Tear down the top level view - if (this.teardownTopLevelView) { this.teardownTopLevelView(); } + var currentDOMChildIndex = this.currentDOMChildIndex; + var start = (currentDOMChildIndex < 0) ? null : currentDOMChildIndex; + var end = (childIndex === childrenLength - 1) ? null : currentDOMChildIndex + 1; - // Tear down any outlets rendered with 'into' - var teardownOutletViews = this.teardownOutletViews || []; - a_forEach(teardownOutletViews, function(teardownOutletView) { - teardownOutletView(); - }); + var morphNum = this.morphNum++; + this.morphs.push([morphNum, this.paths.slice(), start, end, true]); - delete this.teardownTopLevelView; - delete this.teardownOutletViews; - delete this.lastRenderedTemplate; - } - }); + var templateId = this.templateId++; + var inverseId = block.inverse === null ? null : this.templateId++; + prepareSexpr(this, sexpr); + this.opcode('printBlockHook', morphNum, templateId, inverseId); + }; - - function parentRoute(route) { - var handlerInfos = route.router.router.state.handlerInfos; + HydrationOpcodeCompiler.prototype.component = function(component, childIndex, childrenLength) { + var currentDOMChildIndex = this.currentDOMChildIndex; + var program = component.program || {}; + var blockParams = program.blockParams || []; - if (!handlerInfos) { return; } + var start = (currentDOMChildIndex < 0 ? null : currentDOMChildIndex), + end = (childIndex === childrenLength - 1 ? null : currentDOMChildIndex + 1); - var parent, current; + var morphNum = this.morphNum++; + this.morphs.push([morphNum, this.paths.slice(), start, end, true]); - for (var i=0, l=handlerInfos.length; i= 0; i--) { + var name = attrs[i].name; + var value = attrs[i].value; + + // 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'); + } + + this.opcode('pushLiteral', name); } - } - function parentTemplate(route) { - var parent = parentRoute(route), template; + this.opcode('prepareObject', attrs.length); + this.opcode('pushLiteral', component.tag); + this.opcode('printComponentHook', morphNum, this.templateId++, blockParams.length); + }; - if (!parent) { return; } + HydrationOpcodeCompiler.prototype.attribute = function(attr) { + var value = attr.value; + var escaped = true; - if (template = parent.lastRenderedTemplate) { - return template; - } else { - return parentTemplate(parent); + // 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'); } - } - 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; - } + this.opcode('pushLiteral', attr.name); - 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."); - } + if (this.element !== null) { + this.opcode('shareElement', ++this.elementNum); + this.element = null; } - if (model) { - controller.set('model', model); + var attrMorphNum = this.attrMorphNum++; + this.opcode('createAttrMorph', attrMorphNum, this.elementNum, attr.name, escaped); + this.opcode('printAttributeHook', attrMorphNum, this.elementNum); + }; + + HydrationOpcodeCompiler.prototype.elementHelper = function(sexpr) { + prepareSexpr(this, sexpr); + + // 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 } - options.controller = controller; + this.opcode('printElementHook', this.elementNum); + }; + + HydrationOpcodeCompiler.prototype.mustache = function(mustache, childIndex, childrenLength) { + var sexpr = mustache.sexpr; + var currentDOMChildIndex = this.currentDOMChildIndex; - return options; - } + var start = currentDOMChildIndex, + end = (childIndex === childrenLength - 1 ? -1 : currentDOMChildIndex + 1); - function setupView(view, container, options) { - if (view) { - if (options.LOG_VIEW_LOOKUPS) { - Ember.Logger.info("Rendering " + options.name + " with " + view, { fullName: 'view:' + options.name }); - } + var morphNum = this.morphNum++; + this.morphs.push([morphNum, this.paths.slice(), start, end, mustache.escaped]); + + if (isHelper(sexpr)) { + prepareSexpr(this, sexpr); + this.opcode('printInlineHook', morphNum); } 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 }); - } + preparePath(this, sexpr.path); + this.opcode('printContentHook', morphNum); } + }; + + HydrationOpcodeCompiler.prototype.SubExpression = function(sexpr) { + prepareSexpr(this, sexpr); + this.opcode('pushSexprHook'); + }; - if (!get(view, 'templateName')) { - set(view, 'template', options.template); + HydrationOpcodeCompiler.prototype.PathExpression = function(path) { + this.opcode('pushGetHook', path.original); + }; - set(view, '_debugTemplateName', options.name); - } + HydrationOpcodeCompiler.prototype.StringLiteral = function(node) { + this.opcode('pushLiteral', node.value); + }; - set(view, 'renderedName', options.name); - set(view, 'controller', options.controller); + HydrationOpcodeCompiler.prototype.BooleanLiteral = function(node) { + this.opcode('pushLiteral', node.value); + }; + + HydrationOpcodeCompiler.prototype.NumberLiteral = function(node) { + this.opcode('pushLiteral', node.value); + }; - return view; + function preparePath(compiler, path) { + compiler.opcode('pushLiteral', path.original); } - 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); + function prepareParams(compiler, params) { + for (var i = params.length - 1; i >= 0; i--) { + var param = params[i]; + compiler[param.type](param); } - } - function generateTopLevelTeardown(view) { - return function() { view.destroy(); }; + compiler.opcode('prepareArray', params.length); } - function generateOutletTeardown(parentView, outlet) { - return function() { parentView.disconnectOutlet(outlet); }; - } + function prepareHash(compiler, hash) { + var pairs = hash.pairs; - function toggleQueryParamObservers(route, controller, enable) { - var queryParams = get(controller, 'queryParams'), i, len, - method = enable ? 'addObserver' : 'removeObserver'; + for (var i = pairs.length - 1; i >= 0; i--) { + var key = pairs[i].key; + var value = pairs[i].value; - for (i = 0, len = queryParams.length; i < len; ++i) { - var prop = queryParams[i].split(':')[0]; - controller[method](prop, route, route._qpChanged); - controller[method](prop + '.[]', route, route._qpChanged); + compiler[value.type](value); + compiler.opcode('pushLiteral', key); } - } - - __exports__["default"] = Route; - }); -define("ember-routing/system/router", - ["ember-metal/core","ember-metal/error","ember-metal/property_get","ember-metal/property_set","ember-metal/array","ember-metal/properties","ember-metal/computed","ember-metal/merge","ember-metal/run_loop","ember-metal/enumerable_utils","ember-runtime/system/string","ember-runtime/system/object","ember-runtime/mixins/evented","ember-routing/system/dsl","ember-views/views/view","ember-routing/location/api","ember-handlebars/views/metamorph_view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // FEATURES, Logger, K, assert - var EmberError = __dependency2__["default"]; - var get = __dependency3__.get; - var set = __dependency4__.set; - var forEach = __dependency5__.forEach; - var defineProperty = __dependency6__.defineProperty; - var computed = __dependency7__.computed; - var merge = __dependency8__["default"]; - var run = __dependency9__["default"]; - var EnumerableUtils = __dependency10__["default"]; - var fmt = __dependency11__.fmt; - var EmberObject = __dependency12__["default"]; - var Evented = __dependency13__["default"]; - var EmberRouterDSL = __dependency14__["default"]; - var EmberView = __dependency15__.View; - var EmberLocation = __dependency16__["default"]; - var _MetamorphView = __dependency17__._MetamorphView; + compiler.opcode('prepareObject', pairs.length); + } - // requireModule("ember-handlebars"); - // requireModule("ember-runtime"); - // requireModule("ember-views"); + function prepareSexpr(compiler, sexpr) { + prepareHash(compiler, sexpr.hash); + prepareParams(compiler, sexpr.params); + preparePath(compiler, sexpr.path); + } - /** - @module ember - @submodule ember-routing - */ + function distributeMorphs(morphs, opcodes) { + if (morphs.length === 0) { + return; + } - // // side effect of loading some Ember globals, for now - // requireModule("ember-handlebars"); - // requireModule("ember-runtime"); - // requireModule("ember-views"); + // 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; + } + } - var Router = requireModule("router")['default']; - var Transition = requireModule("router/transition").Transition; + 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 = []; + } - var slice = [].slice; - var forEach = EnumerableUtils.forEach; + __exports__["default"] = TemplateCompiler; - var DefaultView = _MetamorphView; + TemplateCompiler.prototype.compile = function(ast) { + var templateVisitor = new TemplateVisitor(); + templateVisitor.visit(ast); - /** - The `Ember.Router` class manages the application state and URLs. Refer to - the [routing guide](http://emberjs.com/guides/routing/) for documentation. + processOpcodes(this, templateVisitor.actions); - @class Router - @namespace Ember - @extends Ember.Object - */ - var EmberRouter = EmberObject.extend(Evented, { - /** - The `location` property determines the type of URL's that your - application will use. + return this.templates.pop(); + }; - The following location types are currently available: + TemplateCompiler.prototype.startProgram = function(program, childTemplateCount, blankChildTextNodes) { + this.fragmentOpcodeCompiler.startProgram(program, childTemplateCount, blankChildTextNodes); + this.hydrationOpcodeCompiler.startProgram(program, childTemplateCount, blankChildTextNodes); - * `hash` - * `history` - * `none` + this.childTemplates.length = 0; + while(childTemplateCount--) { + this.childTemplates.push(this.templates.pop()); + } + }; - @property location - @default 'hash' - @see {Ember.Location} - */ - location: 'hash', + 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'; + } + } + return vars; + }; - /** - Represents the URL of the root of the application, often '/'. This prefix is - assumed on all routes defined on this router. + TemplateCompiler.prototype.getHydrationHooks = function(indent, hooks) { + var hookVars = []; + for (var hook in hooks) { + hookVars.push(hook + ' = hooks.' + hook); + } - @property rootURL - @default '/' - */ - rootURL: '/', + if (hookVars.length > 0) { + return indent + 'var hooks = env.hooks, ' + hookVars.join(', ') + ';\n'; + } else { + return ''; + } + }; - init: function() { - this.router = this.constructor.router || this.constructor.map(Ember.K); - this._activeViews = {}; - this._setupLocation(); - this._qpCache = {}; - this._queuedQPChanges = {}; + TemplateCompiler.prototype.endProgram = function(program, programDepth) { + this.fragmentOpcodeCompiler.endProgram(program); + this.hydrationOpcodeCompiler.endProgram(program); - if (get(this, 'namespace.LOG_TRANSITIONS_INTERNAL')) { - this.router.log = Ember.Logger.debug; - } - }, + var indent = repeat(" ", programDepth); + var options = { + indent: indent + " " + }; - /** - Represents the current URL. + // function build(dom) { return fragment; } + var fragmentProgram = this.fragmentCompiler.compile( + this.fragmentOpcodeCompiler.opcodes, + options + ); - @method url - @return {String} The current URL. - */ - url: computed(function() { - return get(this, 'location').getURL(); - }), + // function hydrate(fragment) { return mustaches; } + var hydrationProgram = this.hydrationCompiler.compile( + this.hydrationOpcodeCompiler.opcodes, + options + ); - /** - Initializes the current router instance and sets up the change handling - event listeners used by the instances `location` implementation. + var blockParams = program.blockParams || []; - A property named `initialURL` will be used to determine the initial URL. - If no value is found `/` will be used. + var templateSignature = 'context, env, contextualElement'; + if (blockParams.length > 0) { + templateSignature += ', blockArguments'; + } - @method startRouting - @private - */ - startRouting: function() { - this.router = this.router || this.constructor.map(Ember.K); + 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+'}())'; - var router = this.router, - location = get(this, 'location'), - container = this.container, - self = this, - initialURL = get(this, 'initialURL'); + this.templates.push(template); + }; - // Allow the Location class to cancel the router setup while it refreshes - // the page - if (get(location, 'cancelRouterSetup')) { - return; - } + 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); + }; - this._setupRouter(router, location); + TemplateCompiler.prototype.closeElement = function(element, i, l, r) { + this.fragmentOpcodeCompiler.closeElement(element, i, l, r); + this.hydrationOpcodeCompiler.closeElement(element, i, l, r); + }; - container.register('view:default', DefaultView); - container.register('view:toplevel', EmberView.extend()); + TemplateCompiler.prototype.component = function(component, i, l) { + this.fragmentOpcodeCompiler.component(component, i, l); + this.hydrationOpcodeCompiler.component(component, i, l); + }; - location.onUpdateURL(function(url) { - self.handleURL(url); - }); + TemplateCompiler.prototype.block = function(block, i, l) { + this.fragmentOpcodeCompiler.block(block, i, l); + this.hydrationOpcodeCompiler.block(block, i, l); + }; - if (typeof initialURL === "undefined") { - initialURL = location.getURL(); - } + TemplateCompiler.prototype.text = function(string, i, l, r) { + this.fragmentOpcodeCompiler.text(string, i, l, r); + this.hydrationOpcodeCompiler.text(string, i, l, r); + }; - this.handleURL(initialURL); - }, + TemplateCompiler.prototype.comment = function(string, i, l, r) { + this.fragmentOpcodeCompiler.comment(string, i, l, r); + this.hydrationOpcodeCompiler.comment(string, i, l, r); + }; - /** - Handles updating the paths and notifying any listeners of the URL - change. + TemplateCompiler.prototype.mustache = function (mustache, i, l) { + this.fragmentOpcodeCompiler.mustache(mustache, i, l); + this.hydrationOpcodeCompiler.mustache(mustache, i, l); + }; - Triggers the router level `didTransition` hook. + TemplateCompiler.prototype.setNamespace = function(namespace) { + this.fragmentOpcodeCompiler.setNamespace(namespace); + }; + }); +enifed("htmlbars-compiler/template-visitor", + ["exports"], + function(__exports__) { + "use strict"; + var push = Array.prototype.push; - @method didTransition - @private - @since 1.2.0 - */ - didTransition: function(infos) { - updatePaths(this); + function Frame() { + this.parentNode = null; + this.children = null; + this.childIndex = null; + this.childCount = null; + this.childTemplateCount = 0; + this.mustacheCount = 0; + this.actions = []; + } - this._cancelLoadingEvent(); + /** + * 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 + */ - this.notifyPropertyChange('url'); + function TemplateVisitor() { + this.frameStack = []; + this.actions = []; + this.programDepth = -1; + } - // Put this in the runloop so url will be accurate. Seems - // less surprising than didTransition being out of sync. - run.once(this, this.trigger, 'didTransition'); + // Traversal methods - if (get(this, 'namespace').LOG_TRANSITIONS) { - Ember.Logger.log("Transitioned into '" + EmberRouter._routePath(infos) + "'"); - } - }, + TemplateVisitor.prototype.visit = function(node) { + this[node.type](node); + }; - handleURL: function(url) { - return this._doTransition('handleURL', [url]); - }, + TemplateVisitor.prototype.Program = function(program) { + this.programDepth++; - transitionTo: function() { - return this._doTransition('transitionTo', arguments); - }, + var parentFrame = this.getCurrentFrame(); + var programFrame = this.pushFrame(); - intermediateTransitionTo: function() { - this.router.intermediateTransitionTo.apply(this.router, arguments); + programFrame.parentNode = program; + programFrame.children = program.body; + programFrame.childCount = program.body.length; + programFrame.blankChildTextNodes = []; + programFrame.actions.push(['endProgram', [program, this.programDepth]]); - updatePaths(this); + for (var i = program.body.length - 1; i >= 0; i--) { + programFrame.childIndex = i; + this.visit(program.body[i]); + } - var infos = this.router.currentHandlerInfos; - if (get(this, 'namespace').LOG_TRANSITIONS) { - Ember.Logger.log("Intermediate-transitioned into '" + EmberRouter._routePath(infos) + "'"); - } - }, + programFrame.actions.push(['startProgram', [ + program, programFrame.childTemplateCount, + programFrame.blankChildTextNodes.reverse() + ]]); + this.popFrame(); - replaceWith: function() { - return this._doTransition('replaceWith', arguments); - }, + this.programDepth--; - generate: function() { - var url = this.router.generate.apply(this.router, arguments); - return this.location.formatURL(url); - }, + // Push the completed template into the global actions list + if (parentFrame) { parentFrame.childTemplateCount++; } + push.apply(this.actions, programFrame.actions.reverse()); + }; - /** - Determines if the supplied route is currently active. + TemplateVisitor.prototype.ElementNode = function(element) { + var parentFrame = this.getCurrentFrame(); + var elementFrame = this.pushFrame(); + var parentNode = parentFrame.parentNode; - @method isActive - @param routeName - @return {Boolean} - @private - */ - isActive: function(routeName) { - var router = this.router; - return router.isActive.apply(router, arguments); - }, + elementFrame.parentNode = element; + elementFrame.children = element.children; + elementFrame.childCount = element.children.length; + elementFrame.mustacheCount += element.helpers.length; + elementFrame.blankChildTextNodes = []; - send: function(name, context) { - this.router.trigger.apply(this.router, arguments); - }, + var actionArgs = [ + element, + parentFrame.childIndex, + parentFrame.childCount, + parentNode.type === 'Program' && parentFrame.childCount === 1 + ]; - /** - Does this router instance have the given route. + elementFrame.actions.push(['closeElement', actionArgs]); - @method hasRoute - @return {Boolean} - @private - */ - hasRoute: function(route) { - return this.router.hasRoute(route); - }, + for (var i = element.attributes.length - 1; i >= 0; i--) { + this.visit(element.attributes[i]); + } - /** - Resets the state of the router by clearing the current route - handlers and deactivating them. + for (i = element.children.length - 1; i >= 0; i--) { + elementFrame.childIndex = i; + this.visit(element.children[i]); + } - @private - @method reset - */ - reset: function() { - this.router.reset(); - }, + elementFrame.actions.push(['openElement', actionArgs.concat([ + elementFrame.mustacheCount, elementFrame.blankChildTextNodes.reverse() ])]); + this.popFrame(); - _lookupActiveView: function(templateName) { - var active = this._activeViews[templateName]; - return active && active[0]; - }, + // 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); + }; - _connectActiveView: function(templateName, view) { - var existing = this._activeViews[templateName]; + TemplateVisitor.prototype.AttrNode = function(attr) { + if (attr.value.type !== 'TextNode') { + this.getCurrentFrame().mustacheCount++; + } + }; - if (existing) { - existing[0].off('willDestroyElement', this, existing[1]); - } + 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)); + } + frame.actions.push(['text', [text, frame.childIndex, frame.childCount, isSingleRoot]]); + }; - function disconnectActiveView() { - delete this._activeViews[templateName]; - } + TemplateVisitor.prototype.BlockStatement = function(node) { + var frame = this.getCurrentFrame(); - this._activeViews[templateName] = [view, disconnectActiveView]; - view.one('willDestroyElement', this, disconnectActiveView); - }, + frame.mustacheCount++; + frame.actions.push(['block', [node, frame.childIndex, frame.childCount]]); - _setupLocation: function() { - var location = get(this, 'location'), - rootURL = get(this, 'rootURL'); + if (node.inverse) { this.visit(node.inverse); } + if (node.program) { this.visit(node.program); } + }; - if (rootURL && !this.container.has('-location-setting:root-url')) { - this.container.register('-location-setting:root-url', rootURL, { instantiate: false }); - } + TemplateVisitor.prototype.ComponentNode = function(node) { + var frame = this.getCurrentFrame(); - if ('string' === typeof location && this.container) { - var resolvedLocation = this.container.lookup('location:' + location); + frame.mustacheCount++; + frame.actions.push(['component', [node, frame.childIndex, frame.childCount]]); - if ('undefined' !== typeof resolvedLocation) { - location = set(this, 'location', resolvedLocation); - } else { - // Allow for deprecated registration of custom location API's - var options = {implementation: location}; + if (node.program) { this.visit(node.program); } + }; - location = set(this, 'location', EmberLocation.create(options)); - } - } - if (rootURL && typeof rootURL === 'string') { - location.rootURL = rootURL; - } + TemplateVisitor.prototype.PartialStatement = function(node) { + var frame = this.getCurrentFrame(); + frame.mustacheCount++; + frame.actions.push(['mustache', [node, frame.childIndex, frame.childCount]]); + }; - // ensure that initState is called AFTER the rootURL is set on - // the location instance - if (typeof location.initState === 'function') { location.initState(); } - }, + TemplateVisitor.prototype.CommentStatement = function(text) { + var frame = this.getCurrentFrame(); + var isSingleRoot = frame.parentNode.type === 'Program' && frame.childCount === 1; - _getHandlerFunction: function() { - var seen = {}, container = this.container, - DefaultRoute = container.lookupFactory('route:basic'), - self = this; + frame.actions.push(['comment', [text, frame.childIndex, frame.childCount, isSingleRoot]]); + }; - return function(name) { - var routeName = 'route:' + name, - handler = container.lookup(routeName); + TemplateVisitor.prototype.MustacheStatement = function(mustache) { + var frame = this.getCurrentFrame(); + frame.mustacheCount++; + frame.actions.push(['mustache', [mustache, frame.childIndex, frame.childCount]]); + }; - if (seen[name]) { return handler; } + // Frame helpers - seen[name] = true; + TemplateVisitor.prototype.getCurrentFrame = function() { + return this.frameStack[this.frameStack.length - 1]; + }; - if (!handler) { - container.register(routeName, DefaultRoute.extend()); - handler = container.lookup(routeName); + TemplateVisitor.prototype.pushFrame = function() { + var frame = new Frame(); + this.frameStack.push(frame); + return frame; + }; - if (get(self, 'namespace.LOG_ACTIVE_GENERATION')) { - Ember.Logger.info("generated -> " + routeName, { fullName: routeName }); - } - } + TemplateVisitor.prototype.popFrame = function() { + return this.frameStack.pop(); + }; - handler.routeName = name; - return handler; - }; - }, + __exports__["default"] = TemplateVisitor; - _setupRouter: function(router, location) { - var lastURL, emberRouter = this; - router.getHandler = this._getHandlerFunction(); + // 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; - var doUpdateURL = function() { - location.setURL(lastURL); - }; + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; - router.updateURL = function(path) { - lastURL = path; - run.once(doUpdateURL); - }; + if (node.type !== 'TextNode' && node.type !== 'ElementNode') { + continue; + } else { + index++; + } - if (location.replaceURL) { - var doReplaceURL = function() { - location.replaceURL(lastURL); - }; + if (node === domNode) { + return index; + } + } - router.replaceURL = function(path) { - lastURL = path; - run.once(doReplaceURL); - }; + return -1; + } + }); +enifed("htmlbars-compiler/utils", + ["exports"], + function(__exports__) { + "use strict"; + function processOpcodes(compiler, opcodes) { + for (var i=0, l=opcodes.length; i= 0; --i) { - var handlerInfo = handlerInfos[i], - route = handlerInfo.handler; + this.type = 'BlockStatement'; + this.sexpr = sexpr; + this.program = program; + this.inverse = inverse; - if (!originRouteFound) { - if (originRoute === route) { - originRouteFound = true; - } - continue; - } + this.openStrip = openStrip; + this.inverseStrip = inverseStrip; + this.closeStrip = closeStrip; + }, - if (callback(route, handlerInfos[i + 1].handler) !== true) { - return false; - } - } - return true; - } + PartialStatement: function(sexpr, strip, locInfo) { + this.loc = locInfo; + this.type = 'PartialStatement'; + this.sexpr = sexpr; + this.indent = ''; - // These get invoked when an action bubbles above ApplicationRoute - // and are not meant to be overridable. - var defaultActionHandlers = { + this.strip = strip; + }, - willResolveModel: function(transition, originRoute) { - originRoute.router._scheduleLoadingEvent(transition, originRoute); + ContentStatement: function(string, locInfo) { + this.loc = locInfo; + this.type = 'ContentStatement'; + this.original = this.value = string; }, - error: function(error, transition, originRoute) { - // Attempt to find an appropriate error substate to enter. - var router = originRoute.router; + CommentStatement: function(comment, strip, locInfo) { + this.loc = locInfo; + this.type = 'CommentStatement'; + this.value = comment; - var tryTopLevel = forEachRouteAbove(originRoute, transition, function(route, childRoute) { - var childErrorRouteName = findChildRouteName(route, childRoute, 'error'); - if (childErrorRouteName) { - router.intermediateTransitionTo(childErrorRouteName, error); - return; - } - return true; - }); + this.strip = strip; + }, - 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; - } + SubExpression: function(path, params, hash, locInfo) { + this.loc = locInfo; - logError(error, 'Error while processing route: ' + transition.targetName); + this.type = 'SubExpression'; + this.path = path; + this.params = params || []; + this.hash = hash; }, - loading: function(transition, originRoute) { - // Attempt to find an appropriate loading substate to enter. - var router = originRoute.router; + PathExpression: function(data, depth, parts, original, locInfo) { + this.loc = locInfo; + this.type = 'PathExpression'; - var tryTopLevel = forEachRouteAbove(originRoute, transition, function(route, childRoute) { - var childLoadingRouteName = findChildRouteName(route, childRoute, 'loading'); + this.data = data; + this.original = original; + this.parts = parts; + this.depth = depth; + }, - if (childLoadingRouteName) { - router.intermediateTransitionTo(childLoadingRouteName); - return; - } + StringLiteral: function(string, locInfo) { + this.loc = locInfo; + this.type = 'StringLiteral'; + this.original = + this.value = string; + }, - // Don't bubble above pivot route. - if (transition.pivotHandler !== route) { - return true; - } - }); + NumberLiteral: function(number, locInfo) { + this.loc = locInfo; + this.type = 'NumberLiteral'; + this.original = + this.value = Number(number); + }, - if (tryTopLevel) { - // Check for top-level loading state to enter. - if (routeHasBeenDefined(originRoute.router, 'application_loading')) { - router.intermediateTransitionTo('application_loading'); - return; - } - } + BooleanLiteral: function(bool, locInfo) { + this.loc = locInfo; + this.type = 'BooleanLiteral'; + this.original = + this.value = bool === 'true'; + }, + + Hash: function(pairs, locInfo) { + this.loc = locInfo; + this.type = 'Hash'; + this.pairs = pairs; + }, + HashPair: function(key, value, locInfo) { + this.loc = locInfo; + this.type = 'HashPair'; + this.key = key; + this.value = value; } }; - function logError(error, initialMessage) { - var errorArgs = []; - if (initialMessage) { errorArgs.push(initialMessage); } + // Must be exported as an object rather than the root of the module as the jison lexer + // most modify the object to operate properly. + __exports__["default"] = AST; + }); +enifed("htmlbars-syntax/handlebars/compiler/base", + ["./parser","./ast","./whitespace-control","./helpers","../utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; + var parser = __dependency1__["default"]; + var AST = __dependency2__["default"]; + var WhitespaceControl = __dependency3__["default"]; + var Helpers = __dependency4__; + var extend = __dependency5__.extend; - if (error) { - if (error.message) { errorArgs.push(error.message); } - if (error.stack) { errorArgs.push(error.stack); } + __exports__.parser = parser; - if (typeof error === "string") { errorArgs.push(error); } - } + var yy = {}; + extend(yy, Helpers, AST); - Ember.Logger.error.apply(this, errorArgs); + function parse(input, options) { + // Just return if an already-compile AST was passed in. + if (input.type === 'Program') { return input; } + + parser.yy = yy; + + // Altering the shared object here, but this is ok as parser is a sync operation + yy.locInfo = function(locInfo) { + return new yy.SourceLocation(options && options.srcName, locInfo); + }; + + var strip = new WhitespaceControl(); + return strip.accept(parser.parse(input)); } - function findChildRouteName(parentRoute, originatingChildRoute, name) { - var router = parentRoute.router, - childName, - targetChildRouteName = originatingChildRoute.routeName.split('.').pop(), - namespace = parentRoute.routeName === 'application' ? '' : parentRoute.routeName + '.'; + __exports__.parse = parse; + }); +enifed("htmlbars-syntax/handlebars/compiler/helpers", + ["../exception","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Exception = __dependency1__["default"]; - - // Second, try general loading state, e.g. 'loading' - childName = namespace + name; - if (routeHasBeenDefined(router, childName)) { - return childName; - } + function SourceLocation(source, locInfo) { + this.source = source; + this.start = { + line: locInfo.first_line, + column: locInfo.first_column + }; + this.end = { + line: locInfo.last_line, + column: locInfo.last_column + }; } - function routeHasBeenDefined(router, name) { - var container = router.container; - return router.hasRoute(name) && - (container.has('template:' + name) || container.has('route:' + name)); + __exports__.SourceLocation = SourceLocation;function stripFlags(open, close) { + return { + open: open.charAt(2) === '~', + close: close.charAt(close.length-3) === '~' + }; } - function triggerEvent(handlerInfos, ignoreFailure, args) { - var name = args.shift(); + __exports__.stripFlags = stripFlags;function stripComment(comment) { + return comment.replace(/^\{\{~?\!-?-?/, '') + .replace(/-?-?~?\}\}$/, ''); + } - 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."); - } + __exports__.stripComment = stripComment;function preparePath(data, parts, locInfo) { + /*jshint -W040 */ + locInfo = this.locInfo(locInfo); - var eventWasHandled = false; + var original = data ? '@' : '', + dig = [], + depth = 0, + depthString = ''; - for (var i = handlerInfos.length - 1; i >= 0; i--) { - var handlerInfo = handlerInfos[i], - handler = handlerInfo.handler; + for(var i=0,l=parts.length; i 0) { + throw new Exception('Invalid path: ' + original, {loc: locInfo}); + } else if (part === '..') { + depth++; + depthString += '../'; } + } else { + dig.push(part); } } - if (defaultActionHandlers[name]) { - defaultActionHandlers[name].apply(null, args); - return; - } + return new this.PathExpression(data, depth, dig, original, locInfo); + } - 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."); - } + __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 !== '&'; + + return new this.MustacheStatement(sexpr, escaped, strip, this.locInfo(locInfo)); } - function updatePaths(router) { - var appController = router.container.lookup('controller:application'); + __exports__.prepareMustache = prepareMustache;function prepareRawBlock(openRawBlock, content, close, locInfo) { + /*jshint -W040 */ + if (openRawBlock.sexpr.path.original !== close) { + var errorNode = {loc: openRawBlock.sexpr.loc}; - 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; + throw new Exception(openRawBlock.sexpr.path.original + " doesn't match " + close, errorNode); } - var infos = router.router.currentHandlerInfos, - path = EmberRouter._routePath(infos); + locInfo = this.locInfo(locInfo); + var program = new this.Program([content], null, {}, locInfo); - if (!('currentPath' in appController)) { - defineProperty(appController, 'currentPath'); - } + return new this.BlockStatement( + openRawBlock.sexpr, program, undefined, + {}, {}, {}, + locInfo); + } - set(appController, 'currentPath', path); + __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}; - if (!('currentRouteName' in appController)) { - defineProperty(appController, 'currentRouteName'); + throw new Exception(openBlock.sexpr.path.original + ' doesn\'t match ' + close.path.original, errorNode); } - set(appController, 'currentRouteName', infos[infos.length - 1].name); - } + program.blockParams = openBlock.blockParams; - 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 inverse, + inverseStrip; - 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); - }); - }); + if (inverseAndProgram) { + if (inverseAndProgram.chain) { + inverseAndProgram.program.body[0].closeStrip = close.strip || close.openStrip; + } - router.callbacks.push(callback); - router.map(dsl.generate()); - return router; - }, + inverseStrip = inverseAndProgram.strip; + inverse = inverseAndProgram.program; + } - _routePath: function(handlerInfos) { - var path = []; + if (inverted) { + inverted = inverse; + inverse = program; + program = inverted; + } - // 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' + return new this.BlockStatement( + openBlock.sexpr, program, inverse, + openBlock.strip, inverseStrip, close && (close.strip || close.openStrip), + this.locInfo(locInfo)); + } - function intersectionMatches(a1, a2) { - for (var i = 0, len = a1.length; i < len; ++i) { - if (a1[i] !== a2[i]) { - return false; + __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 true; + return token; } - - for (var i=1, l=handlerInfos.length; i 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; } - oldNameParts.shift(); - } - - path.push.apply(path, nameParts.slice(oldNameParts.length)); } + 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++; + } + if (this.options.ranges) this.yylloc.range[1]++; - return path.join("."); - } - }); + 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 + }; - __exports__["default"] = EmberRouter; + 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 */ }); -define("route-recognizer", +enifed("htmlbars-syntax/handlebars/compiler/visitor", ["exports"], function(__exports__) { "use strict"; - var specials = [ - '/', '.', '*', '+', '?', '|', - '(', ')', '[', ']', '{', '}', '\\' - ]; - - var escapeRegex = new RegExp('(\\' + specials.join('|\\') + ')', 'g'); - - function isArray(test) { - return Object.prototype.toString.call(test) === "[object Array]"; - } - - // A Segment represents a segment in the original route description. - // Each Segment type provides an `eachChar` and `regex` method. - // - // The `eachChar` method invokes the callback with one or more character - // specifications. A character specification consumes one or more input - // characters. - // - // The `regex` method returns a regex fragment for the segment. If the - // segment is a dynamic of star segment, the regex fragment also includes - // a capture. - // - // A character specification contains: - // - // * `validChars`: a String with a list of all valid characters, or - // * `invalidChars`: a String with a list of all invalid characters - // * `repeat`: true if the character specification can repeat - - function StaticSegment(string) { this.string = string; } - StaticSegment.prototype = { - eachChar: function(callback) { - var string = this.string, ch; + function Visitor() {} - for (var i=0, l=string.length; i " + n.nextStates.map(function(s) { return s.debug() }).join(" or ") + " )"; - }).join(", ") + var next = body[i+1], + sibling = body[i+2]; + if (!next) { + return isRoot; + } + + if (next.type === 'ContentStatement') { + return (sibling || !isRoot ? (/^\s*?\r?\n/) : (/^\s*?(\r?\n|$)/)).test(next.original); + } } - END IF **/ - // 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. + // Marks the node to the right of the position as omitted. + // I.e. {{foo}}' ' will mark the ' ' node as omitted. // - // This strategy generally prefers more static and less dynamic matching. - // Specifically, it + // If i is undefined, then the first child will be marked as such. // - // * 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; } + // 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; + } - 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; } - } + var original = current.value; + current.value = current.value.replace(multiple ? (/^\s+/) : (/^[ \t]*\r?\n?/), ''); + current.rightStripped = current.value !== original; + } - 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; } + // 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; + } - return 0; - }); + // 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; } - function recognizeChar(states, ch) { - var nextStates = []; + __exports__["default"] = WhitespaceControl; + }); +enifed("htmlbars-syntax/handlebars/exception", + ["exports"], + function(__exports__) { + "use strict"; - for (var i=0, l=states.length; i": ">", + '"': """, + "'": "'", + "`": "`" + }; - result.push({ handler: handler.handler, params: params, isDynamic: !!names.length }); - } + var badChars = /[&<>"'`]/g; + var possible = /[&<>"'`]/; - return result; + function escapeChar(chr) { + return escape[chr]; } - function addSegment(currentState, segment) { - segment.eachChar(function(ch) { - var state; + 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]; + } + } + } - currentState = currentState.put(ch); - }); + return obj; + } - return currentState; + __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; - // The main interface + 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 + ''; + } - var RouteRecognizer = function() { - this.rootState = new State(); - this.names = {}; - }; + // 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; + if(!possible.test(string)) { return string; } + return string.replace(badChars, escapeChar); + } - RouteRecognizer.prototype = { - add: function(routes, options) { - var currentState = this.rootState, regex = "^", - types = { statics: 0, dynamics: 0, stars: 0 }, - handlers = [], allSegments = [], name; + __exports__.escapeExpression = escapeExpression;function isEmpty(value) { + if (!value && value !== 0) { + return true; + } else if (isArray(value) && value.length === 0) { + return true; + } else { + return false; + } + } - var isEmpty = true; + __exports__.isEmpty = isEmpty;function appendContextPath(contextPath, id) { + return (contextPath ? contextPath + '.' : '') + id; + } - for (var i=0, l=routes.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; + }); - return "?" + pairs.join("&"); + // Except for `mustache`, all tokens are only allowed outside of + // a start or end tag. + var tokenHandlers = { + Comment: function(token) { + var current = this.currentElement(); + var comment = buildComment(token.chars); + appendChild(current, comment); }, - 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; + Chars: function(token) { + var current = this.currentElement(); + var text = buildText(token.chars); + appendChild(current, text); }, - recognize: function(path) { - var states = [ this.rootState ], - pathLen, i, l, queryStart, queryParams = {}, - isSlashDropped = false; + StartTag: function(tag) { + var element = buildElement(tag.tagName, tag.attributes, tag.helpers || [], []); + element.loc = { + start: { line: tag.firstLine, column: tag.firstColumn}, + end: { line: null, column: null} + }; - path = decodeURI(path); + this.elementStack.push(element); + if (voidMap.hasOwnProperty(tag.tagName) || tag.selfClosing) { + tokenHandlers.EndTag.call(this, tag); + } + }, - queryStart = path.indexOf('?'); - if (queryStart !== -1) { - var queryString = path.substr(queryStart + 1, path.length); - path = path.substr(0, queryStart); - queryParams = this.parseQueryString(queryString); + BlockStatement: function(/*block*/) { + if (this.tokenizer.state === 'comment') { + return; + } else if (this.tokenizer.state !== 'data') { + throw new Error("A block may only be used inside an HTML element or another block."); } + }, - // DEBUG GROUP path + MustacheStatement: function(mustache) { + var tokenizer = this.tokenizer; - if (path.charAt(0) !== "/") { path = "/" + path; } + switch(tokenizer.state) { + // Tag helpers + case "tagName": + tokenizer.addTagHelper(mustache.sexpr); + tokenizer.state = "beforeAttributeName"; + return; + case "beforeAttributeName": + tokenizer.addTagHelper(mustache.sexpr); + return; + case "attributeName": + case "afterAttributeName": + tokenizer.finalizeAttributeValue(); + tokenizer.addTagHelper(mustache.sexpr); + tokenizer.state = "beforeAttributeName"; + return; + case "afterAttributeValueQuoted": + tokenizer.addTagHelper(mustache.sexpr); + tokenizer.state = "beforeAttributeName"; + return; - pathLen = path.length; - if (pathLen > 1 && path.charAt(pathLen - 1) === "/") { - path = path.substr(0, pathLen - 1); - isSlashDropped = true; - } + // Attribute values + case "beforeAttributeValue": + tokenizer.markAttributeQuoted(false); + tokenizer.addToAttributeValue(mustache); + tokenizer.state = 'attributeValueUnquoted'; + return; + case "attributeValueDoubleQuoted": + case "attributeValueSingleQuoted": + case "attributeValueUnquoted": + tokenizer.addToAttributeValue(mustache); + return; - for (i=0, l=path.length; i 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 + ")"); + } - if (delegate && delegate.willAddRoute) { - target = delegate.willAddRoute(this.matcher.target, target); + if (typeof char === 'object') { + if (char.type === 'MustacheStatement') { + value.push(char); + } else { + throw new Error("Unsupported node in attribute value: " + char.type); } - - this.matcher.add(this.path, target); - - 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); + } else { + if (value.length > 0 && value[value.length - 1].type === 'TextNode') { + value[value.length - 1].chars += char; + } else { + value.push(builders.text(char)); } - return this; } }; - function Matcher(target) { - this.routes = {}; - this.children = {}; - this.target = target; + 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)); + } } - Matcher.prototype = { - add: function(path, handler) { - this.routes[path] = handler; - }, + 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); + } + } - addChild: function(path, target, callback, delegate) { - var matcher = new Matcher(target); - this.children[path] = matcher; + function unwrapMustache(mustache) { + if (isHelper(mustache.sexpr)) { + return mustache.sexpr; + } else { + return mustache.sexpr.path; + } + } - var match = generateMatch(path, matcher, delegate); + __exports__.unwrapMustache = unwrapMustache;__exports__.Tokenizer = Tokenizer; + }); +enifed("htmlbars-syntax/utils", + ["./builders","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var buildText = __dependency1__.buildText; - if (delegate && delegate.contextEntered) { - delegate.contextEntered(target, match); - } + // Regex to validate the identifier for block parameters. + // Based on the ID validation regex in Handlebars. - callback(match); + var ID_INVERSE_PATTERN = /[!"#%-,\.\/;->@\[-\^`\{-~]/; + + // 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. + + function parseComponentBlockParams(element, program) { + var l = element.attributes.length; + var attrNames = []; + + for (var i = 0; i < l; i++) { + attrNames.push(element.attributes[i].name); } - }; - function generateMatch(startingPath, matcher, delegate) { - return function(path, nestedCallback) { - var fullPath = startingPath + path; + var asIndex = attrNames.indexOf('as'); - if (nestedCallback) { - nestedCallback(generateMatch(fullPath, matcher, delegate)); - } else { - return new Target(startingPath + path, matcher, delegate); + 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 + '\''); } - }; + + 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); + } + } + + if (params.length === 0) { + throw new Error('Cannot use zero block parameters: \'' + paramsString + '\''); + } + + element.attributes = element.attributes.slice(0, asIndex); + program.blockParams = params; + } } - function addRoute(routeArray, path, handler) { - var len = 0; - for (var i=0, l=routeArray.length; i 0) { + last = children[len-1]; + if (usesMorph(last) && usesMorph(node)) { + children.push(buildText('')); } } + children.push(node); } - RouteRecognizer.prototype.map = function(callback, addRouteCallback) { - var matcher = new Matcher(); - - callback(generateMatch("", matcher, this.delegate)); + __exports__.appendChild = appendChild;function isHelper(sexpr) { + return (sexpr.params && sexpr.params.length > 0) || + (sexpr.hash && sexpr.hash.pairs.length > 0); + } - eachRoute([], matcher, function(route) { - if (addRouteCallback) { addRouteCallback(this, route); } - else { this.add(route); } - }, this); - }; + __exports__.isHelper = isHelper; }); - -define("router/handler-info", - ["./utils","rsvp/promise","exports"], - function(__dependency1__, __dependency2__, __exports__) { +enifed("htmlbars-syntax/walker", + ["exports"], + function(__exports__) { "use strict"; - var bind = __dependency1__.bind; - var merge = __dependency1__.merge; - var serialize = __dependency1__.serialize; - var promiseLabel = __dependency1__.promiseLabel; - var Promise = __dependency2__["default"]; - - function HandlerInfo(_props) { - var props = _props || {}; - merge(this, props); - this.initialize(props); + function Walker(order) { + this.order = order; + this.stack = []; } - HandlerInfo.prototype = { - name: null, - handler: null, - params: null, - context: null, + __exports__["default"] = Walker; - // Injected by the handler info factory. - factory: null, + Walker.prototype.visit = function(node, callback) { + if (!node) { + return; + } - initialize: function() {}, + this.stack.push(node); - log: function(payload, message) { - if (payload.log) { - payload.log(this.name + ': ' + message); - } - }, + if (this.order === 'post') { + this.children(node, callback); + callback(node, this); + } else { + callback(node, this); + this.children(node, callback); + } - promiseLabel: function(label) { - return promiseLabel("'" + this.name + "' " + label); + this.stack.pop(); + }; + + var visitors = { + Program: function(walker, node, callback) { + for (var i = 0; i < node.body.length; i++) { + walker.visit(node.body[i], callback); + } }, - getUnresolved: function() { - return this; + ElementNode: function(walker, node, callback) { + for (var i = 0; i < node.children.length; i++) { + walker.visit(node.children[i], callback); + } }, - serialize: function() { - return this.params || {}; + BlockStatement: function(walker, node, callback) { + walker.visit(node.program, callback); + walker.visit(node.inverse, callback); }, - 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); + ComponentNode: function(walker, node, callback) { + walker.visit(node.program, callback); + } + }; - 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")); - }, + 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); + } - runBeforeModelHook: function(payload) { - if (payload.trigger) { - payload.trigger(true, 'willResolveModel', payload, this.handler); + __exports__.equalInnerHTML = equalInnerHTML;function equalHTML(node, html) { + var fragment; + if (!node.nodeType && node.length) { + fragment = document.createDocumentFragment(); + while (node[0]) { + fragment.appendChild(node[0]); } - return this.runSharedModelHook(payload, 'beforeModel', []); - }, + } else { + fragment = node; + } - 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); + var div = document.createElement("div"); + div.appendChild(fragment.cloneNode(true)); - 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")); - }, + equalInnerHTML(div, html); + } - runSharedModelHook: function(payload, hookName, args) { - this.log(payload, "calling " + hookName + " hook"); + __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+'"'; + }); + } + return actualHTML; + } - if (this.queryParams) { - args.push(this.queryParams); - } - args.push(payload); + __exports__.normalizeInnerHTML = normalizeInnerHTML;// detect weird IE8 checked element string + var checkedInput = document.createElement('input'); + checkedInput.setAttribute('checked', 'checked'); + var checkedInputString = checkedInput.outerHTML; + function isCheckedInputHTML(element) { + equal(element.outerHTML, checkedInputString); + } - var handler = this.handler; - var result = handler[hookName] && handler[hookName].apply(handler, args); + __exports__.isCheckedInputHTML = isCheckedInputHTML; + }); +enifed("htmlbars-util", + ["./htmlbars-util/safe-string","./htmlbars-util/handlebars/utils","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var SafeString = __dependency1__["default"]; + var escapeExpression = __dependency2__.escapeExpression; - if (result && result.isTransition) { - result = null; + __exports__.SafeString = SafeString; + __exports__.escapeExpression = escapeExpression; + }); +enifed("htmlbars-util/array-utils", + ["exports"], + function(__exports__) { + "use strict"; + function forEach(array, callback, binding) { + var i; + if (binding === undefined) { + for (i = 0; i < array.length; i++) { + callback(array[i], i, array); } + } else { + for (i = 0; i < array.length; i++) { + callback.call(binding, array[i], i, array); + } + } + } - return Promise.resolve(result, null, this.promiseLabel("Resolve value returned from one of the model hooks")); - }, + __exports__.forEach = forEach; + }); +enifed("htmlbars-util/handlebars/safe-string", + ["exports"], + function(__exports__) { + "use strict"; + // Build out our basic SafeString type + function SafeString(string) { + this.string = string; + } - // overridden by subclasses - getModel: null, + SafeString.prototype.toString = SafeString.prototype.toHTML = function() { + return "" + this.string; + }; - 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__["default"] = SafeString; + }); +enifed("htmlbars-util/handlebars/utils", + ["./safe-string","exports"], + function(__dependency1__, __exports__) { + "use strict"; + /*jshint -W004 */ + var SafeString = __dependency1__["default"]; - stashResolvedModel: function(payload, resolvedModel) { - payload.resolvedModels = payload.resolvedModels || {}; - payload.resolvedModels[this.name] = resolvedModel; - }, + var escape = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", + "`": "`" + }; - becomeResolved: function(payload, resolvedContext) { - var params = this.serialize(resolvedContext); + var badChars = /[&<>"'`]/g; + var possible = /[&<>"'`]/; - if (payload) { - this.stashResolvedModel(payload, resolvedContext); - payload.params = payload.params || {}; - payload.params[this.name] = params; - } + function escapeChar(chr) { + return escape[chr]; + } - return this.factory('resolved', { - context: resolvedContext, - name: this.name, - handler: this.handler, - params: params - }); - }, + 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]; + } + } + } - 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; } + return obj; + } - var contextsMatch = (other.context === this.context); - return other.name !== this.name || - (this.hasOwnProperty('context') && !contextsMatch) || - (this.hasOwnProperty('params') && !paramsMatch(this.params, other.params)); - } + __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; - function paramsMatch(a, b) { - if ((!a) ^ (!b)) { - // Only one is null. - return false; + 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 + ''; } - if (!a) { - // Both must be null. + // 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; + + if(!possible.test(string)) { return string; } + return string.replace(badChars, escapeChar); + } + + __exports__.escapeExpression = escapeExpression;function isEmpty(value) { + if (!value && value !== 0) { + return true; + } else if (isArray(value) && value.length === 0) { return true; + } else { + return false; } + } - // 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; - } + __exports__.isEmpty = isEmpty;function appendContextPath(contextPath, id) { + return (contextPath ? contextPath + '.' : '') + id; + } + + __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 true; + return options; } - __exports__["default"] = HandlerInfo; + __exports__.merge = merge; }); -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__) { +enifed("htmlbars-util/quoting", + ["exports"], + function(__exports__) { "use strict"; - var ResolvedHandlerInfo = __dependency1__["default"]; - var UnresolvedHandlerInfoByObject = __dependency2__["default"]; - var UnresolvedHandlerInfoByParam = __dependency3__["default"]; + function escapeString(str) { + str = str.replace(/\\/g, "\\\\"); + str = str.replace(/"/g, '\\"'); + str = str.replace(/\n/g, "\\n"); + return str; + } - handlerInfoFactory.klasses = { - resolved: ResolvedHandlerInfo, - param: UnresolvedHandlerInfoByParam, - object: UnresolvedHandlerInfoByObject - }; + __exports__.escapeString = escapeString; - function handlerInfoFactory(name, props) { - var Ctor = handlerInfoFactory.klasses[name], - handlerInfo = new Ctor(props || {}); - handlerInfo.factory = handlerInfoFactory; - return handlerInfo; + function string(str) { + return '"' + escapeString(str) + '"'; } - __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"]; + __exports__.string = string; - 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")); - }, + function array(a) { + return "[" + a + "]"; + } - getUnresolved: function() { - return this.factory('param', { - name: this.name, - handler: this.handler, - params: this.params - }); - }, + __exports__.array = array; - isResolved: true - }); + function hash(pairs) { + return "{" + pairs.join(", ") + "}"; + } - __exports__["default"] = ResolvedHandlerInfo; + __exports__.hash = hash;function repeat(chars, times) { + var str = ""; + while (times--) { + str += chars; + } + return str; + } + + __exports__.repeat = repeat; }); -define("router/handler-info/unresolved-handler-info-by-object", - ["../handler-info","router/utils","rsvp/promise","exports"], +enifed("htmlbars-util/safe-string", + ["./handlebars/safe-string","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var SafeString = __dependency1__["default"]; + + __exports__["default"] = SafeString; + }); +enifed("morph", + ["./morph/morph","./morph/attr-morph","./morph/dom-helper","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"]; + var Morph = __dependency1__["default"]; + var AttrMorph = __dependency2__["default"]; + var DOMHelper = __dependency3__["default"]; - var UnresolvedHandlerInfoByObject = subclass(HandlerInfo, { - getModel: function(payload) { - this.log(payload, this.name + ": resolving provided model"); - return Promise.resolve(this.context); - }, + __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; - initialize: function(props) { - this.names = props.names || []; - this.context = props.context; - }, + function updateProperty(value) { + this.domHelper.setPropertyStrict(this.element, this.attrName, value); + } - /** - @private + function updateAttribute(value) { + if (value === null) { + this.domHelper.removeAttribute(this.element, this.attrName); + } else { + this.domHelper.setAttribute(this.element, this.attrName, value); + } + } - Serializes a handler using its custom `serialize` method or - by a default that looks up the expected property name from - the dynamic segment. + function AttrMorph(element, attrName, domHelper) { + this.element = element; + this.domHelper = domHelper; + this.escaped = true; - @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; + 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; + } + } - var object = {}; - if (isParam(model)) { - object[names[0]] = model; - return object; - } + AttrMorph.prototype.setContent = function (value) { + if (this.escaped) { + var sanitized = sanitizeAttributeValue(this.element, this.attrName, value); + this._update(sanitized); + } else { + this._update(value); + } + }; - // Use custom serialize if it exists. - if (handler.serialize) { - return handler.serialize(model, names); - } + __exports__["default"] = AttrMorph; + }); +enifed("morph/attr-morph/sanitize-attribute-value", + ["exports"], + function(__exports__) { + "use strict"; + /* jshint scripturl:true */ - if (names.length !== 1) { return; } + var parsingNode; + var badProtocols = { + 'javascript:': true, + 'vbscript:': true + }; - var name = names[0]; + var badTags = { + 'A': true, + 'BODY': true, + 'LINK': true, + 'IMG': true, + 'IFRAME': true + }; - if (/_id$/.test(name)) { - object[name] = model.id; - } else { - object[name] = model; + 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; + } + + if (value && value.toHTML) { + return value.toHTML(); + } + + if ((tagName === null || badTags[tagName]) && badAttributes[attribute]) { + parsingNode.href = value; + + if (badProtocols[parsingNode.protocol] === true) { + return 'unsafe:' + value; } - return object; } - }); - __exports__["default"] = UnresolvedHandlerInfoByObject; + return value; + } + + __exports__.sanitizeAttributeValue = sanitizeAttributeValue; }); -define("router/handler-info/unresolved-handler-info-by-param", - ["../handler-info","router/utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { +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__) { "use strict"; - var HandlerInfo = __dependency1__["default"]; - var merge = __dependency2__.merge; - var subclass = __dependency2__.subclass; - var promiseLabel = __dependency2__.promiseLabel; + 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; + } + } - // Generated by URL transitions and non-dynamic route segments in named Transitions. - var UnresolvedHandlerInfoByParam = subclass (HandlerInfo, { - initialize: function(props) { - this.params = props.params || {}; - }, + // 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 + // - getModel: function(payload) { - var fullParams = this.params; - if (payload && payload.queryParams) { - fullParams = {}; - merge(fullParams, this.params); - fullParams.queryParams = payload.queryParams; + 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'; } + } + } - var hookName = typeof this.handler.deserialize === 'function' ? - 'deserialize' : 'model'; + function buildSVGDOM(html, dom){ + var div = dom.document.createElement('div'); + div.innerHTML = ''+html+''; + return div.firstChild.childNodes; + } - return this.runSharedModelHook(payload, hookName, [fullParams]); + /* + * 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"); } - }); - - __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"]; + this.namespace = null; + } - var pop = Array.prototype.pop; + var prototype = DOMHelper.prototype; + prototype.constructor = DOMHelper; - function Router() { - this.recognizer = new RouteRecognizer(); - this.reset(); - } + prototype.getElementById = function(id, rootNode) { + rootNode = rootNode || this.document; + return rootNode.getElementById(id); + }; - Router.prototype = { + prototype.insertBefore = function(element, childElement, referenceChild) { + return element.insertBefore(childElement, referenceChild); + }; - /** - The main entry point into the router. The API is essentially - the same as the `map` method in `route-recognizer`. + prototype.appendChild = function(element, childElement) { + return element.appendChild(childElement); + }; - This method extracts the String handler at the last `.to()` - call and uses it as the name of the whole route. + prototype.childAt = function(element, indices) { + var child = element; - @param {Function} callback - */ - map: function(callback) { - this.recognizer.delegate = this.delegate; + for (var i = 0; i < indices.length; i++) { + child = child.childNodes[indices[i]]; + } - this.recognizer.map(callback, function(recognizer, routes) { - for (var i = routes.length - 1, proceed = true; i >= 0 && proceed; --i) { - var route = routes[i]; - recognizer.add(routes, { as: route.handler }); - proceed = route.path === '/' || route.path === '' || route.handler.slice(-6) === '.index'; - } - }); - }, + return child; + }; - hasRoute: function(route) { - return this.recognizer.hasRoute(route); - }, + prototype.appendText = function(element, text) { + return element.appendChild(this.document.createTextNode(text)); + }; - // 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.setAttribute = function(element, name, value) { + element.setAttribute(name, value); + }; - var wasTransitioning = !!this.activeTransition; - var oldState = wasTransitioning ? this.activeTransition.state : this.state; - var newTransition; - var router = this; + prototype.removeAttribute = function(element, name) { + element.removeAttribute(name); + }; - try { - var newState = intent.applyToState(oldState, this.recognizer, this.getHandler, isIntermediate); + prototype.setPropertyStrict = function(element, name, value) { + element[name] = value; + }; - if (handlerInfosEqual(newState.handlerInfos, oldState.handlerInfos)) { + prototype.setProperty = function(element, name, value) { + var lowercaseName = name.toLowerCase(); + if (element.namespaceURI === svgNamespace || lowercaseName === 'style') { + if (value === null) { + element.removeAttribute(name); + } else { + element.setAttribute(name, value); + } + } else { + var normalized = normalizeProperty(element, name); + if (normalized) { + element[normalized] = value; + } else { + if (value === null) { + element.removeAttribute(name); + } else { + element.setAttribute(name, value); + } + } + } + }; - // This is a no-op transition. See if query params changed. - var queryParamChangelist = getChangelist(oldState.queryParams, newState.queryParams); - if (queryParamChangelist) { + 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); + }; + } - // 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.addClasses = addClasses; + prototype.removeClasses = removeClasses; - 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.setNamespace = function(ns) { + this.namespace = ns; + }; - // 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.detectNamespace = function(element) { + this.namespace = interiorNamespace(element); + }; - oldState.queryParams = finalizeQueryParamChange(this, newState.handlerInfos, newState.queryParams, newTransition); + prototype.createDocumentFragment = function(){ + return this.document.createDocumentFragment(); + }; - 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; - } - } + prototype.createTextNode = function(text){ + return this.document.createTextNode(text); + }; - // No-op. No need to create a new transition. - return new Transition(this); - } + prototype.createComment = function(text){ + return this.document.createComment(text); + }; - if (isIntermediate) { - setupContexts(this, newState); - return; + prototype.repairClonedNode = function(element, blankChildTextNodes, isChecked){ + if (deletesBlankTextNodes && blankChildTextNodes.length > 0) { + for (var i=0, len=blankChildTextNodes.length;i]*selected/; + return function detectAutoSelectedOption(select, option, html) { //jshint ignore:line + return select.selectedIndex === 0 && + !detectAutoSelectedOptionRegex.test(html); + }; + })(); + } else { + detectAutoSelectedOption = function detectAutoSelectedOption(select, option, html) { //jshint ignore:line + var selectedAttribute = option.getAttribute('selected'); + return select.selectedIndex === 0 && ( + selectedAttribute === null || + ( selectedAttribute !== '' && selectedAttribute.toLowerCase() !== 'selected' ) + ); + }; + } - /** - Hook point for replacing the current URL, i.e. with replaceState + var tagNamesRequiringInnerHTMLFix = doc && (function(document) { + var tagNamesRequiringInnerHTMLFix; + // IE 9 and earlier don't allow us to set innerHTML on col, colgroup, frameset, + // html, style, table, tbody, tfoot, thead, title, tr. Detect this and add + // them to an initial list of corrected tags. + // + // Here we are only dealing with the ones which can have child nodes. + // + var tableNeedsInnerHTMLFix; + var tableInnerHTMLTestElement = document.createElement('table'); + try { + tableInnerHTMLTestElement.innerHTML = ''; + } catch (e) { + } finally { + tableNeedsInnerHTMLFix = (tableInnerHTMLTestElement.childNodes.length === 0); + } + if (tableNeedsInnerHTMLFix) { + tagNamesRequiringInnerHTMLFix = { + colgroup: ['table'], + table: [], + tbody: ['table'], + tfoot: ['table'], + thead: ['table'], + tr: ['table', 'tbody'] + }; + } - By default this behaves the same as `updateURL` + // IE 8 doesn't allow setting innerHTML on a select tag. Detect this and + // add it to the list of corrected tags. + // + var selectInnerHTMLTestElement = document.createElement('select'); + selectInnerHTMLTestElement.innerHTML = ''; + if (!selectInnerHTMLTestElement.childNodes[0]) { + tagNamesRequiringInnerHTMLFix = tagNamesRequiringInnerHTMLFix || {}; + tagNamesRequiringInnerHTMLFix.select = []; + } + return tagNamesRequiringInnerHTMLFix; + })(doc); - @param {String} url a URL to update to - */ - replaceURL: function(url) { - this.updateURL(url); - }, + function scriptSafeInnerHTML(element, html) { + // without a leading text node, IE will drop a leading script tag. + html = '­'+html; - /** - Transition into the specified named route. + element.innerHTML = html; - If necessary, trigger the exit callback on any handlers - that are no longer represented by the target route. + var nodes = element.childNodes; - @param {String} name the name of the route - */ - transitionTo: function(name) { - return doTransition(this, arguments); - }, + // Look for ­ to remove it. + var shyElement = nodes[0]; + while (shyElement.nodeType === 1 && !shyElement.nodeName) { + shyElement = shyElement.firstChild; + } + // At this point it's the actual unicode character. + if (shyElement.nodeType === 3 && shyElement.nodeValue.charAt(0) === "\u00AD") { + var newValue = shyElement.nodeValue.slice(1); + if (newValue.length) { + shyElement.nodeValue = shyElement.nodeValue.slice(1); + } else { + shyElement.parentNode.removeChild(shyElement); + } + } - intermediateTransitionTo: function(name) { - doTransition(this, arguments, true); - }, + return nodes; + } - refresh: function(pivotHandler) { + function buildDOMWithFix(html, contextualElement){ + var tagName = contextualElement.tagName; + // Firefox versions < 11 do not have support for element.outerHTML. + var outerHTML = contextualElement.outerHTML || new XMLSerializer().serializeToString(contextualElement); + if (!outerHTML) { + throw "Can't set innerHTML on "+tagName+" in this browser"; + } - var state = this.activeTransition ? this.activeTransition.state : this.state; - var handlerInfos = state.handlerInfos; - var params = {}; - for (var i = 0, len = handlerInfos.length; i < len; ++i) { - var handlerInfo = handlerInfos[i]; - params[handlerInfo.name] = handlerInfo.params || {}; - } + var wrappingTags = tagNamesRequiringInnerHTMLFix[tagName.toLowerCase()]; + var startTag = outerHTML.match(new RegExp("<"+tagName+"([^>]*)>", 'i'))[0]; + var endTag = ''; - log(this, "Starting a refresh transition"); - var intent = new NamedTransitionIntent({ - name: handlerInfos[handlerInfos.length - 1].name, - pivotHandler: pivotHandler || handlerInfos[0].handler, - contexts: [], // TODO collect contexts...? - queryParams: this._changedQueryParams || state.queryParams || {} - }); + var wrappedHTML = [startTag, html, endTag]; - return this.transitionByIntent(intent, false); - }, + var i = wrappingTags.length; + var wrappedDepth = 1 + i; + while(i--) { + wrappedHTML.unshift('<'+wrappingTags[i]+'>'); + wrappedHTML.push(''); + } - /** - Identical to `transitionTo` except that the current URL will be replaced - if possible. + var wrapper = document.createElement('div'); + scriptSafeInnerHTML(wrapper, wrappedHTML.join('')); + var element = wrapper; + while (wrappedDepth--) { + element = element.firstChild; + while (element && element.nodeType !== 1) { + element = element.nextSibling; + } + } + while (element && element.tagName !== tagName) { + element = element.nextSibling; + } + return element ? element.childNodes : []; + } - This method is intended primarily for use with `replaceState`. + var buildDOM; + if (needsShy) { + buildDOM = function buildDOM(html, contextualElement, dom){ + contextualElement = dom.cloneNode(contextualElement, false); + scriptSafeInnerHTML(contextualElement, html); + return contextualElement.childNodes; + }; + } else { + buildDOM = function buildDOM(html, contextualElement, dom){ + contextualElement = dom.cloneNode(contextualElement, false); + contextualElement.innerHTML = html; + return contextualElement.childNodes; + }; + } - @param {String} name the name of the route - */ - replaceWith: function(name) { - return doTransition(this, arguments).method('replace'); - }, + var buildIESafeDOM; + if (tagNamesRequiringInnerHTMLFix || movesWhitespace) { + buildIESafeDOM = function buildIESafeDOM(html, contextualElement, dom) { + // Make a list of the leading text on script nodes. Include + // script tags without any whitespace for easier processing later. + var spacesBefore = []; + var spacesAfter = []; + html = html.replace(/(\s*)()(\s*)/g, function(match, tag, spaces) { + spacesAfter.push(spaces); + return tag; + }); - @param {String} name the name of the route to generate - a URL for - @param {...Object} objects a list of objects to serialize + // Fetch nodes + var nodes; + if (tagNamesRequiringInnerHTMLFix[contextualElement.tagName.toLowerCase()]) { + // buildDOMWithFix uses string wrappers for problematic innerHTML. + nodes = buildDOMWithFix(html, contextualElement); + } else { + nodes = buildDOM(html, contextualElement, dom); + } - @return {String} a URL - */ - generate: function(handlerName) { + // Build a list of script tags, the nodes themselves will be + // mutated as we add test nodes. + var i, j, node, nodeScriptNodes; + var scriptNodes = []; + for (i=0;i 0) { + textNode = dom.document.createTextNode(spaceBefore); + scriptNode.parentNode.insertBefore(textNode, scriptNode); + } - // Construct a TransitionIntent with the provided params - // and apply it to the present state of the router. - var intent = new NamedTransitionIntent({ name: handlerName, contexts: suppliedParams }); - var state = intent.applyToState(this.state, this.recognizer, this.getHandler); - var params = {}; + spaceAfter = spacesAfter[i]; + if (spaceAfter && spaceAfter.length > 0) { + textNode = dom.document.createTextNode(spaceAfter); + scriptNode.parentNode.insertBefore(textNode, scriptNode.nextSibling); + } + } - for (var i = 0, len = state.handlerInfos.length; i < len; ++i) { - var handlerInfo = state.handlerInfos[i]; - var handlerParams = handlerInfo.serialize(); - merge(params, handlerParams); + return nodes; + }; + } else { + buildIESafeDOM = buildDOM; + } + + // When parsing innerHTML, the browser may set up DOM with some things + // not desired. For example, with a select element context and option + // innerHTML the first option will be marked selected. + // + // This method cleans up some of that, resetting those values back to + // their defaults. + // + function buildSafeDOM(html, contextualElement, dom) { + var childNodes = buildIESafeDOM(html, contextualElement, dom); + + if (contextualElement.tagName === 'SELECT') { + // Walk child nodes + for (var i = 0; childNodes[i]; i++) { + // Find and process the first option child node + if (childNodes[i].tagName === 'OPTION') { + if (detectAutoSelectedOption(childNodes[i].parentNode, childNodes[i], html)) { + // If the first node is selected but does not have an attribute, + // presume it is not really selected. + childNodes[i].parentNode.selectedIndex = -1; + } + break; + } } - params.queryParams = queryParams; + } - return this.recognizer.generate(handlerName, params); - }, + return childNodes; + } - isActive: function(handlerName) { + var buildHTMLDOM; + if (needsIntegrationPointFix) { + buildHTMLDOM = function buildHTMLDOM(html, contextualElement, dom){ + if (svgHTMLIntegrationPoints[contextualElement.tagName]) { + return buildSafeDOM(html, document.createElement('div'), dom); + } else { + return buildSafeDOM(html, contextualElement, dom); + } + }; + } else { + buildHTMLDOM = buildSafeDOM; + } - var partitionedArgs = extractQueryParams(slice.call(arguments, 1)), - contexts = partitionedArgs[0], - queryParams = partitionedArgs[1], - activeQueryParams = this.state.queryParams; + __exports__.buildHTMLDOM = buildHTMLDOM; + }); +enifed("morph/dom-helper/classes", + ["exports"], + function(__exports__) { + "use strict"; + var doc = typeof document === 'undefined' ? false : document; - var targetHandlerInfos = this.state.handlerInfos, - found = false, names, object, handlerInfo, handlerObj, i, len; + // PhantomJS has a broken classList. See https://github.com/ariya/phantomjs/issues/12782 + var canClassList = doc && (function(){ + var d = document.createElement('div'); + if (!d.classList) { + return false; + } + d.classList.add('boo'); + d.classList.add('boo', 'baz'); + return (d.className === 'boo baz'); + })(); - if (!targetHandlerInfos.length) { return false; } + function buildClassList(element) { + var classString = (element.getAttribute('class') || ''); + return classString !== '' && classString !== ' ' ? classString.split(' ') : []; + } - var targetHandler = targetHandlerInfos[targetHandlerInfos.length - 1].name; - var recogHandlers = this.recognizer.handlersFor(targetHandler); + function intersect(containingArray, valuesArray) { + var containingIndex = 0; + var containingLength = containingArray.length; + var valuesIndex = 0; + var valuesLength = valuesArray.length; - var index = 0; - for (len = recogHandlers.length; index < len; ++index) { - handlerInfo = targetHandlerInfos[index]; - if (handlerInfo.name === handlerName) { break; } + var intersection = new Array(valuesLength); + + // TODO: rewrite this loop in an optimal manner + for (;containingIndex 0 ? existingClasses.join(' ') : ''); + } + } - var intent = new NamedTransitionIntent({ - name: targetHandler, - contexts: contexts - }); + function removeClassesViaAttribute(element, classNames) { + var existingClasses = buildClassList(element); - var newState = intent.applyToHandlers(state, recogHandlers, this.getHandler, targetHandler, true, true); + var indexes = intersect(classNames, existingClasses); + var didChange = false; + var newClasses = []; - // Get a hash of QPs that will still be active on new route - var activeQPsOnNewHandler = {}; - merge(activeQPsOnNewHandler, queryParams); - for (var key in activeQueryParams) { - if (activeQueryParams.hasOwnProperty(key) && - activeQPsOnNewHandler.hasOwnProperty(key)) { - activeQPsOnNewHandler[key] = activeQueryParams[key]; + for (var i=0, l=existingClasses.length; i 0 ? newClasses.join(' ') : ''); + } + } + + var addClasses, removeClasses; + if (canClassList) { + addClasses = function addClasses(element, classNames) { + if (element.classList) { + if (classNames.length === 1) { + element.classList.add(classNames[0]); + } else if (classNames.length === 2) { + element.classList.add(classNames[0], classNames[1]); + } else { + element.classList.add.apply(element.classList, classNames); + } + } else { + addClassesViaAttribute(element, classNames); + } + }; + removeClasses = function removeClasses(element, classNames) { + if (element.classList) { + if (classNames.length === 1) { + element.classList.remove(classNames[0]); + } else if (classNames.length === 2) { + element.classList.remove(classNames[0], classNames[1]); + } else { + element.classList.remove.apply(element.classList, classNames); } + } else { + removeClassesViaAttribute(element, classNames); } + }; + } else { + addClasses = addClassesViaAttribute; + removeClasses = removeClassesViaAttribute; + } - return handlerInfosEqual(newState.handlerInfos, state.handlerInfos) && - !getChangelist(activeQPsOnNewHandler, queryParams); - }, + __exports__.addClasses = addClasses; + __exports__.removeClasses = removeClasses; + }); +enifed("morph/dom-helper/prop", + ["exports"], + function(__exports__) { + "use strict"; + // TODO should this be an o_create kind of thing? + var propertyCaches = {}; + __exports__.propertyCaches = propertyCaches; + function normalizeProperty(element, attrName) { + var tagName = element.tagName; + var key; + var cache = propertyCaches[tagName]; + if (!cache) { + // TODO should this be an o_create kind of thing? + cache = {}; + for (key in element) { + cache[key.toLowerCase()] = key; + } + propertyCaches[tagName] = cache; + } - trigger: function(name) { - var args = slice.call(arguments); - trigger(this, this.currentHandlerInfos, false, args); - }, + // presumes that the attrName has been lowercased. + return cache[attrName]; + } - /** - Hook point for logging transition status updates. + __exports__.normalizeProperty = normalizeProperty; + }); +enifed("morph/morph", + ["exports"], + function(__exports__) { + "use strict"; + var splice = Array.prototype.splice; - @param {String} message The message to log. - */ - log: null - }; + function ensureStartEnd(start, end) { + if (start === null || end === null) { + throw new Error('a fragment parent must have boundary nodes in order to detect insertion'); + } + } - /** - @private + function ensureContext(contextualElement) { + if (!contextualElement || contextualElement.nodeType !== 1) { + throw new Error('An element node must be provided for a contextualElement, you provided ' + + (contextualElement ? 'nodeType ' + contextualElement.nodeType : 'nothing')); + } + } - Takes an Array of `HandlerInfo`s, figures out which ones are - exiting, entering, or changing contexts, and calls the - proper handler hooks. + // TODO: this is an internal API, this should be an assert + function Morph(parent, start, end, domHelper, contextualElement) { + if (parent.nodeType === 11) { + ensureStartEnd(start, end); + this.element = null; + } else { + this.element = parent; + } + this._parent = parent; + this.start = start; + this.end = end; + this.domHelper = domHelper; + ensureContext(contextualElement); + this.contextualElement = contextualElement; + this.escaped = true; + this.reset(); + } - For example, consider the following tree of handlers. Each handler is - followed by the URL segment it handles. + Morph.prototype.reset = function() { + this.text = null; + this.owner = null; + this.morphs = null; + this.before = null; + this.after = null; + }; - ``` - |~index ("/") - | |~posts ("/posts") - | | |-showPost ("/:id") - | | |-newPost ("/new") - | | |-editPost ("/edit") - | |~about ("/about/:id") - ``` + Morph.prototype.parent = function () { + if (!this.element) { + var parent = this.start.parentNode; + if (this._parent !== parent) { + this._parent = parent; + } + if (parent.nodeType === 1) { + this.element = parent; + } + } + return this._parent; + }; - Consider the following transitions: + Morph.prototype.destroy = function () { + if (this.owner) { + this.owner.removeMorph(this); + } else { + clear(this.element || this.parent(), this.start, this.end); + } + }; - 1. A URL transition to `/posts/1`. - 1. Triggers the `*model` callbacks on the - `index`, `posts`, and `showPost` handlers - 2. Triggers the `enter` callback on the same - 3. Triggers the `setup` callback on the same - 2. A direct transition to `newPost` - 1. Triggers the `exit` callback on `showPost` - 2. Triggers the `enter` callback on `newPost` - 3. Triggers the `setup` callback on `newPost` - 3. A direct transition to `about` with a specified - context object - 1. Triggers the `exit` callback on `newPost` - and `posts` - 2. Triggers the `serialize` callback on `about` - 3. Triggers the `enter` callback on `about` - 4. Triggers the `setup` callback on `about` + Morph.prototype.removeMorph = function (morph) { + var morphs = this.morphs; + for (var i=0, l=morphs.length; i 0 ? morphs[index-1] : null; + var after = index < morphs.length ? morphs[index] : null; + var start = before === null ? this.start : (before.end === null ? parent.lastChild : before.end.previousSibling); + var end = after === null ? this.end : (after.start === null ? parent.firstChild : after.start.nextSibling); + var morph = new Morph(parent, start, end, this.domHelper, this.contextualElement); - var handler = handlerInfo.handler, - context = handlerInfo.context; + morph.owner = this; + morph._update(parent, node); - if (enter && handler.enter) { handler.enter(transition); } - if (transition && transition.isAborted) { - throw new TransitionAborted(); + if (before !== null) { + morph.before = before; + before.end = start.nextSibling; + before.after = morph; } - handler.context = context; - if (handler.contextDidChange) { handler.contextDidChange(); } + if (after !== null) { + morph.after = after; + after.before = morph; + after.start = end.previousSibling; + } - if (handler.setup) { handler.setup(context, transition); } - if (transition && transition.isAborted) { - throw new TransitionAborted(); + this.morphs.splice(index, 0, morph); + return morph; + }; + + Morph.prototype.replace = function (index, removedLength, addedNodes) { + if (this.morphs === null) { + this.morphs = []; } + var parent = this.element || this.parent(); + var morphs = this.morphs; + var before = index > 0 ? morphs[index-1] : null; + var after = index+removedLength < morphs.length ? morphs[index+removedLength] : null; + var start = before === null ? this.start : (before.end === null ? parent.lastChild : before.end.previousSibling); + var end = after === null ? this.end : (after.start === null ? parent.firstChild : after.start.nextSibling); + var addedLength = addedNodes === undefined ? 0 : addedNodes.length; + var args, i, current; - currentHandlerInfos.push(handlerInfo); + if (removedLength > 0) { + clear(parent, start, end); + } + + if (addedLength === 0) { + if (before !== null) { + before.after = after; + before.end = end; + } + if (after !== null) { + after.before = before; + after.start = start; + } + morphs.splice(index, removedLength); + return; + } - return true; - } + args = new Array(addedLength+2); + if (addedLength > 0) { + for (i=0; i= 0; --i) { - var handlerInfo = handlerInfos[i]; - merge(params, handlerInfo.params); - if (handlerInfo.handler.inaccessibleByURL) { - urlMethod = null; - } + regex: function() { + return "(.+)"; + }, + + generate: function(params) { + return params[this.name]; } + }; - if (urlMethod) { - params.queryParams = transition._visibleQueryParams || state.queryParams; - var url = router.recognizer.generate(handlerName, params); + function EpsilonSegment() {} + EpsilonSegment.prototype = { + eachChar: function() {}, + regex: function() { return ""; }, + generate: function() { return ""; } + }; - if (urlMethod === 'replace') { - router.replaceURL(url); + function parse(route, names, types) { + // normalize route as not starting with a "/". Recognition will + // also normalize. + if (route.charAt(0) === "/") { route = route.substr(1); } + + var segments = route.split("/"), results = []; + + for (var i=0, l=segments.length; i " + n.nextStates.map(function(s) { return s.debug() }).join(" or ") + " )"; + }).join(", ") + } + END IF **/ - for (var i = 0, len = handlerInfos.length; i < len; ++i) { - if (handlerInfos[i] !== otherHandlerInfos[i]) { - return false; + // 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; } + + 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 true; + + 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; } + + return 0; + }); } - function finalizeQueryParamChange(router, resolvedHandlers, newQueryParams, transition) { - // We fire a finalizeQueryParamChange event which - // gives the new route hierarchy a chance to tell - // us which query params it's consuming and what - // their final values are. If a query param is - // no longer consumed in the final route hierarchy, - // its serialized segment will be removed - // from the URL. + function recognizeChar(states, ch) { + var nextStates = []; - for (var k in newQueryParams) { - if (newQueryParams.hasOwnProperty(k) && - newQueryParams[k] === null) { - delete newQueryParams[k]; - } + for (var i=0, l=states.length; i= 0; --i) { - var result = handlers[i]; - var name = result.handler; - var handler = getHandler(name); + for (var j=0, m=segments.length; j 0) { - if (i >= invalidateIndex) { - newHandlerInfo = this.createParamHandlerInfo(name, handler, result.names, objects, oldHandlerInfo); - } else { - newHandlerInfo = this.getHandlerInfoForDynamicSegment(name, handler, result.names, objects, oldHandlerInfo, targetRouteName, i); - } - } else { - // This route has no dynamic segment. - // Therefore treat as a param-based handlerInfo - // with empty params. This will cause the `model` - // hook to be called with empty params, which is desirable. - newHandlerInfo = this.createParamHandlerInfo(name, handler, result.names, objects, oldHandlerInfo); - } + isEmpty = false; - if (checkingIfActive) { - // If we're performing an isActive check, we want to - // serialize URL params with the provided context, but - // ignore mismatches between old and new context. - newHandlerInfo = newHandlerInfo.becomeResolved(null, newHandlerInfo.context); - var oldContext = oldHandlerInfo && oldHandlerInfo.context; - if (result.names.length > 0 && newHandlerInfo.context === oldContext) { - // If contexts match in isActive test, assume params also match. - // This allows for flexibility in not requiring that every last - // handler provide a `serialize` method - newHandlerInfo.params = oldHandlerInfo && oldHandlerInfo.params; - } - newHandlerInfo.context = oldContext; - } + // Add a "/" for the new segment + currentState = currentState.put({ validChars: "/" }); + regex += "/"; - var handlerToUse = oldHandlerInfo; - if (i >= invalidateIndex || newHandlerInfo.shouldSupercede(oldHandlerInfo)) { - invalidateIndex = Math.min(i, invalidateIndex); - handlerToUse = newHandlerInfo; + // Add a representation of the segment to the NFA and regex + currentState = addSegment(currentState, segment); + regex += segment.regex(); } - if (isIntermediate && !checkingIfActive) { - handlerToUse = handlerToUse.becomeResolved(null, handlerToUse.context); - } + var handler = { handler: route.handler, names: names }; + handlers.push(handler); + } - newState.handlerInfos.unshift(handlerToUse); + if (isEmpty) { + currentState = currentState.put({ validChars: "/" }); + regex += "/"; } - if (objects.length > 0) { - throw new Error("More context objects were passed than there are dynamic segments for the route: " + targetRouteName); + currentState.handlers = handlers; + currentState.regex = new RegExp(regex + "$"); + currentState.types = types; + + if (name = options && options.as) { + this.names[name] = { + segments: allSegments, + handlers: handlers + }; } + }, - if (!isIntermediate) { - this.invalidateChildren(newState.handlerInfos, invalidateIndex); + handlersFor: function(name) { + var route = this.names[name], result = []; + if (!route) { throw new Error("There is no route named " + name); } + + for (var i=0, l=route.handlers.length; i 0) { + if (params && params.queryParams) { + output += this.generateQueryString(params.queryParams, route.handlers); + } - // Use the objects provided for this transition. - objectToUse = objects[objects.length - 1]; - if (isParam(objectToUse)) { - return this.createParamHandlerInfo(name, handler, names, objects, oldHandlerInfo); - } else { - objects.pop(); + return output; + }, + + generateQueryString: function(params, handlers) { + var pairs = []; + var keys = []; + for(var key in params) { + if (params.hasOwnProperty(key)) { + keys.push(key); } - } else if (oldHandlerInfo && oldHandlerInfo.name === name) { - // Reuse the matching oldHandlerInfo - return oldHandlerInfo; - } else { - if (this.preTransitionState) { - var preTransitionHandlerInfo = this.preTransitionState.handlerInfos[i]; - objectToUse = preTransitionHandlerInfo && preTransitionHandlerInfo.context; + } + 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 = encodeURIComponent(key); + if (isArray(value)) { + for (var j = 0, l = value.length; j < l; j++) { + var arrayPair = key + '[]' + '=' + encodeURIComponent(value[j]); + pairs.push(arrayPair); + } } else { - // Ideally we should throw this error to provide maximal - // information to the user that not enough context objects - // were provided, but this proves too cumbersome in Ember - // in cases where inner template helpers are evaluated - // before parent helpers un-render, in which cases this - // error somewhat prematurely fires. - //throw new Error("Not enough context objects were provided to complete a transition to " + targetRouteName + ". Specifically, the " + name + " route needs an object that can be serialized into its dynamic URL segments [" + names.join(', ') + "]"); - return oldHandlerInfo; + pair += "=" + encodeURIComponent(value); + pairs.push(pair); } } - return handlerInfoFactory('object', { - name: name, - handler: handler, - context: objectToUse, - names: names - }); - }, - - createParamHandlerInfo: function(name, handler, names, objects, oldHandlerInfo) { - var params = {}; - - // Soak up all the provided string/numbers - var numNames = names.length; - while (numNames--) { + if (pairs.length === 0) { return ''; } - // Only use old params if the names match with the new handler - var oldParams = (oldHandlerInfo && name === oldHandlerInfo.name && oldHandlerInfo.params) || {}; + return "?" + pairs.join("&"); + }, - var peek = objects[objects.length - 1]; - var paramName = names[numNames]; - if (isParam(peek)) { - params[paramName] = "" + objects.pop(); + 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 { - // If we're here, this means only some of the params - // were string/number params, so try and use a param - // value from a previous handler. - if (oldParams.hasOwnProperty(paramName)) { - params[paramName] = oldParams[paramName]; - } else { - throw new Error("You didn't provide enough string/numeric parameters to satisfy all of the dynamic segments for route " + name); + //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] = value; } } + return queryParams; + }, - return handlerInfoFactory('param', { - name: name, - handler: handler, - params: params - }); - } - }); - }); -define("router/transition-intent/url-transition-intent", - ["../transition-intent","../transition-state","../handler-info/factory","../utils","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - var TransitionIntent = __dependency1__["default"]; - var TransitionState = __dependency2__["default"]; - var handlerInfoFactory = __dependency3__["default"]; - var oCreate = __dependency4__.oCreate; - var merge = __dependency4__.merge; - var subclass = __dependency4__.subclass; + recognize: function(path) { + var states = [ this.rootState ], + pathLen, i, l, queryStart, queryParams = {}, + isSlashDropped = false; - __exports__["default"] = subclass(TransitionIntent, { - url: null, + queryStart = path.indexOf('?'); + if (queryStart !== -1) { + var queryString = path.substr(queryStart + 1, path.length); + path = path.substr(0, queryStart); + queryParams = this.parseQueryString(queryString); + } - initialize: function(props) { - this.url = props.url; - }, + path = decodeURI(path); - applyToState: function(oldState, recognizer, getHandler) { - var newState = new TransitionState(); + // DEBUG GROUP path - var results = recognizer.recognize(this.url), - queryParams = {}, - i, len; + if (path.charAt(0) !== "/") { path = "/" + path; } - if (!results) { - throw new UnrecognizedURLError(this.url); + pathLen = path.length; + if (pathLen > 1 && path.charAt(pathLen - 1) === "/") { + path = path.substr(0, pathLen - 1); + isSlashDropped = true; } - var statesDiffer = false; + for (i=0, l=path.length; i= handlerInfos.length ? - handlerInfos.length - 1 : payload.resolveIndex; - return Promise.reject({ - error: error, - handlerWithError: currentState.handlerInfos[errorHandlerIndex].handler, - wasAborted: wasAborted, - state: currentState - }); - } + function Matcher(target) { + this.routes = {}; + this.children = {}; + this.target = target; + } - function proceed(resolvedHandlerInfo) { - var wasAlreadyResolved = currentState.handlerInfos[payload.resolveIndex].isResolved; + Matcher.prototype = { + add: function(path, handler) { + this.routes[path] = handler; + }, - // Swap the previously unresolved handlerInfo with - // the resolved handlerInfo - currentState.handlerInfos[payload.resolveIndex++] = resolvedHandlerInfo; + addChild: function(path, target, callback, delegate) { + var matcher = new Matcher(target); + this.children[path] = matcher; - if (!wasAlreadyResolved) { - // Call the redirect hook. The reason we call it here - // vs. afterModel is so that redirects into child - // routes don't re-run the model hooks for this - // already-resolved route. - var handler = resolvedHandlerInfo.handler; - if (handler && handler.redirect) { - handler.redirect(resolvedHandlerInfo.context, payload); - } - } + var match = generateMatch(path, matcher, delegate); - // Proceed after ensuring that the redirect hook - // didn't abort this transition by transitioning elsewhere. - return innerShouldContinue().then(resolveOneHandlerInfo, null, promiseLabel('Resolve handler')); + if (delegate && delegate.contextEntered) { + delegate.contextEntered(target, match); } - function resolveOneHandlerInfo() { - if (payload.resolveIndex === currentState.handlerInfos.length) { - // This is is the only possible - // fulfill value of TransitionState#resolve - return { - error: null, - state: currentState - }; - } - - var handlerInfo = currentState.handlerInfos[payload.resolveIndex]; - - return handlerInfo.resolve(innerShouldContinue, payload) - .then(proceed, null, promiseLabel('Proceed')); - } + callback(match); } }; - __exports__["default"] = TransitionState; - }); -define("router/transition", - ["rsvp/promise","./handler-info","./utils","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var Promise = __dependency1__["default"]; - var ResolvedHandlerInfo = __dependency2__.ResolvedHandlerInfo; - var trigger = __dependency3__.trigger; - var slice = __dependency3__.slice; - var log = __dependency3__.log; - var promiseLabel = __dependency3__.promiseLabel; - - /** - @private + function generateMatch(startingPath, matcher, delegate) { + return function(path, nestedCallback) { + var fullPath = startingPath + path; - A Transition is a thennable (a promise-like object) that represents - an attempt to transition to another route. It can be aborted, either - explicitly via `abort` or by attempting another transition while a - previous one is still underway. An aborted transition can also - be `retry()`d later. - */ - function Transition(router, intent, state, error) { - var transition = this; - this.state = state || router.state; - this.intent = intent; - this.router = router; - this.data = this.intent && this.intent.data || {}; - this.resolvedModels = {}; - this.queryParams = {}; + if (nestedCallback) { + nestedCallback(generateMatch(fullPath, matcher, delegate)); + } else { + return new Target(startingPath + path, matcher, delegate); + } + }; + } - if (error) { - this.promise = Promise.reject(error); - return; + function addRoute(routeArray, path, handler) { + var len = 0; + for (var i=0, l=routeArray.length; i 0 && array[len - 1] && array[len - 1].hasOwnProperty('queryParams')) { - queryParams = array[len - 1].queryParams; - head = slice.call(array, 0, len - 1); - return [head, queryParams]; - } else { - return [array, null]; + function paramsMatch(a, b) { + if ((!a) ^ (!b)) { + // Only one is null. + return false; } - } - - __exports__.extractQueryParams = extractQueryParams;/** - @private - Coerces query param properties and array elements into strings. - **/ - function coerceQueryParamsToString(queryParams) { - for (var key in queryParams) { - if (typeof queryParams[key] === 'number') { - queryParams[key] = '' + queryParams[key]; - } else if (isArray(queryParams[key])) { - for (var i = 0, l = queryParams[key].length; i < l; i++) { - queryParams[key][i] = '' + queryParams[key][i]; - } - } + if (!a) { + // Both must be null. + return true; } - } - /** - @private - */ - function log(router, sequence, msg) { - if (!router.log) { return; } - if (arguments.length === 3) { - router.log("Transition #" + sequence + ": " + msg); - } else { - msg = sequence; - router.log(msg); + // 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 true; } - __exports__.log = log;function bind(context, fn) { - var boundArgs = arguments; - return function(value) { - var args = slice.call(boundArgs, 2); - args.push(value); - return fn.apply(context, args); - }; - } - - __exports__.bind = bind;function isParam(object) { - return (typeof object === "string" || object instanceof String || typeof object === "number" || object instanceof Number); - } + __exports__["default"] = HandlerInfo; + }); +enifed("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 ResolvedHandlerInfo = __dependency1__["default"]; + var UnresolvedHandlerInfoByObject = __dependency2__["default"]; + var UnresolvedHandlerInfoByParam = __dependency3__["default"]; + handlerInfoFactory.klasses = { + resolved: ResolvedHandlerInfo, + param: UnresolvedHandlerInfoByParam, + object: UnresolvedHandlerInfoByObject + }; - function forEach(array, callback) { - for (var i=0, l=array.length; i=0; i--) { - var handlerInfo = handlerInfos[i], - handler = handlerInfo.handler; + __exports__["default"] = ResolvedHandlerInfo; + }); +enifed("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"]; - if (handler.events && handler.events[name]) { - if (handler.events[name].apply(handler, args) === true) { - eventWasHandled = true; - } else { - return; - } - } - } + var UnresolvedHandlerInfoByObject = subclass(HandlerInfo, { + getModel: function(payload) { + this.log(payload, this.name + ": resolving provided model"); + return Promise.resolve(this.context); + }, - if (!eventWasHandled && !ignoreFailure) { - throw new Error("Nothing handled the event '" + name + "'."); - } - } + initialize: function(props) { + this.names = props.names || []; + this.context = props.context; + }, - __exports__.trigger = trigger;function getChangelist(oldObject, newObject) { - var key; - var results = { - all: {}, - changed: {}, - removed: {} - }; + /** + @private - merge(results.all, newObject); + Serializes a handler using its custom `serialize` method or + by a default that looks up the expected property name from + the dynamic segment. - var didChange = false; - coerceQueryParamsToString(oldObject); - coerceQueryParamsToString(newObject); + @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; - // Calculate removals - for (key in oldObject) { - if (oldObject.hasOwnProperty(key)) { - if (!newObject.hasOwnProperty(key)) { - didChange = true; - results.removed[key] = oldObject[key]; - } + var object = {}; + if (isParam(model)) { + object[names[0]] = model; + return object; } - } - // Calculate changes - for (key in newObject) { - if (newObject.hasOwnProperty(key)) { - if (isArray(oldObject[key]) && isArray(newObject[key])) { - if (oldObject[key].length !== newObject[key].length) { - results.changed[key] = newObject[key]; - didChange = true; - } else { - for (var i = 0, l = oldObject[key].length; i < l; i++) { - if (oldObject[key][i] !== newObject[key][i]) { - results.changed[key] = newObject[key]; - didChange = true; - } - } - } - } - else { - if (oldObject[key] !== newObject[key]) { - results.changed[key] = newObject[key]; - didChange = true; - } - } + // Use custom serialize if it exists. + if (handler.serialize) { + return handler.serialize(model, names); } - } - return didChange && results; - } + if (names.length !== 1) { return; } - __exports__.getChangelist = getChangelist;function promiseLabel(label) { - return 'Router: ' + label; - } + var name = names[0]; - __exports__.promiseLabel = promiseLabel;function subclass(parentConstructor, proto) { - function C(props) { - parentConstructor.call(this, props || {}); + if (/_id$/.test(name)) { + object[name] = model.id; + } else { + object[name] = model; + } + return object; } - C.prototype = oCreate(parentConstructor.prototype); - merge(C.prototype, proto); - return C; - } + }); - __exports__.subclass = subclass;__exports__.merge = merge; - __exports__.slice = slice; - __exports__.isParam = isParam; - __exports__.coerceQueryParamsToString = coerceQueryParamsToString; + __exports__["default"] = UnresolvedHandlerInfoByObject; }); -define("router", - ["./router/router","exports"], - function(__dependency1__, __exports__) { +enifed("router/handler-info/unresolved-handler-info-by-param", + ["../handler-info","router/utils","exports"], + function(__dependency1__, __dependency2__, __exports__) { "use strict"; - var Router = __dependency1__["default"]; + var HandlerInfo = __dependency1__["default"]; + var resolveHook = __dependency2__.resolveHook; + var merge = __dependency2__.merge; + var subclass = __dependency2__.subclass; + var promiseLabel = __dependency2__.promiseLabel; - __exports__["default"] = Router; - }); + // 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; + } -(function() { -define("ember-application/ext/controller", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/error","ember-metal/utils","ember-metal/computed","ember-runtime/controllers/controller","ember-routing/system/controller_for","exports"], + var handler = this.handler; + var hookName = resolveHook(handler, 'deserialize') || + resolveHook(handler, 'model'); + + return this.runSharedModelHook(payload, hookName, [fullParams]); + } + }); + + __exports__["default"] = UnresolvedHandlerInfoByParam; + }); +enifed("router/router", + ["route-recognizer","rsvp/promise","./utils","./transition-state","./transition","./transition-intent/named-transition-intent","./transition-intent/url-transition-intent","./handler-info","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { "use strict"; - /** - @module ember - @submodule ember-application - */ + 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 callHook = __dependency3__.callHook; + var TransitionState = __dependency4__["default"]; + var logAbort = __dependency5__.logAbort; + var Transition = __dependency5__.Transition; + var TransitionAborted = __dependency5__.TransitionAborted; + var NamedTransitionIntent = __dependency6__["default"]; + var URLTransitionIntent = __dependency7__["default"]; + var ResolvedHandlerInfo = __dependency8__.ResolvedHandlerInfo; - var Ember = __dependency1__["default"]; - // Ember.assert - var get = __dependency2__.get; - var set = __dependency3__.set; - var EmberError = __dependency4__["default"]; - var inspect = __dependency5__.inspect; - var computed = __dependency6__.computed; - var ControllerMixin = __dependency7__.ControllerMixin; - var meta = __dependency5__.meta; - var controllerFor = __dependency8__.controllerFor; - var meta = __dependency5__.meta; + var pop = Array.prototype.pop; - function verifyNeedsDependencies(controller, container, needs) { - var dependency, i, l, missing = []; + function Router() { + this.recognizer = new RouteRecognizer(); + this.reset(); + } - for (i=0, l=needs.length; i 1 ? 'they' : 'it') + " could not be found"); - } - } - var defaultControllersComputedProperty = computed(function() { - var controller = this; + // No-op. No need to create a new transition. + return new Transition(this); + } - return { - needs: get(controller, 'needs'), - container: get(controller, 'container'), - unknownProperty: function(controllerName) { - var needs = this.needs, - dependency, i, l; - for (i=0, l=needs.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'; + } }); - ``` - - You will be able to get access to it: + }, - ```javascript - this.get('controllers.commentsNew'); // instance of App.CommentsNewController - ``` + hasRoute: function(route) { + return this.recognizer.hasRoute(route); + }, - This is only available for singleton controllers. + queryParamsTransition: function(changelist, wasTransitioning, oldState, newState) { + var router = this; - @property {Array} needs - @default [] - */ - needs: [], + fireQueryParamDidChange(this, newState, changelist); - init: function() { - var needs = get(this, 'needs'), - length = get(needs, 'length'); + 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. - if (length > 0) { - Ember.assert(' `' + inspect(this) + ' specifies `needs`, but does ' + - "not have a container. Please ensure this controller was " + - "instantiated with a container.", - this.container || meta(this, false).descs.controllers !== defaultControllersComputedProperty); + // 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). + var newTransition = new Transition(this); + newTransition.queryParamsOnly = true; - if (this.container) { - verifyNeedsDependencies(this, this.container, needs); - } + oldState.queryParams = finalizeQueryParamChange(this, newState.handlerInfos, newState.queryParams, newTransition); - // if needs then initialize controllers proxy - get(this, 'controllers'); + 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; } + }, - this._super.apply(this, arguments); + // NOTE: this doesn't really belong here, but here + // it shall remain until our ES6 transpiler can + // handle cyclical deps. + transitionByIntent: function(intent, isIntermediate) { + try { + return getTransitionByIntent.apply(this, arguments); + } catch(e) { + return new Transition(this, intent, null, e); + } }, /** - @method controllerFor - @see {Ember.Route#controllerFor} - @deprecated Use `needs` instead + Clears the current and target route handlers and triggers exit + on each of them starting at the leaf and traversing up through + its ancestors. */ - controllerFor: function(controllerName) { - Ember.deprecate("Controller#controllerFor is deprecated, please use Controller#needs instead"); - return controllerFor(get(this, 'container'), controllerName); + reset: function() { + if (this.state) { + forEach(this.state.handlerInfos.slice().reverse(), function(handlerInfo) { + var handler = handlerInfo.handler; + callHook(handler, 'exit'); + }); + } + + this.state = new TransitionState(); + this.currentHandlerInfos = null; }, + activeTransition: null, + /** - Stores the instances of other controllers available from within - this controller. Any controller listed by name in the `needs` - property will be accessible by name through this property. + var handler = handlerInfo.handler; + The entry point for handling a change to the URL (usually + via the back and forward button). - ```javascript - App.CommentsController = Ember.ArrayController.extend({ - needs: ['post'], - postTitle: function(){ - var currentPost = this.get('controllers.post'); // instance of App.PostController - return currentPost.get('title'); - }.property('controllers.post.title') - }); - ``` + Returns an Array of handlers and the parameters associated + with those parameters. - @see {Ember.ControllerMixin#needs} - @property {Object} controllers - @default null + @param {String} url a URL to process + + @return {Array} an Array of `[handler, parameter]` tuples */ - controllers: defaultControllersComputedProperty - }); + handleURL: function(url) { + // Perform a URL-based transition, but don't change + // the URL afterward, since it already happened. + var args = slice.call(arguments); + if (url.charAt(0) !== '/') { args[0] = '/' + url; } - __exports__["default"] = ControllerMixin; - }); -define("ember-application", - ["ember-metal/core","ember-runtime/system/lazy_load","ember-application/system/dag","ember-application/system/resolver","ember-application/system/application","ember-application/ext/controller"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { - "use strict"; - var Ember = __dependency1__["default"]; - var runLoadHooks = __dependency2__.runLoadHooks; + return doTransition(this, args).method(null); + }, - /** - Ember Application + /** + Hook point for updating the URL. - @module ember - @submodule ember-application - @requires ember-views, ember-routing - */ + @param {String} url a URL to update to + */ + updateURL: function() { + throw new Error("updateURL is not implemented"); + }, - var DAG = __dependency3__["default"];var Resolver = __dependency4__.Resolver; - var DefaultResolver = __dependency4__.DefaultResolver; - var Application = __dependency5__["default"]; - // side effect of extending ControllerMixin + /** + Hook point for replacing the current URL, i.e. with replaceState - Ember.Application = Application; - Ember.DAG = DAG; - Ember.Resolver = Resolver; - Ember.DefaultResolver = DefaultResolver; + By default this behaves the same as `updateURL` - runLoadHooks('Ember.Application', Application); - }); -define("ember-application/system/application", - ["ember-metal","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/lazy_load","ember-application/system/dag","ember-runtime/system/namespace","ember-runtime/mixins/deferred","ember-application/system/resolver","ember-metal/platform","ember-metal/run_loop","ember-metal/utils","container/container","ember-runtime/controllers/controller","ember-metal/enumerable_utils","ember-runtime/controllers/object_controller","ember-runtime/controllers/array_controller","ember-views/system/event_dispatcher","ember-extension-support/container_debug_adapter","ember-views/system/jquery","ember-routing/system/route","ember-routing/system/router","ember-routing/location/hash_location","ember-routing/location/history_location","ember-routing/location/auto_location","ember-routing/location/none_location","ember-handlebars-compiler","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__, __dependency22__, __dependency23__, __dependency24__, __dependency25__, __dependency26__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-application - */ + @param {String} url a URL to update to + */ + replaceURL: function(url) { + this.updateURL(url); + }, - var Ember = __dependency1__["default"]; - // Ember.FEATURES, Ember.deprecate, Ember.assert, Ember.libraries, LOG_VERSION, Namespace, BOOTED - var get = __dependency2__.get; - var set = __dependency3__.set; - var runLoadHooks = __dependency4__.runLoadHooks; - var DAG = __dependency5__["default"];var Namespace = __dependency6__["default"]; - var DeferredMixin = __dependency7__["default"]; - var DefaultResolver = __dependency8__.DefaultResolver; - var create = __dependency9__.create; - var run = __dependency10__["default"]; - var canInvoke = __dependency11__.canInvoke; - var Container = __dependency12__["default"]; - var Controller = __dependency13__.Controller; - var EnumerableUtils = __dependency14__["default"]; - var ObjectController = __dependency15__["default"]; - var ArrayController = __dependency16__["default"]; - var EventDispatcher = __dependency17__["default"]; - var ContainerDebugAdapter = __dependency18__["default"]; - var jQuery = __dependency19__["default"]; - var Route = __dependency20__["default"]; - var Router = __dependency21__["default"]; - var HashLocation = __dependency22__["default"]; - var HistoryLocation = __dependency23__["default"]; - var AutoLocation = __dependency24__["default"]; - var NoneLocation = __dependency25__["default"]; + /** + Transition into the specified named route. - var EmberHandlebars = __dependency26__["default"]; + If necessary, trigger the exit callback on any handlers + that are no longer represented by the target route. - var K = Ember.K; + @param {String} name the name of the route + */ + transitionTo: function(name) { + return doTransition(this, arguments); + }, - function DeprecatedContainer(container) { - this._container = container; - } + intermediateTransitionTo: function(name) { + return doTransition(this, arguments, true); + }, - DeprecatedContainer.deprecate = function(method) { - return function() { - var container = this._container; + refresh: function(pivotHandler) { + var state = this.activeTransition ? this.activeTransition.state : this.state; + var handlerInfos = state.handlerInfos; + var params = {}; + for (var i = 0, len = handlerInfos.length; i < len; ++i) { + var handlerInfo = handlerInfos[i]; + params[handlerInfo.name] = handlerInfo.params || {}; + } - Ember.deprecate('Using the defaultContainer is no longer supported. [defaultContainer#' + method + '] see: http://git.io/EKPpnA', false); - return container[method].apply(container, arguments); - }; - }; + log(this, "Starting a refresh transition"); + var intent = new NamedTransitionIntent({ + name: handlerInfos[handlerInfos.length - 1].name, + pivotHandler: pivotHandler || handlerInfos[0].handler, + contexts: [], // TODO collect contexts...? + queryParams: this._changedQueryParams || state.queryParams || {} + }); - DeprecatedContainer.prototype = { - _container: null, - lookup: DeprecatedContainer.deprecate('lookup'), - resolve: DeprecatedContainer.deprecate('resolve'), - register: DeprecatedContainer.deprecate('register') - }; + return this.transitionByIntent(intent, false); + }, - /** - An instance of `Ember.Application` is the starting point for every Ember - application. It helps to instantiate, initialize and coordinate the many - objects that make up your app. + /** + Identical to `transitionTo` except that the current URL will be replaced + if possible. - Each Ember app has one and only one `Ember.Application` object. In fact, the - very first thing you should do in your application is create the instance: + This method is intended primarily for use with `replaceState`. - ```javascript - window.App = Ember.Application.create(); - ``` + @param {String} name the name of the route + */ + replaceWith: function(name) { + return doTransition(this, arguments).method('replace'); + }, - Typically, the application object is the only global variable. All other - classes in your app should be properties on the `Ember.Application` instance, - which highlights its first role: a global namespace. + /** + Take a named route and context objects and generate a + URL. - For example, if you define a view class, it might look like this: + @param {String} name the name of the route to generate + a URL for + @param {...Object} objects a list of objects to serialize - ```javascript - App.MyView = Ember.View.extend(); - ``` + @return {String} a URL + */ + generate: function(handlerName) { - By default, calling `Ember.Application.create()` will automatically initialize - your application by calling the `Ember.Application.initialize()` method. If - you need to delay initialization, you can call your app's `deferReadiness()` - method. When you are ready for your app to be initialized, call its - `advanceReadiness()` method. + var partitionedArgs = extractQueryParams(slice.call(arguments, 1)), + suppliedParams = partitionedArgs[0], + queryParams = partitionedArgs[1]; - You can define a `ready` method on the `Ember.Application` instance, which - will be run by Ember when the application is initialized. + // Construct a TransitionIntent with the provided params + // and apply it to the present state of the router. + var intent = new NamedTransitionIntent({ name: handlerName, contexts: suppliedParams }); + var state = intent.applyToState(this.state, this.recognizer, this.getHandler); + var params = {}; - Because `Ember.Application` inherits from `Ember.Namespace`, any classes - you create will have useful string representations when calling `toString()`. - See the `Ember.Namespace` documentation for more information. + for (var i = 0, len = state.handlerInfos.length; i < len; ++i) { + var handlerInfo = state.handlerInfos[i]; + var handlerParams = handlerInfo.serialize(); + merge(params, handlerParams); + } + params.queryParams = queryParams; - While you can think of your `Ember.Application` as a container that holds the - other classes in your application, there are several other responsibilities - going on under-the-hood that you may want to understand. + return this.recognizer.generate(handlerName, params); + }, - ### Event Delegation + applyIntent: function(handlerName, contexts) { + var intent = new NamedTransitionIntent({ + name: handlerName, + contexts: contexts + }); - Ember uses a technique called _event delegation_. This allows the framework - to set up a global, shared event listener instead of requiring each view to - do it manually. For example, instead of each view registering its own - `mousedown` listener on its associated element, Ember sets up a `mousedown` - listener on the `body`. + var state = this.activeTransition && this.activeTransition.state || this.state; + return intent.applyToState(state, this.recognizer, this.getHandler); + }, - If a `mousedown` event occurs, Ember will look at the target of the event and - start walking up the DOM node tree, finding corresponding views and invoking - their `mouseDown` method as it goes. + isActiveIntent: function(handlerName, contexts, queryParams) { + var targetHandlerInfos = this.state.handlerInfos, + found = false, names, object, handlerInfo, handlerObj, i, len; - `Ember.Application` has a number of default events that it listens for, as - well as a mapping from lowercase events to camel-cased view method names. For - example, the `keypress` event causes the `keyPress` method on the view to be - called, the `dblclick` event causes `doubleClick` to be called, and so on. + if (!targetHandlerInfos.length) { return false; } - If there is a bubbling browser event that Ember does not listen for by - default, you can specify custom events and their corresponding view method - names by setting the application's `customEvents` property: + var targetHandler = targetHandlerInfos[targetHandlerInfos.length - 1].name; + var recogHandlers = this.recognizer.handlersFor(targetHandler); - ```javascript - App = Ember.Application.create({ - customEvents: { - // add support for the paste event - paste: "paste" + var index = 0; + for (len = recogHandlers.length; index < len; ++index) { + handlerInfo = targetHandlerInfos[index]; + if (handlerInfo.name === handlerName) { break; } } - }); - ``` - - By default, the application sets up these event listeners on the document - body. However, in cases where you are embedding an Ember application inside - an existing page, you may want it to set up the listeners on an element - inside the body. - - For example, if only events inside a DOM element with the ID of `ember-app` - should be delegated, set your application's `rootElement` property: - ```javascript - window.App = Ember.Application.create({ - rootElement: '#ember-app' - }); - ``` + if (index === recogHandlers.length) { + // The provided route name isn't even in the route hierarchy. + return false; + } - The `rootElement` can be either a DOM element or a jQuery-compatible selector - string. Note that *views appended to the DOM outside the root element will - not receive events.* If you specify a custom root element, make sure you only - append views inside it! + var state = new TransitionState(); + state.handlerInfos = targetHandlerInfos.slice(0, index + 1); + recogHandlers = recogHandlers.slice(0, index + 1); - To learn more about the advantages of event delegation and the Ember view - layer, and a list of the event listeners that are setup by default, visit the - [Ember View Layer guide](http://emberjs.com/guides/understanding-ember/the-view-layer/#toc_event-delegation). + var intent = new NamedTransitionIntent({ + name: targetHandler, + contexts: contexts + }); - ### Initializers + var newState = intent.applyToHandlers(state, recogHandlers, this.getHandler, targetHandler, true, true); - Libraries on top of Ember can add initializers, like so: + var handlersEqual = handlerInfosEqual(newState.handlerInfos, state.handlerInfos); + if (!queryParams || !handlersEqual) { + return handlersEqual; + } - ```javascript - Ember.Application.initializer({ - name: 'api-adapter', + // Get a hash of QPs that will still be active on new route + var activeQPsOnNewHandler = {}; + merge(activeQPsOnNewHandler, queryParams); - initialize: function(container, application) { - application.register('api-adapter:main', ApiAdapter); + var activeQueryParams = this.state.queryParams; + for (var key in activeQueryParams) { + if (activeQueryParams.hasOwnProperty(key) && + activeQPsOnNewHandler.hasOwnProperty(key)) { + activeQPsOnNewHandler[key] = activeQueryParams[key]; + } } - }); - ``` - Initializers provide an opportunity to access the container, which - organizes the different components of an Ember application. Additionally - they provide a chance to access the instantiated application. Beyond - being used for libraries, initializers are also a great way to organize - dependency injection or setup in your own application. + return handlersEqual && !getChangelist(activeQPsOnNewHandler, queryParams); + }, - ### Routing + isActive: function(handlerName) { + var partitionedArgs = extractQueryParams(slice.call(arguments, 1)); + return this.isActiveIntent(handlerName, partitionedArgs[0], partitionedArgs[1]); + }, - In addition to creating your application's router, `Ember.Application` is - also responsible for telling the router when to start routing. Transitions - between routes can be logged with the `LOG_TRANSITIONS` flag, and more - detailed intra-transition logging can be logged with - the `LOG_TRANSITIONS_INTERNAL` flag: + trigger: function(name) { + var args = slice.call(arguments); + trigger(this, this.currentHandlerInfos, false, args); + }, - ```javascript - window.App = Ember.Application.create({ - LOG_TRANSITIONS: true, // basic logging of successful transitions - LOG_TRANSITIONS_INTERNAL: true // detailed logging of all routing steps - }); - ``` + /** + Hook point for logging transition status updates. - By default, the router will begin trying to translate the current URL into - application state once the browser emits the `DOMContentReady` event. If you - need to defer routing, you can call the application's `deferReadiness()` - method. Once routing can begin, call the `advanceReadiness()` method. + @param {String} message The message to log. + */ + log: null, - If there is any setup required before routing begins, you can implement a - `ready()` method on your app that will be invoked immediately before routing - begins. - ``` + _willChangeContextEvent: 'willChangeContext', + _triggerWillChangeContext: function(handlerInfos, newTransition) { + trigger(this, handlerInfos, true, [this._willChangeContextEvent, newTransition]); + }, - @class Application - @namespace Ember - @extends Ember.Namespace - */ + _triggerWillLeave: function(handlerInfos, newTransition, leavingChecker) { + trigger(this, handlerInfos, true, ['willLeave', newTransition, leavingChecker]); + } + }; - var Application = Namespace.extend(DeferredMixin, { + /** + @private - /** - The root DOM element of the Application. This can be specified as an - element or a - [jQuery-compatible selector string](http://api.jquery.com/category/selectors/). + Fires queryParamsDidChange event + */ + function fireQueryParamDidChange(router, newState, queryParamChangelist) { + // If queryParams changed trigger event + if (queryParamChangelist) { - This is the element that will be passed to the Application's, - `eventDispatcher`, which sets up the listeners for event delegation. Every - view in your application should be a child of the element you specify here. + // This is a little hacky but we need some way of storing + // changed query params given that no activeTransition + // is guaranteed to have occurred. + router._changedQueryParams = queryParamChangelist.all; + trigger(router, newState.handlerInfos, true, ['queryParamsDidChange', queryParamChangelist.changed, queryParamChangelist.all, queryParamChangelist.removed]); + router._changedQueryParams = null; + } + } - @property rootElement - @type DOMElement - @default 'body' - */ - rootElement: 'body', + /** + @private - /** - The `Ember.EventDispatcher` responsible for delegating events to this - application's views. + Takes an Array of `HandlerInfo`s, figures out which ones are + exiting, entering, or changing contexts, and calls the + proper handler hooks. - The event dispatcher is created by the application at initialization time - and sets up event listeners on the DOM element described by the - application's `rootElement` property. + For example, consider the following tree of handlers. Each handler is + followed by the URL segment it handles. - See the documentation for `Ember.EventDispatcher` for more information. + ``` + |~index ("/") + | |~posts ("/posts") + | | |-showPost ("/:id") + | | |-newPost ("/new") + | | |-editPost ("/edit") + | |~about ("/about/:id") + ``` - @property eventDispatcher - @type Ember.EventDispatcher - @default null - */ - eventDispatcher: null, + Consider the following transitions: - /** - The DOM events for which the event dispatcher should listen. + 1. A URL transition to `/posts/1`. + 1. Triggers the `*model` callbacks on the + `index`, `posts`, and `showPost` handlers + 2. Triggers the `enter` callback on the same + 3. Triggers the `setup` callback on the same + 2. A direct transition to `newPost` + 1. Triggers the `exit` callback on `showPost` + 2. Triggers the `enter` callback on `newPost` + 3. Triggers the `setup` callback on `newPost` + 3. A direct transition to `about` with a specified + context object + 1. Triggers the `exit` callback on `newPost` + and `posts` + 2. Triggers the `serialize` callback on `about` + 3. Triggers the `enter` callback on `about` + 4. Triggers the `setup` callback on `about` - By default, the application's `Ember.EventDispatcher` listens - for a set of standard DOM events, such as `mousedown` and - `keyup`, and delegates them to your application's `Ember.View` - instances. + @param {Router} transition + @param {TransitionState} newState + */ + function setupContexts(router, newState, transition) { + var partition = partitionHandlers(router.state, newState); - If you would like additional bubbling events to be delegated to your - views, set your `Ember.Application`'s `customEvents` property - to a hash containing the DOM event name as the key and the - corresponding view method name as the value. For example: + forEach(partition.exited, function(handlerInfo) { + var handler = handlerInfo.handler; + delete handler.context; - ```javascript - App = Ember.Application.create({ - customEvents: { - // add support for the paste event - paste: "paste" - } - }); - ``` + callHook(handler, 'reset', true, transition); + callHook(handler, 'exit', transition); + }); - @property customEvents - @type Object - @default null - */ - customEvents: null, + var oldState = router.oldState = router.state; + router.state = newState; + var currentHandlerInfos = router.currentHandlerInfos = partition.unchanged.slice(); - // Start off the number of deferrals at 1. This will be - // decremented by the Application's own `initialize` method. - _readinessDeferrals: 1, + try { + forEach(partition.reset, function(handlerInfo) { + var handler = handlerInfo.handler; + callHook(handler, 'reset', false, transition); + }); - init: function() { - if (!this.$) { this.$ = jQuery; } - this.__container__ = this.buildContainer(); + forEach(partition.updatedContext, function(handlerInfo) { + return handlerEnteredOrUpdated(currentHandlerInfos, handlerInfo, false, transition); + }); - this.Router = this.defaultRouter(); + forEach(partition.entered, function(handlerInfo) { + return handlerEnteredOrUpdated(currentHandlerInfos, handlerInfo, true, transition); + }); + } catch(e) { + router.state = oldState; + router.currentHandlerInfos = oldState.handlerInfos; + throw e; + } - this._super(); + router.state.queryParams = finalizeQueryParamChange(router, currentHandlerInfos, newState.queryParams, transition); + } - this.scheduleInitialize(); - Ember.libraries.registerCoreLibrary('Handlebars', EmberHandlebars.VERSION); - Ember.libraries.registerCoreLibrary('jQuery', jQuery().jquery); + /** + @private - if ( Ember.LOG_VERSION ) { - Ember.LOG_VERSION = false; // we only need to see this once per Application#init + Helper method used by setupContexts. Handles errors or redirects + that may happen in enter/setup. + */ + function handlerEnteredOrUpdated(currentHandlerInfos, handlerInfo, enter, transition) { - var nameLengths = EnumerableUtils.map(Ember.libraries, function(item) { - return get(item, "name.length"); - }); + var handler = handlerInfo.handler, + context = handlerInfo.context; - var maxNameLength = Math.max.apply(this, nameLengths); + if (enter) { + callHook(handler, 'enter', transition); + } + if (transition && transition.isAborted) { + throw new TransitionAborted(); + } - Ember.debug('-------------------------------'); - Ember.libraries.each(function(name, version) { - var spaces = new Array(maxNameLength - name.length + 1).join(" "); - Ember.debug([name, spaces, ' : ', version].join("")); - }); - Ember.debug('-------------------------------'); - } - }, + handler.context = context; + callHook(handler, 'contextDidChange'); - /** - Build the container for the current application. + callHook(handler, 'setup', context, transition); + if (transition && transition.isAborted) { + throw new TransitionAborted(); + } - Also register a default application view in case the application - itself does not. + currentHandlerInfos.push(handlerInfo); - @private - @method buildContainer - @return {Ember.Container} the configured container - */ - buildContainer: function() { - var container = this.__container__ = Application.buildContainer(this); + return true; + } - return container; - }, - /** - If the application has not opted out of routing and has not explicitly - defined a router, supply a default router for the application author - to configure. + /** + @private - This allows application developers to do: + This function is called when transitioning from one URL to + another to determine which handlers are no longer active, + which handlers are newly active, and which handlers remain + active but have their context changed. - ```javascript - var App = Ember.Application.create(); + Take a list of old handlers and new handlers and partition + them into four buckets: - App.Router.map(function() { - this.resource('posts'); - }); - ``` + * unchanged: the handler was active in both the old and + new URL, and its context remains the same + * updated context: the handler was active in both the + old and new URL, but its context changed. The handler's + `setup` method, if any, will be called with the new + context. + * exited: the handler was active in the old URL, but is + no longer active. + * entered: the handler was not active in the old URL, but + is now active. - @private - @method defaultRouter - @return {Ember.Router} the default router - */ + The PartitionedHandlers structure has four fields: - defaultRouter: function() { - if (this.Router === false) { return; } - var container = this.__container__; + * `updatedContext`: a list of `HandlerInfo` objects that + represent handlers that remain active but have a changed + context + * `entered`: a list of `HandlerInfo` objects that represent + handlers that are newly active + * `exited`: a list of `HandlerInfo` objects that are no + longer active. + * `unchanged`: a list of `HanderInfo` objects that remain active. - if (this.Router) { - container.unregister('router:main'); - container.register('router:main', this.Router); - } + @param {Array[HandlerInfo]} oldHandlers a list of the handler + information for the previous URL (or `[]` if this is the + first handled transition) + @param {Array[HandlerInfo]} newHandlers a list of the handler + information for the new URL - return container.lookupFactory('router:main'); - }, + @return {Partition} + */ + function partitionHandlers(oldState, newState) { + var oldHandlers = oldState.handlerInfos; + var newHandlers = newState.handlerInfos; - /** - Automatically initialize the application once the DOM has - become ready. + var handlers = { + updatedContext: [], + exited: [], + entered: [], + unchanged: [] + }; - The initialization itself is scheduled on the actions queue - which ensures that application loading finishes before - booting. + var handlerChanged, contextChanged = false, i, l; - If you are asynchronously loading code, you should call - `deferReadiness()` to defer booting, and then call - `advanceReadiness()` once all of your code has finished - loading. + for (i=0, l=newHandlers.length; i 0); - this._readinessDeferrals++; - }, + var router = transition.router, + handlerInfos = state.handlerInfos, + handlerName = handlerInfos[handlerInfos.length - 1].name, + params = {}; - /** - Call `advanceReadiness` after any asynchronous setup logic has completed. - Each call to `deferReadiness` must be matched by a call to `advanceReadiness` - or the application will never become ready and routing will not begin. + for (var i = handlerInfos.length - 1; i >= 0; --i) { + var handlerInfo = handlerInfos[i]; + merge(params, handlerInfo.params); + if (handlerInfo.handler.inaccessibleByURL) { + urlMethod = null; + } + } - @method advanceReadiness - @see {Ember.Application#deferReadiness} - */ - advanceReadiness: function() { - Ember.assert("You must call advanceReadiness on an instance of Ember.Application", this instanceof Application); - this._readinessDeferrals--; + if (urlMethod) { + params.queryParams = transition._visibleQueryParams || state.queryParams; + var url = router.recognizer.generate(handlerName, params); - if (this._readinessDeferrals === 0) { - run.once(this, this.didBecomeReady); + if (urlMethod === 'replace') { + router.replaceURL(url); + } else { + router.updateURL(url); } - }, + } + } - /** - Registers a factory that can be used for dependency injection (with - `App.inject`) or for service lookup. Each factory is registered with - a full name including two parts: `type:name`. + /** + @private - A simple example: + Updates the URL (if necessary) and calls `setupContexts` + to update the router's array of `currentHandlerInfos`. + */ + function finalizeTransition(transition, newState) { - ```javascript - var App = Ember.Application.create(); - App.Orange = Ember.Object.extend(); - App.register('fruit:favorite', App.Orange); - ``` + try { + log(transition.router, transition.sequence, "Resolved all models on destination route; finalizing transition."); - Ember will resolve factories from the `App` namespace automatically. - For example `App.CarsController` will be discovered and returned if - an application requests `controller:cars`. + var router = transition.router, + handlerInfos = newState.handlerInfos, + seq = transition.sequence; - An example of registering a controller with a non-standard name: + // Run all the necessary enter/setup/exit hooks + setupContexts(router, newState, transition); - ```javascript - var App = Ember.Application.create(), - Session = Ember.Controller.extend(); + // Check if a redirect occurred in enter/setup + if (transition.isAborted) { + // TODO: cleaner way? distinguish b/w targetHandlerInfos? + router.state.handlerInfos = router.currentHandlerInfos; + return Promise.reject(logAbort(transition)); + } - App.register('controller:session', Session); + updateURL(transition, newState, transition.intent.url); - // The Session controller can now be treated like a normal controller, - // despite its non-standard name. - App.ApplicationController = Ember.Controller.extend({ - needs: ['session'] - }); - ``` + transition.isActive = false; + router.activeTransition = null; - Registered factories are **instantiated** by having `create` - called on them. Additionally they are **singletons**, each time - they are looked up they return the same instance. + trigger(router, router.currentHandlerInfos, true, ['didTransition']); - Some examples modifying that default behavior: + if (router.didTransition) { + router.didTransition(router.currentHandlerInfos); + } - ```javascript - var App = Ember.Application.create(); + log(router, transition.sequence, "TRANSITION COMPLETE."); - App.Person = Ember.Object.extend(); - App.Orange = Ember.Object.extend(); - App.Email = Ember.Object.extend(); - App.session = Ember.Object.create(); + // Resolve with the final handler. + return handlerInfos[handlerInfos.length - 1].handler; + } catch(e) { + if (!((e instanceof TransitionAborted))) { + //var erroneousHandler = handlerInfos.pop(); + var infos = transition.state.handlerInfos; + transition.trigger(true, 'error', e, transition, infos[infos.length-1].handler); + transition.abort(); + } - App.register('model:user', App.Person, {singleton: false }); - App.register('fruit:favorite', App.Orange); - App.register('communication:main', App.Email, {singleton: false}); - App.register('session', App.session, {instantiate: false}); - ``` + throw e; + } + } - @method register - @param fullName {String} type:name (e.g., 'model:user') - @param factory {Function} (e.g., App.Person) - @param options {Object} (optional) disable instantiation or singleton usage - **/ - register: function() { - var container = this.__container__; - container.register.apply(container, arguments); - }, + /** + @private - /** - Define a dependency injection onto a specific factory or all factories - of a type. + Begins and returns a Transition based on the provided + arguments. Accepts arguments in the form of both URL + transitions and named transitions. - When Ember instantiates a controller, view, or other framework component - it can attach a dependency to that component. This is often used to - provide services to a set of framework components. + @param {Router} router + @param {Array[Object]} args arguments passed to transitionTo, + replaceWith, or handleURL + */ + function doTransition(router, args, isIntermediate) { + // Normalize blank transitions to root URL transitions. + var name = args[0] || '/'; - An example of providing a session object to all controllers: + var lastArg = args[args.length-1]; + var queryParams = {}; + if (lastArg && lastArg.hasOwnProperty('queryParams')) { + queryParams = pop.call(args).queryParams; + } - ```javascript - var App = Ember.Application.create(), - Session = Ember.Object.extend({ isAuthenticated: false }); + var intent; + if (args.length === 0) { - // A factory must be registered before it can be injected - App.register('session:main', Session); + log(router, "Updating query params"); - // Inject 'session:main' onto all factories of the type 'controller' - // with the name 'session' - App.inject('controller', 'session', 'session:main'); + // A query param update is really just a transition + // into the route you're already on. + var handlerInfos = router.state.handlerInfos; + intent = new NamedTransitionIntent({ + name: handlerInfos[handlerInfos.length - 1].name, + contexts: [], + queryParams: queryParams + }); - App.IndexController = Ember.Controller.extend({ - isLoggedIn: Ember.computed.alias('session.isAuthenticated') + } else if (name.charAt(0) === '/') { + + log(router, "Attempting URL transition to " + name); + intent = new URLTransitionIntent({ url: name }); + + } else { + + log(router, "Attempting transition to " + name); + intent = new NamedTransitionIntent({ + name: args[0], + contexts: slice.call(args, 1), + queryParams: queryParams }); - ``` + } - Injections can also be performed on specific factories. + return router.transitionByIntent(intent, isIntermediate); + } - ```javascript - App.inject(, , ) - App.inject('route', 'source', 'source:main') - App.inject('route:application', 'email', 'model:email') - ``` + function handlerInfosEqual(handlerInfos, otherHandlerInfos) { + if (handlerInfos.length !== otherHandlerInfos.length) { + return false; + } - It is important to note that injections can only be performed on - classes that are instantiated by Ember itself. Instantiating a class - directly (via `create` or `new`) bypasses the dependency injection - system. + for (var i = 0, len = handlerInfos.length; i < len; ++i) { + if (handlerInfos[i] !== otherHandlerInfos[i]) { + return false; + } + } + return true; + } - Ember-Data instantiates its models in a unique manner, and consequently - injections onto models (or all models) will not work as expected. Injections - on models can be enabled by setting `Ember.MODEL_FACTORY_INJECTIONS` - to `true`. + function finalizeQueryParamChange(router, resolvedHandlers, newQueryParams, transition) { + // We fire a finalizeQueryParamChange event which + // gives the new route hierarchy a chance to tell + // us which query params it's consuming and what + // their final values are. If a query param is + // no longer consumed in the final route hierarchy, + // its serialized segment will be removed + // from the URL. - @method inject - @param factoryNameOrType {String} - @param property {String} - @param injectionName {String} - **/ - inject: function() { - var container = this.__container__; - container.injection.apply(container, arguments); - }, + for (var k in newQueryParams) { + if (newQueryParams.hasOwnProperty(k) && + newQueryParams[k] === null) { + delete newQueryParams[k]; + } + } - /** - Calling initialize manually is not supported. + var finalQueryParamsArray = []; + trigger(router, resolvedHandlers, true, ['finalizeQueryParamChange', newQueryParams, finalQueryParamsArray, transition]); - Please see Ember.Application#advanceReadiness and - Ember.Application#deferReadiness. + if (transition) { + transition._visibleQueryParams = {}; + } - @private - @deprecated - @method initialize - **/ - initialize: function() { - Ember.deprecate('Calling initialize manually is not supported. Please see Ember.Application#advanceReadiness and Ember.Application#deferReadiness'); - }, + var finalQueryParams = {}; + for (var i = 0, len = finalQueryParamsArray.length; i < len; ++i) { + var qp = finalQueryParamsArray[i]; + finalQueryParams[qp.key] = qp.value; + if (transition && qp.visible !== false) { + transition._visibleQueryParams[qp.key] = qp.value; + } + } + return finalQueryParams; + } - /** - Initialize the application. This happens automatically. + function notifyExistingHandlers(router, newState, newTransition) { + var oldHandlers = router.state.handlerInfos, + changing = [], + leavingIndex = null, + leaving, leavingChecker, i, oldHandlerLen, oldHandler, newHandler; - Run any initializers and run the application load hook. These hooks may - choose to defer readiness. For example, an authentication hook might want - to defer readiness until the auth token has been retrieved. + oldHandlerLen = oldHandlers.length; + for (i = 0; i < oldHandlerLen; i++) { + oldHandler = oldHandlers[i]; + newHandler = newState.handlerInfos[i]; - @private - @method _initialize - */ - _initialize: function() { - if (this.isDestroyed) { return; } + if (!newHandler || oldHandler.name !== newHandler.name) { + leavingIndex = i; + break; + } - // At this point, the App.Router must already be assigned - if (this.Router) { - var container = this.__container__; - container.unregister('router:main'); - container.register('router:main', this.Router); + if (!newHandler.isResolved) { + changing.push(oldHandler); } + } - this.runInitializers(); - runLoadHooks('application', this); + if (leavingIndex !== null) { + leaving = oldHandlers.slice(leavingIndex, oldHandlerLen); + leavingChecker = function(name) { + for (var h = 0, len = leaving.length; h < len; h++) { + if (leaving[h].name === name) { + return true; + } + } + return false; + }; - // At this point, any initializers or load hooks that would have wanted - // to defer readiness have fired. In general, advancing readiness here - // will proceed to didBecomeReady. - this.advanceReadiness(); + router._triggerWillLeave(leaving, newTransition, leavingChecker); + } - return this; - }, + if (changing.length > 0) { + router._triggerWillChangeContext(changing, newTransition); + } - /** - Reset the application. This is typically used only in tests. It cleans up - the application in the following order: + trigger(router, oldHandlers, true, ['willTransition', newTransition]); + } - 1. Deactivate existing routes - 2. Destroy all objects in the container - 3. Create a new application container - 4. Re-route to the existing url + __exports__["default"] = Router; + }); +enifed("router/transition-intent", + ["./utils","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var merge = __dependency1__.merge; - Typical Example: + function TransitionIntent(props) { + this.initialize(props); - ```javascript + // TODO: wat + this.data = this.data || {}; + } - var App; + TransitionIntent.prototype = { + initialize: null, + applyToState: null + }; - run(function() { - App = Ember.Application.create(); - }); + __exports__["default"] = TransitionIntent; + }); +enifed("router/transition-intent/named-transition-intent", + ["../transition-intent","../transition-state","../handler-info/factory","../utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + var TransitionIntent = __dependency1__["default"]; + var TransitionState = __dependency2__["default"]; + var handlerInfoFactory = __dependency3__["default"]; + var isParam = __dependency4__.isParam; + var extractQueryParams = __dependency4__.extractQueryParams; + var merge = __dependency4__.merge; + var subclass = __dependency4__.subclass; - module("acceptance test", { - setup: function() { - App.reset(); - } - }); + __exports__["default"] = subclass(TransitionIntent, { + name: null, + pivotHandler: null, + contexts: null, + queryParams: null, - test("first test", function() { - // App is freshly reset - }); + initialize: function(props) { + this.name = props.name; + this.pivotHandler = props.pivotHandler; + this.contexts = props.contexts || []; + this.queryParams = props.queryParams; + }, - test("first test", function() { - // App is again freshly reset - }); - ``` + applyToState: function(oldState, recognizer, getHandler, isIntermediate) { - Advanced Example: + var partitionedArgs = extractQueryParams([this.name].concat(this.contexts)), + pureArgs = partitionedArgs[0], + queryParams = partitionedArgs[1], + handlers = recognizer.handlersFor(pureArgs[0]); - Occasionally you may want to prevent the app from initializing during - setup. This could enable extra configuration, or enable asserting prior - to the app becoming ready. + var targetRouteName = handlers[handlers.length-1].handler; - ```javascript + return this.applyToHandlers(oldState, handlers, getHandler, targetRouteName, isIntermediate); + }, - var App; + applyToHandlers: function(oldState, handlers, getHandler, targetRouteName, isIntermediate, checkingIfActive) { - run(function() { - App = Ember.Application.create(); - }); + var i, len; + var newState = new TransitionState(); + var objects = this.contexts.slice(0); - module("acceptance test", { - setup: function() { - run(function() { - App.reset(); - App.deferReadiness(); - }); + var invalidateIndex = handlers.length; + + // Pivot handlers are provided for refresh transitions + if (this.pivotHandler) { + for (i = 0, len = handlers.length; i < len; ++i) { + if (getHandler(handlers[i].handler) === this.pivotHandler) { + invalidateIndex = i; + break; + } } - }); + } - test("first test", function() { - ok(true, 'something before app is initialized'); + var pivotHandlerFound = !this.pivotHandler; - run(function() { - App.advanceReadiness(); - }); - ok(true, 'something after app is initialized'); - }); - ``` + for (i = handlers.length - 1; i >= 0; --i) { + var result = handlers[i]; + var name = result.handler; + var handler = getHandler(name); - @method reset - **/ - reset: function() { - this._readinessDeferrals = 1; + var oldHandlerInfo = oldState.handlerInfos[i]; + var newHandlerInfo = null; - function handleReset() { - var router = this.__container__.lookup('router:main'); - router.reset(); + if (result.names.length > 0) { + if (i >= invalidateIndex) { + newHandlerInfo = this.createParamHandlerInfo(name, handler, result.names, objects, oldHandlerInfo); + } else { + newHandlerInfo = this.getHandlerInfoForDynamicSegment(name, handler, result.names, objects, oldHandlerInfo, targetRouteName, i); + } + } else { + // This route has no dynamic segment. + // Therefore treat as a param-based handlerInfo + // with empty params. This will cause the `model` + // hook to be called with empty params, which is desirable. + newHandlerInfo = this.createParamHandlerInfo(name, handler, result.names, objects, oldHandlerInfo); + } - run(this.__container__, 'destroy'); + if (checkingIfActive) { + // If we're performing an isActive check, we want to + // serialize URL params with the provided context, but + // ignore mismatches between old and new context. + newHandlerInfo = newHandlerInfo.becomeResolved(null, newHandlerInfo.context); + var oldContext = oldHandlerInfo && oldHandlerInfo.context; + if (result.names.length > 0 && newHandlerInfo.context === oldContext) { + // If contexts match in isActive test, assume params also match. + // This allows for flexibility in not requiring that every last + // handler provide a `serialize` method + newHandlerInfo.params = oldHandlerInfo && oldHandlerInfo.params; + } + newHandlerInfo.context = oldContext; + } - this.buildContainer(); + var handlerToUse = oldHandlerInfo; + if (i >= invalidateIndex || newHandlerInfo.shouldSupercede(oldHandlerInfo)) { + invalidateIndex = Math.min(i, invalidateIndex); + handlerToUse = newHandlerInfo; + } - run.schedule('actions', this, function() { - this._initialize(); - }); + if (isIntermediate && !checkingIfActive) { + handlerToUse = handlerToUse.becomeResolved(null, handlerToUse.context); + } + + newState.handlerInfos.unshift(handlerToUse); } - run.join(this, handleReset); - }, + if (objects.length > 0) { + throw new Error("More context objects were passed than there are dynamic segments for the route: " + targetRouteName); + } - /** - @private - @method runInitializers - */ - runInitializers: function() { - var initializers = get(this.constructor, 'initializers'), - container = this.__container__, - graph = new DAG(), - namespace = this, - name, initializer; - - for (name in initializers) { - initializer = initializers[name]; - graph.addEdges(initializer.name, initializer.initialize, initializer.before, initializer.after); + if (!isIntermediate) { + this.invalidateChildren(newState.handlerInfos, invalidateIndex); } - graph.topsort(function (vertex) { - var initializer = vertex.value; - Ember.assert("No application initializer named '"+vertex.name+"'", initializer); - initializer(container, namespace); - }); - }, + merge(newState.queryParams, this.queryParams || {}); - /** - @private - @method didBecomeReady - */ - didBecomeReady: function() { - this.setupEventDispatcher(); - this.ready(); // user hook - this.startRouting(); + return newState; + }, - if (!Ember.testing) { - // Eagerly name all classes that are already loaded - Ember.Namespace.processAll(); - Ember.BOOTED = true; + invalidateChildren: function(handlerInfos, invalidateIndex) { + for (var i = invalidateIndex, l = handlerInfos.length; i < l; ++i) { + var handlerInfo = handlerInfos[i]; + handlerInfos[i] = handlerInfos[i].getUnresolved(); } - - this.resolve(this); }, - /** - Setup up the event dispatcher to receive events on the - application's `rootElement` with any registered - `customEvents`. - - @private - @method setupEventDispatcher - */ - setupEventDispatcher: function() { - var customEvents = get(this, 'customEvents'), - rootElement = get(this, 'rootElement'), - dispatcher = this.__container__.lookup('event_dispatcher:main'); - - set(this, 'eventDispatcher', dispatcher); - dispatcher.setup(customEvents, rootElement); - }, + getHandlerInfoForDynamicSegment: function(name, handler, names, objects, oldHandlerInfo, targetRouteName, i) { - /** - If the application has a router, use it to route to the current URL, and - trigger a new call to `route` whenever the URL changes. + var numNames = names.length; + var objectToUse; + if (objects.length > 0) { - @private - @method startRouting - @property router {Ember.Router} - */ - startRouting: function() { - var router = this.__container__.lookup('router:main'); - if (!router) { return; } + // Use the objects provided for this transition. + objectToUse = objects[objects.length - 1]; + if (isParam(objectToUse)) { + return this.createParamHandlerInfo(name, handler, names, objects, oldHandlerInfo); + } else { + objects.pop(); + } + } else if (oldHandlerInfo && oldHandlerInfo.name === name) { + // Reuse the matching oldHandlerInfo + return oldHandlerInfo; + } else { + if (this.preTransitionState) { + var preTransitionHandlerInfo = this.preTransitionState.handlerInfos[i]; + objectToUse = preTransitionHandlerInfo && preTransitionHandlerInfo.context; + } else { + // Ideally we should throw this error to provide maximal + // information to the user that not enough context objects + // were provided, but this proves too cumbersome in Ember + // in cases where inner template helpers are evaluated + // before parent helpers un-render, in which cases this + // error somewhat prematurely fires. + //throw new Error("Not enough context objects were provided to complete a transition to " + targetRouteName + ". Specifically, the " + name + " route needs an object that can be serialized into its dynamic URL segments [" + names.join(', ') + "]"); + return oldHandlerInfo; + } + } - router.startRouting(); + return handlerInfoFactory('object', { + name: name, + handler: handler, + context: objectToUse, + names: names + }); }, - handleURL: function(url) { - var router = this.__container__.lookup('router:main'); - - router.handleURL(url); - }, + createParamHandlerInfo: function(name, handler, names, objects, oldHandlerInfo) { + var params = {}; - /** - Called when the Application has become ready. - The call will be delayed until the DOM has become ready. + // Soak up all the provided string/numbers + var numNames = names.length; + while (numNames--) { - @event ready - */ - ready: K, + // Only use old params if the names match with the new handler + var oldParams = (oldHandlerInfo && name === oldHandlerInfo.name && oldHandlerInfo.params) || {}; - /** - @deprecated Use 'Resolver' instead - Set this to provide an alternate class to `Ember.DefaultResolver` + var peek = objects[objects.length - 1]; + var paramName = names[numNames]; + if (isParam(peek)) { + params[paramName] = "" + objects.pop(); + } else { + // If we're here, this means only some of the params + // were string/number params, so try and use a param + // value from a previous handler. + if (oldParams.hasOwnProperty(paramName)) { + params[paramName] = oldParams[paramName]; + } else { + throw new Error("You didn't provide enough string/numeric parameters to satisfy all of the dynamic segments for route " + name); + } + } + } + return handlerInfoFactory('param', { + name: name, + handler: handler, + params: params + }); + } + }); + }); +enifed("router/transition-intent/url-transition-intent", + ["../transition-intent","../transition-state","../handler-info/factory","../utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + var TransitionIntent = __dependency1__["default"]; + var TransitionState = __dependency2__["default"]; + var handlerInfoFactory = __dependency3__["default"]; + var oCreate = __dependency4__.oCreate; + var merge = __dependency4__.merge; + var subclass = __dependency4__.subclass; - @property resolver - */ - resolver: null, + __exports__["default"] = subclass(TransitionIntent, { + url: null, - /** - Set this to provide an alternate class to `Ember.DefaultResolver` + initialize: function(props) { + this.url = props.url; + }, - @property resolver - */ - Resolver: null, + applyToState: function(oldState, recognizer, getHandler) { + var newState = new TransitionState(); - willDestroy: function() { - Ember.BOOTED = false; - // Ensure deactivation of routes before objects are destroyed - this.__container__.lookup('router:main').reset(); + var results = recognizer.recognize(this.url), + queryParams = {}, + i, len; - this.__container__.destroy(); - }, + if (!results) { + throw new UnrecognizedURLError(this.url); + } - initializer: function(options) { - this.constructor.initializer(options); - } - }); + var statesDiffer = false; - Application.reopenClass({ - initializers: {}, + for (i = 0, len = results.length; i < len; ++i) { + var result = results[i]; + var name = result.handler; + var handler = getHandler(name); - /** - Initializer receives an object which has the following attributes: - `name`, `before`, `after`, `initialize`. The only required attribute is - `initialize, all others are optional. + if (handler.inaccessibleByURL) { + throw new UnrecognizedURLError(this.url); + } - * `name` allows you to specify under which name the initializer is registered. - This must be a unique name, as trying to register two initializers with the - same name will result in an error. + var newHandlerInfo = handlerInfoFactory('param', { + name: name, + handler: handler, + params: result.params + }); - ```javascript - Ember.Application.initializer({ - name: 'namedInitializer', - initialize: function(container, application) { - Ember.debug("Running namedInitializer!"); + var oldHandlerInfo = oldState.handlerInfos[i]; + if (statesDiffer || newHandlerInfo.shouldSupercede(oldHandlerInfo)) { + statesDiffer = true; + newState.handlerInfos[i] = newHandlerInfo; + } else { + newState.handlerInfos[i] = oldHandlerInfo; } - }); - ``` - - * `before` and `after` are used to ensure that this initializer is ran prior - or after the one identified by the value. This value can be a single string - or an array of strings, referencing the `name` of other initializers. + } - An example of ordering initializers, we create an initializer named `first`: + merge(newState.queryParams, results.queryParams); - ```javascript - Ember.Application.initializer({ - name: 'first', - initialize: function(container, application) { - Ember.debug("First initializer!"); - } - }); + return newState; + } + }); - // DEBUG: First initializer! - ``` + /** + Promise reject reasons passed to promise rejection + handlers for failed transitions. + */ + function UnrecognizedURLError(message) { + this.message = (message || "UnrecognizedURLError"); + this.name = "UnrecognizedURLError"; + } + }); +enifed("router/transition-state", + ["./handler-info","./utils","rsvp/promise","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var ResolvedHandlerInfo = __dependency1__.ResolvedHandlerInfo; + var forEach = __dependency2__.forEach; + var promiseLabel = __dependency2__.promiseLabel; + var callHook = __dependency2__.callHook; + var Promise = __dependency3__["default"]; - We add another initializer named `second`, specifying that it should run - after the initializer named `first`: + function TransitionState(other) { + this.handlerInfos = []; + this.queryParams = {}; + this.params = {}; + } - ```javascript - Ember.Application.initializer({ - name: 'second', - after: 'first', + TransitionState.prototype = { + handlerInfos: null, + queryParams: null, + params: null, - initialize: function(container, application) { - Ember.debug("Second initializer!"); + promiseLabel: function(label) { + var targetName = ''; + forEach(this.handlerInfos, function(handlerInfo) { + if (targetName !== '') { + targetName += '.'; } + targetName += handlerInfo.name; }); + return promiseLabel("'" + targetName + "': " + label); + }, - // DEBUG: First initializer! - // DEBUG: Second initializer! - ``` - - Afterwards we add a further initializer named `pre`, this time specifying - that it should run before the initializer named `first`: - - ```javascript - Ember.Application.initializer({ - name: 'pre', - before: 'first', - - initialize: function(container, application) { - Ember.debug("Pre initializer!"); - } + resolve: function(shouldContinue, payload) { + var self = this; + // First, calculate params for this state. This is useful + // information to provide to the various route hooks. + var params = this.params; + forEach(this.handlerInfos, function(handlerInfo) { + params[handlerInfo.name] = handlerInfo.params || {}; }); - // DEBUG: Pre initializer! - // DEBUG: First initializer! - // DEBUG: Second initializer! - ``` - - Finally we add an initializer named `post`, specifying it should run after - both the `first` and the `second` initializers: + payload = payload || {}; + payload.resolveIndex = 0; - ```javascript - Ember.Application.initializer({ - name: 'post', - after: ['first', 'second'], + var currentState = this; + var wasAborted = false; - initialize: function(container, application) { - Ember.debug("Post initializer!"); - } - }); + // The prelude RSVP.resolve() asyncs us into the promise land. + return Promise.resolve(null, this.promiseLabel("Start transition")) + .then(resolveOneHandlerInfo, null, this.promiseLabel('Resolve handler'))['catch'](handleError, this.promiseLabel('Handle error')); - // DEBUG: Pre initializer! - // DEBUG: First initializer! - // DEBUG: Second initializer! - // DEBUG: Post initializer! - ``` + function innerShouldContinue() { + return Promise.resolve(shouldContinue(), currentState.promiseLabel("Check if should continue"))['catch'](function(reason) { + // We distinguish between errors that occurred + // during resolution (e.g. beforeModel/model/afterModel), + // and aborts due to a rejecting promise from shouldContinue(). + wasAborted = true; + return Promise.reject(reason); + }, currentState.promiseLabel("Handle abort")); + } - * `initialize` is a callback function that receives two arguments, `container` - and `application` on which you can operate. + function handleError(error) { + // This is the only possible + // reject value of TransitionState#resolve + var handlerInfos = currentState.handlerInfos; + var errorHandlerIndex = payload.resolveIndex >= handlerInfos.length ? + handlerInfos.length - 1 : payload.resolveIndex; + return Promise.reject({ + error: error, + handlerWithError: currentState.handlerInfos[errorHandlerIndex].handler, + wasAborted: wasAborted, + state: currentState + }); + } - Example of using `container` to preload data into the store: + function proceed(resolvedHandlerInfo) { + var wasAlreadyResolved = currentState.handlerInfos[payload.resolveIndex].isResolved; - ```javascript - Ember.Application.initializer({ - name: "preload-data", + // Swap the previously unresolved handlerInfo with + // the resolved handlerInfo + currentState.handlerInfos[payload.resolveIndex++] = resolvedHandlerInfo; - initialize: function(container, application) { - var store = container.lookup('store:main'); - store.pushPayload(preloadedData); + if (!wasAlreadyResolved) { + // Call the redirect hook. The reason we call it here + // vs. afterModel is so that redirects into child + // routes don't re-run the model hooks for this + // already-resolved route. + var handler = resolvedHandlerInfo.handler; + callHook(handler, 'redirect', resolvedHandlerInfo.context, payload); } - }); - ``` - - Example of using `application` to register an adapter: - ```javascript - Ember.Application.initializer({ - name: 'api-adapter', + // Proceed after ensuring that the redirect hook + // didn't abort this transition by transitioning elsewhere. + return innerShouldContinue().then(resolveOneHandlerInfo, null, currentState.promiseLabel('Resolve handler')); + } - initialize: function(container, application) { - application.register('api-adapter:main', ApiAdapter); + function resolveOneHandlerInfo() { + if (payload.resolveIndex === currentState.handlerInfos.length) { + // This is is the only possible + // fulfill value of TransitionState#resolve + return { + error: null, + state: currentState + }; } - }); - ``` - @method initializer - @param initializer {Object} - */ - initializer: function(initializer) { - // If this is the first initializer being added to a subclass, we are going to reopen the class - // to make sure we have a new `initializers` object, which extends from the parent class' using - // prototypal inheritance. Without this, attempting to add initializers to the subclass would - // pollute the parent class as well as other subclasses. - if (this.superclass.initializers !== undefined && this.superclass.initializers === this.initializers) { - this.reopenClass({ - initializers: create(this.initializers) - }); + var handlerInfo = currentState.handlerInfos[payload.resolveIndex]; + + return handlerInfo.resolve(innerShouldContinue, payload) + .then(proceed, null, currentState.promiseLabel('Proceed')); } + } + }; - Ember.assert("The initializer '" + initializer.name + "' has already been registered", !this.initializers[initializer.name]); - Ember.assert("An initializer cannot be registered without an initialize function", canInvoke(initializer, 'initialize')); + __exports__["default"] = TransitionState; + }); +enifed("router/transition", + ["rsvp/promise","./handler-info","./utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var Promise = __dependency1__["default"]; + var ResolvedHandlerInfo = __dependency2__.ResolvedHandlerInfo; + var trigger = __dependency3__.trigger; + var slice = __dependency3__.slice; + var log = __dependency3__.log; + var promiseLabel = __dependency3__.promiseLabel; - this.initializers[initializer.name] = initializer; - }, + /** + @private - /** - This creates a container with the default Ember naming conventions. + A Transition is a thennable (a promise-like object) that represents + an attempt to transition to another route. It can be aborted, either + explicitly via `abort` or by attempting another transition while a + previous one is still underway. An aborted transition can also + be `retry()`d later. + */ + function Transition(router, intent, state, error) { + var transition = this; + this.state = state || router.state; + this.intent = intent; + this.router = router; + this.data = this.intent && this.intent.data || {}; + this.resolvedModels = {}; + this.queryParams = {}; - It also configures the container: + if (error) { + this.promise = Promise.reject(error); + this.error = error; + return; + } - * registered views are created every time they are looked up (they are - not singletons) - * registered templates are not factories; the registered value is - returned directly. - * the router receives the application as its `namespace` property - * all controllers receive the router as their `target` and `controllers` - properties - * all controllers receive the application as their `namespace` property - * the application view receives the application controller as its - `controller` property - * the application view receives the application template as its - `defaultTemplate` property + if (state) { + this.params = state.params; + this.queryParams = state.queryParams; + this.handlerInfos = state.handlerInfos; - @private - @method buildContainer - @static - @param {Ember.Application} namespace the application to build the - container for. - @return {Ember.Container} the built container - */ - buildContainer: function(namespace) { - var container = new Container(); + var len = state.handlerInfos.length; + if (len) { + this.targetName = state.handlerInfos[len-1].name; + } - Container.defaultContainer = new DeprecatedContainer(container); + for (var i = 0; i < len; ++i) { + var handlerInfo = state.handlerInfos[i]; - container.set = set; - container.resolver = resolverFor(namespace); - container.normalize = container.resolver.normalize; - container.describe = container.resolver.describe; - container.makeToString = container.resolver.makeToString; + // TODO: this all seems hacky + if (!handlerInfo.isResolved) { break; } + this.pivotHandler = handlerInfo.handler; + } - container.optionsForType('component', { singleton: false }); - container.optionsForType('view', { singleton: false }); - container.optionsForType('template', { instantiate: false }); - container.optionsForType('helper', { instantiate: false }); + this.sequence = Transition.currentSequence++; + this.promise = state.resolve(checkForAbort, this)['catch'](function(result) { + if (result.wasAborted || transition.isAborted) { + return Promise.reject(logAbort(transition)); + } else { + transition.trigger('error', result.error, transition, result.handlerWithError); + transition.abort(); + return Promise.reject(result.error); + } + }, promiseLabel('Handle Abort')); + } else { + this.promise = Promise.resolve(this.state); + this.params = {}; + } - container.register('application:main', namespace, { instantiate: false }); + function checkForAbort() { + if (transition.isAborted) { + return Promise.reject(undefined, promiseLabel("Transition aborted - reject")); + } + } + } - container.register('controller:basic', Controller, { instantiate: false }); - container.register('controller:object', ObjectController, { instantiate: false }); - container.register('controller:array', ArrayController, { instantiate: false }); - container.register('route:basic', Route, { instantiate: false }); - container.register('event_dispatcher:main', EventDispatcher); + Transition.currentSequence = 0; - container.register('router:main', Router); - container.injection('router:main', 'namespace', 'application:main'); + Transition.prototype = { + targetName: null, + urlMethod: 'update', + intent: null, + params: null, + pivotHandler: null, + resolveIndex: 0, + handlerInfos: null, + resolvedModels: null, + isActive: true, + state: null, + queryParamsOnly: false, - container.register('location:auto', AutoLocation); - container.register('location:hash', HashLocation); - container.register('location:history', HistoryLocation); - container.register('location:none', NoneLocation); + isTransition: true, - container.injection('controller', 'target', 'router:main'); - container.injection('controller', 'namespace', 'application:main'); + isExiting: function(handler) { + var handlerInfos = this.handlerInfos; + for (var i = 0, len = handlerInfos.length; i < len; ++i) { + var handlerInfo = handlerInfos[i]; + if (handlerInfo.name === handler || handlerInfo.handler === handler) { + return false; + } + } + return true; + }, - container.injection('route', 'router', 'router:main'); - container.injection('location', 'rootURL', '-location-setting:root-url'); + /** + @public - // DEBUGGING - container.register('resolver-for-debugging:main', container.resolver.__resolver__, { instantiate: false }); - container.injection('container-debug-adapter:main', 'resolver', 'resolver-for-debugging:main'); - container.injection('data-adapter:main', 'containerDebugAdapter', 'container-debug-adapter:main'); - // Custom resolver authors may want to register their own ContainerDebugAdapter with this key + The Transition's internal promise. Calling `.then` on this property + is that same as calling `.then` on the Transition object itself, but + this property is exposed for when you want to pass around a + Transition's promise, but not the Transition object itself, since + Transition object can be externally `abort`ed, while the promise + cannot. + */ + promise: null, - // ES6TODO: resolve this via import once ember-application package is ES6'ed - requireModule('ember-extension-support'); - container.register('container-debug-adapter:main', ContainerDebugAdapter); + /** + @public - return container; - } - }); + Custom state can be stored on a Transition's `data` object. + This can be useful for decorating a Transition within an earlier + hook and shared with a later hook. Properties set on `data` will + be copied to new transitions generated by calling `retry` on this + transition. + */ + data: null, - /** - This function defines the default lookup rules for container lookups: + /** + @public - * templates are looked up on `Ember.TEMPLATES` - * other names are looked up on the application after classifying the name. - For example, `controller:post` looks up `App.PostController` by default. - * if the default lookup fails, look for registered classes on the container + A standard promise hook that resolves if the transition + succeeds and rejects if it fails/redirects/aborts. - This allows the application to register default injections in the container - that could be overridden by the normal naming convention. + Forwards to the internal `promise` property which you can + use in situations where you want to pass around a thennable, + but not the Transition itself. - @private - @method resolverFor - @param {Ember.Namespace} namespace the namespace to look for classes - @return {*} the resolved value for a given lookup - */ - function resolverFor(namespace) { - if (namespace.get('resolver')) { - Ember.deprecate('Application.resolver is deprecated in favor of Application.Resolver', false); - } + @param {Function} onFulfilled + @param {Function} onRejected + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} + */ + then: function(onFulfilled, onRejected, label) { + return this.promise.then(onFulfilled, onRejected, label); + }, - var ResolverClass = namespace.get('resolver') || namespace.get('Resolver') || DefaultResolver; - var resolver = ResolverClass.create({ - namespace: namespace - }); + /** + @public - function resolve(fullName) { - return resolver.resolve(fullName); - } + Forwards to the internal `promise` property which you can + use in situations where you want to pass around a thennable, + but not the Transition itself. - resolve.describe = function(fullName) { - return resolver.lookupDescription(fullName); - }; + @method catch + @param {Function} onRejection + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} + */ + "catch": function(onRejection, label) { + return this.promise["catch"](onRejection, label); + }, - resolve.makeToString = function(factory, fullName) { - return resolver.makeToString(factory, fullName); - }; + /** + @public - resolve.normalize = function(fullName) { - if (resolver.normalize) { - return resolver.normalize(fullName); - } else { - Ember.deprecate('The Resolver should now provide a \'normalize\' function', false); - return fullName; - } - }; + Forwards to the internal `promise` property which you can + use in situations where you want to pass around a thennable, + but not the Transition itself. - resolve.__resolver__ = resolver; + @method finally + @param {Function} callback + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} + */ + "finally": function(callback, label) { + return this.promise["finally"](callback, label); + }, - return resolve; - } + /** + @public - __exports__["default"] = Application; - }); -define("ember-application/system/dag", - ["exports"], - function(__exports__) { - "use strict"; - function visit(vertex, fn, visited, path) { - var name = vertex.name, - vertices = vertex.incoming, - names = vertex.incomingNames, - len = names.length, - i; - if (!visited) { - visited = {}; - } - if (!path) { - path = []; - } - if (visited.hasOwnProperty(name)) { - return; - } - path.push(name); - visited[name] = true; - for (i = 0; i < len; i++) { - visit(vertices[names[i]], fn, visited, path); - } - fn(vertex, path); - path.pop(); - } + Aborts the Transition. Note you can also implicitly abort a transition + by initiating another transition while a previous one is underway. + */ + abort: function() { + if (this.isAborted) { return this; } + log(this.router, this.sequence, this.targetName + ": transition was aborted"); + this.intent.preTransitionState = this.router.state; + this.isAborted = true; + this.isActive = false; + this.router.activeTransition = null; + return this; + }, - function DAG() { - this.names = []; - this.vertices = {}; - } + /** + @public - DAG.prototype.add = function(name) { - if (!name) { return; } - if (this.vertices.hasOwnProperty(name)) { - return this.vertices[name]; - } - var vertex = { - name: name, incoming: {}, incomingNames: [], hasOutgoing: false, value: null - }; - this.vertices[name] = vertex; - this.names.push(name); - return vertex; - }; + Retries a previously-aborted transition (making sure to abort the + transition if it's still active). Returns a new transition that + represents the new attempt to transition. + */ + retry: function() { + // TODO: add tests for merged state retry()s + this.abort(); + return this.router.transitionByIntent(this.intent, false); + }, - DAG.prototype.map = function(name, value) { - this.add(name).value = value; - }; + /** + @public - DAG.prototype.addEdge = function(fromName, toName) { - if (!fromName || !toName || fromName === toName) { - return; - } - var from = this.add(fromName), to = this.add(toName); - if (to.incoming.hasOwnProperty(fromName)) { - return; - } - function checkCycle(vertex, path) { - if (vertex.name === toName) { - throw new EmberError("cycle detected: " + toName + " <- " + path.join(" <- ")); - } - } - visit(from, checkCycle); - from.hasOutgoing = true; - to.incoming[fromName] = from; - to.incomingNames.push(fromName); - }; + Sets the URL-changing method to be employed at the end of a + successful transition. By default, a new Transition will just + use `updateURL`, but passing 'replace' to this method will + cause the URL to update using 'replaceWith' instead. Omitting + a parameter will disable the URL change, allowing for transitions + that don't update the URL at completion (this is also used for + handleURL, since the URL has already changed before the + transition took place). - DAG.prototype.topsort = function(fn) { - var visited = {}, - vertices = this.vertices, - names = this.names, - len = names.length, - i, vertex; - for (i = 0; i < len; i++) { - vertex = vertices[names[i]]; - if (!vertex.hasOutgoing) { - visit(vertex, fn, visited); - } - } - }; + @param {String} method the type of URL-changing method to use + at the end of a transition. Accepted values are 'replace', + falsy values, or any other non-falsy value (which is + interpreted as an updateURL transition). - DAG.prototype.addEdges = function(name, value, before, after) { - var i; - this.map(name, value); - if (before) { - if (typeof before === 'string') { - this.addEdge(name, before); - } else { - for (i = 0; i < before.length; i++) { - this.addEdge(name, before[i]); - } - } - } - if (after) { - if (typeof after === 'string') { - this.addEdge(after, name); - } else { - for (i = 0; i < after.length; i++) { - this.addEdge(after[i], name); - } - } - } - }; + @return {Transition} this transition + */ + method: function(method) { + this.urlMethod = method; + return this; + }, + + /** + @public + + Fires an event on the current list of resolved/resolving + handlers within this transition. Useful for firing events + on route hierarchies that haven't fully been entered yet. - __exports__["default"] = DAG; - }); -define("ember-application/system/resolver", - ["ember-metal/core","ember-metal/property_get","ember-metal/logger","ember-runtime/system/string","ember-runtime/system/object","ember-runtime/system/namespace","ember-handlebars","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-application - */ + Note: This method is also aliased as `send` - var Ember = __dependency1__["default"]; - // Ember.TEMPLATES, Ember.assert - var get = __dependency2__.get; - var Logger = __dependency3__["default"]; - var classify = __dependency4__.classify; - var capitalize = __dependency4__.capitalize; - var decamelize = __dependency4__.decamelize; - var EmberObject = __dependency5__["default"]; - var Namespace = __dependency6__["default"]; - var EmberHandlebars = __dependency7__["default"]; + @param {Boolean} [ignoreFailure=false] a boolean specifying whether unhandled events throw an error + @param {String} name the name of the event to fire + */ + trigger: function (ignoreFailure) { + var args = slice.call(arguments); + if (typeof ignoreFailure === 'boolean') { + args.shift(); + } else { + // Throw errors on unhandled trigger events by default + ignoreFailure = false; + } + trigger(this.router, this.state.handlerInfos.slice(0, this.resolveIndex + 1), ignoreFailure, args); + }, - var Resolver = EmberObject.extend({ /** - This will be set to the Application instance when it is - created. + @public - @property namespace - */ - namespace: null, - normalize: function(fullName) { - throw new Error("Invalid call to `resolver.normalize(fullName)`. Please override the 'normalize' method in subclass of `Ember.Resolver` to prevent falling through to this error."); - }, - resolve: function(fullName) { - throw new Error("Invalid call to `resolver.resolve(parsedName)`. Please override the 'resolve' method in subclass of `Ember.Resolver` to prevent falling through to this error."); - }, - parseName: function(parsedName) { - throw new Error("Invalid call to `resolver.resolveByType(parsedName)`. Please override the 'resolveByType' method in subclass of `Ember.Resolver` to prevent falling through to this error."); - }, - lookupDescription: function(fullName) { - throw new Error("Invalid call to `resolver.lookupDescription(fullName)`. Please override the 'lookupDescription' method in subclass of `Ember.Resolver` to prevent falling through to this error."); - }, - makeToString: function(factory, fullName) { - throw new Error("Invalid call to `resolver.makeToString(factory, fullName)`. Please override the 'makeToString' method in subclass of `Ember.Resolver` to prevent falling through to this error."); + Transitions are aborted and their promises rejected + when redirects occur; this method returns a promise + that will follow any redirects that occur and fulfill + with the value fulfilled by any redirecting transitions + that occur. + + @return {Promise} a promise that fulfills with the same + value that the final redirecting transition fulfills with + */ + followRedirects: function() { + var router = this.router; + return this.promise['catch'](function(reason) { + if (router.activeTransition) { + return router.activeTransition.followRedirects(); + } + return Promise.reject(reason); + }); }, - resolveOther: function(parsedName) { - throw new Error("Invalid call to `resolver.resolveOther(parsedName)`. Please override the 'resolveOther' method in subclass of `Ember.Resolver` to prevent falling through to this error."); + + toString: function() { + return "Transition (sequence " + this.sequence + ")"; }, - _logLookup: function(found, parsedName) { - throw new Error("Invalid call to `resolver._logLookup(found, parsedName)`. Please override the '_logLookup' method in subclass of `Ember.Resolver` to prevent falling through to this error."); - } - }); + /** + @private + */ + log: function(message) { + log(this.router, this.sequence, message); + } + }; + // Alias 'trigger' as 'send' + Transition.prototype.send = Transition.prototype.trigger; /** - The DefaultResolver defines the default lookup rules to resolve - container lookups before consulting the container for registered - items: + @private - * templates are looked up on `Ember.TEMPLATES` - * other names are looked up on the application after converting - the name. For example, `controller:post` looks up - `App.PostController` by default. - * there are some nuances (see examples below) + Logs and returns a TransitionAborted error. + */ + function logAbort(transition) { + log(transition.router, transition.sequence, "detected abort."); + return new TransitionAborted(); + } - ### How Resolving Works + function TransitionAborted(message) { + this.message = (message || "TransitionAborted"); + this.name = "TransitionAborted"; + } - The container calls this object's `resolve` method with the - `fullName` argument. + __exports__.Transition = Transition; + __exports__.logAbort = logAbort; + __exports__.TransitionAborted = TransitionAborted; + }); +enifed("router/utils", + ["exports"], + function(__exports__) { + "use strict"; + var slice = Array.prototype.slice; - It first parses the fullName into an object using `parseName`. + var _isArray; + if (!Array.isArray) { + _isArray = function (x) { + return Object.prototype.toString.call(x) === "[object Array]"; + }; + } else { + _isArray = Array.isArray; + } - Then it checks for the presence of a type-specific instance - method of the form `resolve[Type]` and calls it if it exists. - For example if it was resolving 'template:post', it would call - the `resolveTemplate` method. + var isArray = _isArray; + __exports__.isArray = isArray; + function merge(hash, other) { + for (var prop in other) { + if (other.hasOwnProperty(prop)) { hash[prop] = other[prop]; } + } + } - Its last resort is to call the `resolveOther` method. + var oCreate = Object.create || function(proto) { + function F() {} + F.prototype = proto; + return new F(); + }; + __exports__.oCreate = oCreate; + /** + @private - The methods of this object are designed to be easy to override - in a subclass. For example, you could enhance how a template - is resolved like so: + Extracts query params from the end of an array + **/ + function extractQueryParams(array) { + var len = (array && array.length), head, queryParams; - ```javascript - App = Ember.Application.create({ - Resolver: Ember.DefaultResolver.extend({ - resolveTemplate: function(parsedName) { - var resolvedTemplate = this._super(parsedName); - if (resolvedTemplate) { return resolvedTemplate; } - return Ember.TEMPLATES['not_found']; + if(len && len > 0 && array[len - 1] && array[len - 1].hasOwnProperty('queryParams')) { + queryParams = array[len - 1].queryParams; + head = slice.call(array, 0, len - 1); + return [head, queryParams]; + } else { + return [array, null]; + } + } + + __exports__.extractQueryParams = extractQueryParams;/** + @private + + Coerces query param properties and array elements into strings. + **/ + function coerceQueryParamsToString(queryParams) { + for (var key in queryParams) { + if (typeof queryParams[key] === 'number') { + queryParams[key] = '' + queryParams[key]; + } else if (isArray(queryParams[key])) { + for (var i = 0, l = queryParams[key].length; i < l; i++) { + queryParams[key][i] = '' + queryParams[key][i]; } - }) - }); - ``` + } + } + } + /** + @private + */ + function log(router, sequence, msg) { + if (!router.log) { return; } - Some examples of how names are resolved: + if (arguments.length === 3) { + router.log("Transition #" + sequence + ": " + msg); + } else { + msg = sequence; + router.log(msg); + } + } - ``` - 'template:post' //=> Ember.TEMPLATES['post'] - 'template:posts/byline' //=> Ember.TEMPLATES['posts/byline'] - 'template:posts.byline' //=> Ember.TEMPLATES['posts/byline'] - 'template:blogPost' //=> Ember.TEMPLATES['blogPost'] - // OR - // Ember.TEMPLATES['blog_post'] - 'controller:post' //=> App.PostController - 'controller:posts.index' //=> App.PostsIndexController - 'controller:blog/post' //=> Blog.PostController - 'controller:basic' //=> Ember.Controller - 'route:post' //=> App.PostRoute - 'route:posts.index' //=> App.PostsIndexRoute - 'route:blog/post' //=> Blog.PostRoute - 'route:basic' //=> Ember.Route - 'view:post' //=> App.PostView - 'view:posts.index' //=> App.PostsIndexView - 'view:blog/post' //=> Blog.PostView - 'view:basic' //=> Ember.View - 'foo:post' //=> App.PostFoo - 'model:post' //=> App.Post - ``` + __exports__.log = log;function bind(context, fn) { + var boundArgs = arguments; + return function(value) { + var args = slice.call(boundArgs, 2); + args.push(value); + return fn.apply(context, args); + }; + } - @class DefaultResolver - @namespace Ember - @extends Ember.Object - */ - var DefaultResolver = EmberObject.extend({ - /** - This will be set to the Application instance when it is - created. + __exports__.bind = bind;function isParam(object) { + return (typeof object === "string" || object instanceof String || typeof object === "number" || object instanceof Number); + } - @property namespace - */ - namespace: null, - normalize: function(fullName) { - var split = fullName.split(':', 2), - type = split[0], - name = split[1]; + function forEach(array, callback) { + for (var i=0, l=array.length; i -1) { - result = result.replace(/\.(.)/g, function(m) { return m.charAt(1).toUpperCase(); }); - } + var eventWasHandled = false; - if (name.indexOf('_') > -1) { - result = result.replace(/_(.)/g, function(m) { return m.charAt(1).toUpperCase(); }); - } + for (var i=handlerInfos.length-1; i>=0; i--) { + var handlerInfo = handlerInfos[i], + handler = handlerInfo.handler; - return type + ':' + result; - } else { - return fullName; + if (handler.events && handler.events[name]) { + if (handler.events[name].apply(handler, args) === true) { + eventWasHandled = true; + } else { + return; + } } - }, - + } - /** - This method is called via the container's resolver method. - It parses the provided `fullName` and then looks up and - returns the appropriate template or class. + if (!eventWasHandled && !ignoreFailure) { + throw new Error("Nothing handled the event '" + name + "'."); + } + } - @method resolve - @param {String} fullName the lookup string - @return {Object} the resolved factory - */ - resolve: function(fullName) { - var parsedName = this.parseName(fullName), - resolveMethodName = parsedName.resolveMethodName, - resolved; + __exports__.trigger = trigger;function getChangelist(oldObject, newObject) { + var key; + var results = { + all: {}, + changed: {}, + removed: {} + }; - if (!(parsedName.name && parsedName.type)) { - throw new TypeError("Invalid fullName: `" + fullName + "`, must be of the form `type:name` "); - } + merge(results.all, newObject); - if (this[resolveMethodName]) { - resolved = this[resolveMethodName](parsedName); - } + var didChange = false; + coerceQueryParamsToString(oldObject); + coerceQueryParamsToString(newObject); - if (!resolved) { - resolved = this.resolveOther(parsedName); + // Calculate removals + for (key in oldObject) { + if (oldObject.hasOwnProperty(key)) { + if (!newObject.hasOwnProperty(key)) { + didChange = true; + results.removed[key] = oldObject[key]; + } } + } - if (parsedName.root && parsedName.root.LOG_RESOLVER) { - this._logLookup(resolved, parsedName); + // Calculate changes + for (key in newObject) { + if (newObject.hasOwnProperty(key)) { + if (isArray(oldObject[key]) && isArray(newObject[key])) { + if (oldObject[key].length !== newObject[key].length) { + results.changed[key] = newObject[key]; + didChange = true; + } else { + for (var i = 0, l = oldObject[key].length; i < l; i++) { + if (oldObject[key][i] !== newObject[key][i]) { + results.changed[key] = newObject[key]; + didChange = true; + } + } + } + } + else { + if (oldObject[key] !== newObject[key]) { + results.changed[key] = newObject[key]; + didChange = true; + } + } } + } - return resolved; - }, - /** - Convert the string name of the form "type:name" to - a Javascript object with the parsed aspects of the name - broken out. - - @protected - @param {String} fullName the lookup string - @method parseName - */ - parseName: function(fullName) { - var nameParts = fullName.split(":"), - type = nameParts[0], fullNameWithoutType = nameParts[1], - name = fullNameWithoutType, - namespace = get(this, 'namespace'), - root = namespace; - - if (type !== 'template' && name.indexOf('/') !== -1) { - var parts = name.split('/'); - name = parts[parts.length - 1]; - var namespaceName = capitalize(parts.slice(0, -1).join('.')); - root = Namespace.byName(namespaceName); + return didChange && results; + } - Ember.assert('You are looking for a ' + name + ' ' + type + ' in the ' + namespaceName + ' namespace, but the namespace could not be found', root); - } + __exports__.getChangelist = getChangelist;function promiseLabel(label) { + return 'Router: ' + label; + } - return { - fullName: fullName, - type: type, - fullNameWithoutType: fullNameWithoutType, - name: name, - root: root, - resolveMethodName: "resolve" + classify(type) - }; - }, + __exports__.promiseLabel = promiseLabel;function subclass(parentConstructor, proto) { + function C(props) { + parentConstructor.call(this, props || {}); + } + C.prototype = oCreate(parentConstructor.prototype); + merge(C.prototype, proto); + return C; + } - /** - Returns a human-readable description for a fullName. Used by the - Application namespace in assertions to describe the - precise name of the class that Ember is looking for, rather than - container keys. + __exports__.subclass = subclass;function resolveHook(obj, hookName) { + if (!obj) { return; } + var underscored = "_" + hookName; + return obj[underscored] && underscored || + obj[hookName] && hookName; + } - @protected - @param {String} fullName the lookup string - @method lookupDescription - */ - lookupDescription: function(fullName) { - var parsedName = this.parseName(fullName); + function callHook(obj, hookName) { + var args = slice.call(arguments, 2); + return applyHook(obj, hookName, args); + } - if (parsedName.type === 'template') { - return "template at " + parsedName.fullNameWithoutType.replace(/\./g, '/'); - } + function applyHook(obj, _hookName, args) { + var hookName = resolveHook(obj, _hookName); + if (hookName) { + return obj[hookName].apply(obj, args); + } + } - var description = parsedName.root + "." + classify(parsedName.name); - if (parsedName.type !== 'model') { description += classify(parsedName.type); } + __exports__.merge = merge; + __exports__.slice = slice; + __exports__.isParam = isParam; + __exports__.coerceQueryParamsToString = coerceQueryParamsToString; + __exports__.callHook = callHook; + __exports__.resolveHook = resolveHook; + __exports__.applyHook = applyHook; + }); +enifed("rsvp", + ["./rsvp/promise","./rsvp/events","./rsvp/node","./rsvp/all","./rsvp/all-settled","./rsvp/race","./rsvp/hash","./rsvp/hash-settled","./rsvp/rethrow","./rsvp/defer","./rsvp/config","./rsvp/map","./rsvp/resolve","./rsvp/reject","./rsvp/filter","./rsvp/asap","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __exports__) { + "use strict"; + var Promise = __dependency1__["default"]; + var EventTarget = __dependency2__["default"]; + var denodeify = __dependency3__["default"]; + var all = __dependency4__["default"]; + var allSettled = __dependency5__["default"]; + var race = __dependency6__["default"]; + var hash = __dependency7__["default"]; + var hashSettled = __dependency8__["default"]; + var rethrow = __dependency9__["default"]; + var defer = __dependency10__["default"]; + var config = __dependency11__.config; + var configure = __dependency11__.configure; + var map = __dependency12__["default"]; + var resolve = __dependency13__["default"]; + var reject = __dependency14__["default"]; + var filter = __dependency15__["default"]; + var asap = __dependency16__["default"]; + + config.async = asap; // default async is asap; + var cast = resolve; + function async(callback, arg) { + config.async(callback, arg); + } - return description; - }, + function on() { + config.on.apply(config, arguments); + } - makeToString: function(factory, fullName) { - return factory.toString(); - }, - /** - Given a parseName object (output from `parseName`), apply - the conventions expected by `Ember.Router` + function off() { + config.off.apply(config, arguments); + } - @protected - @param {Object} parsedName a parseName object with the parsed - fullName lookup string - @method useRouterNaming - */ - useRouterNaming: function(parsedName) { - parsedName.name = parsedName.name.replace(/\./g, '_'); - if (parsedName.name === 'basic') { - parsedName.name = ''; + // Set up instrumentation through `window.__PROMISE_INTRUMENTATION__` + if (typeof window !== 'undefined' && typeof window['__PROMISE_INSTRUMENTATION__'] === 'object') { + var callbacks = window['__PROMISE_INSTRUMENTATION__']; + configure('instrument', true); + for (var eventName in callbacks) { + if (callbacks.hasOwnProperty(eventName)) { + on(eventName, callbacks[eventName]); } - }, - /** - Look up the template in Ember.TEMPLATES + } + } - @protected - @param {Object} parsedName a parseName object with the parsed - fullName lookup string - @method resolveTemplate - */ - resolveTemplate: function(parsedName) { - var templateName = parsedName.fullNameWithoutType.replace(/\./g, '/'); + __exports__.cast = cast; + __exports__.Promise = Promise; + __exports__.EventTarget = EventTarget; + __exports__.all = all; + __exports__.allSettled = allSettled; + __exports__.race = race; + __exports__.hash = hash; + __exports__.hashSettled = hashSettled; + __exports__.rethrow = rethrow; + __exports__.defer = defer; + __exports__.denodeify = denodeify; + __exports__.configure = configure; + __exports__.on = on; + __exports__.off = off; + __exports__.resolve = resolve; + __exports__.reject = reject; + __exports__.async = async; + __exports__.map = map; + __exports__.filter = filter; + }); +enifed("rsvp.umd", + ["./rsvp"], + function(__dependency1__) { + "use strict"; + var Promise = __dependency1__.Promise; + var allSettled = __dependency1__.allSettled; + var hash = __dependency1__.hash; + var hashSettled = __dependency1__.hashSettled; + var denodeify = __dependency1__.denodeify; + var on = __dependency1__.on; + var off = __dependency1__.off; + var map = __dependency1__.map; + var filter = __dependency1__.filter; + var resolve = __dependency1__.resolve; + var reject = __dependency1__.reject; + var rethrow = __dependency1__.rethrow; + var all = __dependency1__.all; + var defer = __dependency1__.defer; + var EventTarget = __dependency1__.EventTarget; + var configure = __dependency1__.configure; + var race = __dependency1__.race; + var async = __dependency1__.async; + + var RSVP = { + 'race': race, + 'Promise': Promise, + 'allSettled': allSettled, + 'hash': hash, + 'hashSettled': hashSettled, + 'denodeify': denodeify, + 'on': on, + 'off': off, + 'map': map, + 'filter': filter, + 'resolve': resolve, + 'reject': reject, + 'all': all, + 'rethrow': rethrow, + 'defer': defer, + 'EventTarget': EventTarget, + 'configure': configure, + 'async': async + }; + + /* global define:true module:true window: true */ + if (typeof enifed === 'function' && enifed['amd']) { + enifed(function() { return RSVP; }); + } else if (typeof module !== 'undefined' && module['exports']) { + module['exports'] = RSVP; + } else if (typeof this !== 'undefined') { + this['RSVP'] = RSVP; + } + }); +enifed("rsvp/-internal", + ["./utils","./instrument","./config","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var objectOrFunction = __dependency1__.objectOrFunction; + var isFunction = __dependency1__.isFunction; - if (Ember.TEMPLATES[templateName]) { - return Ember.TEMPLATES[templateName]; - } + var instrument = __dependency2__["default"]; - templateName = decamelize(templateName); - if (Ember.TEMPLATES[templateName]) { - return Ember.TEMPLATES[templateName]; - } - }, - /** - Lookup the view using `resolveOther` + var config = __dependency3__.config; - @protected - @param {Object} parsedName a parseName object with the parsed - fullName lookup string - @method resolveView - */ - resolveView: function(parsedName) { - this.useRouterNaming(parsedName); - return this.resolveOther(parsedName); - }, - /** - Lookup the controller using `resolveOther` + function withOwnPromise() { + return new TypeError('A promises callback cannot return that same promise.'); + } - @protected - @param {Object} parsedName a parseName object with the parsed - fullName lookup string - @method resolveController - */ - resolveController: function(parsedName) { - this.useRouterNaming(parsedName); - return this.resolveOther(parsedName); - }, - /** - Lookup the route using `resolveOther` + function noop() {} - @protected - @param {Object} parsedName a parseName object with the parsed - fullName lookup string - @method resolveRoute - */ - resolveRoute: function(parsedName) { - this.useRouterNaming(parsedName); - return this.resolveOther(parsedName); - }, + var PENDING = void 0; + var FULFILLED = 1; + var REJECTED = 2; - /** - Lookup the model on the Application namespace + var GET_THEN_ERROR = new ErrorObject(); - @protected - @param {Object} parsedName a parseName object with the parsed - fullName lookup string - @method resolveModel - */ - resolveModel: function(parsedName) { - var className = classify(parsedName.name), - factory = get(parsedName.root, className); + function getThen(promise) { + try { + return promise.then; + } catch(error) { + GET_THEN_ERROR.error = error; + return GET_THEN_ERROR; + } + } - if (factory) { return factory; } - }, - /** - Look up the specified object (from parsedName) on the appropriate - namespace (usually on the Application) + function tryThen(then, value, fulfillmentHandler, rejectionHandler) { + try { + then.call(value, fulfillmentHandler, rejectionHandler); + } catch(e) { + return e; + } + } - @protected - @param {Object} parsedName a parseName object with the parsed - fullName lookup string - @method resolveHelper - */ - resolveHelper: function(parsedName) { - return this.resolveOther(parsedName) || EmberHandlebars.helpers[parsedName.fullNameWithoutType]; - }, - /** - Look up the specified object (from parsedName) on the appropriate - namespace (usually on the Application) + function handleForeignThenable(promise, thenable, then) { + config.async(function(promise) { + var sealed = false; + var error = tryThen(then, thenable, function(value) { + if (sealed) { return; } + sealed = true; + if (thenable !== value) { + resolve(promise, value); + } else { + fulfill(promise, value); + } + }, function(reason) { + if (sealed) { return; } + sealed = true; - @protected - @param {Object} parsedName a parseName object with the parsed - fullName lookup string - @method resolveOther - */ - resolveOther: function(parsedName) { - var className = classify(parsedName.name) + classify(parsedName.type), - factory = get(parsedName.root, className); - if (factory) { return factory; } - }, + reject(promise, reason); + }, 'Settle: ' + (promise._label || ' unknown promise')); - /** - @method _logLookup - @param {Boolean} found - @param {Object} parsedName - @private - */ - _logLookup: function(found, parsedName) { - var symbol, padding; + if (!sealed && error) { + sealed = true; + reject(promise, error); + } + }, promise); + } - if (found) { symbol = '[✓]'; } - else { symbol = '[ ]'; } + function handleOwnThenable(promise, thenable) { + if (thenable._state === FULFILLED) { + fulfill(promise, thenable._result); + } else if (promise._state === REJECTED) { + reject(promise, thenable._result); + } else { + subscribe(thenable, undefined, function(value) { + if (thenable !== value) { + resolve(promise, value); + } else { + fulfill(promise, value); + } + }, function(reason) { + reject(promise, reason); + }); + } + } - if (parsedName.fullName.length > 60) { - padding = '.'; + function handleMaybeThenable(promise, maybeThenable) { + if (maybeThenable.constructor === promise.constructor) { + handleOwnThenable(promise, maybeThenable); + } else { + var then = getThen(maybeThenable); + + if (then === GET_THEN_ERROR) { + reject(promise, GET_THEN_ERROR.error); + } else if (then === undefined) { + fulfill(promise, maybeThenable); + } else if (isFunction(then)) { + handleForeignThenable(promise, maybeThenable, then); } else { - padding = new Array(60 - parsedName.fullName.length).join('.'); + fulfill(promise, maybeThenable); } + } + } - Logger.info(symbol, parsedName.fullName, padding, this.lookupDescription(parsedName.fullName)); + function resolve(promise, value) { + if (promise === value) { + fulfill(promise, value); + } else if (objectOrFunction(value)) { + handleMaybeThenable(promise, value); + } else { + fulfill(promise, value); } - }); + } - __exports__.Resolver = Resolver; - __exports__.DefaultResolver = DefaultResolver; - }); -})(); + function publishRejection(promise) { + if (promise._onerror) { + promise._onerror(promise._result); + } -(function() { -define("ember-extension-support/container_debug_adapter", - ["ember-metal/core","ember-metal/utils","ember-runtime/system/string","ember-runtime/system/namespace","ember-runtime/system/object","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - var typeOf = __dependency2__.typeOf; - var dasherize = __dependency3__.dasherize; - var classify = __dependency3__.classify; - var Namespace = __dependency4__["default"]; - var EmberObject = __dependency5__["default"]; + publish(promise); + } - /** - @module ember - @submodule ember-extension-support - */ + function fulfill(promise, value) { + if (promise._state !== PENDING) { return; } - /** - The `ContainerDebugAdapter` helps the container and resolver interface - with tools that debug Ember such as the - [Ember Extension](https://github.com/tildeio/ember-extension) - for Chrome and Firefox. + promise._result = value; + promise._state = FULFILLED; - This class can be extended by a custom resolver implementer - to override some of the methods with library-specific code. + if (promise._subscribers.length === 0) { + if (config.instrument) { + instrument('fulfilled', promise); + } + } else { + config.async(publish, promise); + } + } - The methods likely to be overridden are: + function reject(promise, reason) { + if (promise._state !== PENDING) { return; } + promise._state = REJECTED; + promise._result = reason; - * `canCatalogEntriesByType` - * `catalogEntriesByType` + config.async(publishRejection, promise); + } - The adapter will need to be registered - in the application's container as `container-debug-adapter:main` + function subscribe(parent, child, onFulfillment, onRejection) { + var subscribers = parent._subscribers; + var length = subscribers.length; - Example: + parent._onerror = null; - ```javascript - Application.initializer({ - name: "containerDebugAdapter", + subscribers[length] = child; + subscribers[length + FULFILLED] = onFulfillment; + subscribers[length + REJECTED] = onRejection; - initialize: function(container, application) { - application.register('container-debug-adapter:main', require('app/container-debug-adapter')); + if (length === 0 && parent._state) { + config.async(publish, parent); + } + } + + function publish(promise) { + var subscribers = promise._subscribers; + var settled = promise._state; + + if (config.instrument) { + instrument(settled === FULFILLED ? 'fulfilled' : 'rejected', promise); + } + + if (subscribers.length === 0) { return; } + + var child, callback, detail = promise._result; + + for (var i = 0; i < subscribers.length; i += 3) { + child = subscribers[i]; + callback = subscribers[i + settled]; + + if (child) { + invokeCallback(settled, child, callback, detail); + } else { + callback(detail); } - }); - ``` + } - @class ContainerDebugAdapter - @namespace Ember - @extends EmberObject - @since 1.5.0 - */ - var ContainerDebugAdapter = EmberObject.extend({ - /** - The container of the application being debugged. - This property will be injected - on creation. + promise._subscribers.length = 0; + } - @property container - @default null - */ - container: null, + function ErrorObject() { + this.error = null; + } - /** - The resolver instance of the application - being debugged. This property will be injected - on creation. + var TRY_CATCH_ERROR = new ErrorObject(); - @property resolver - @default null - */ - resolver: null, + function tryCatch(callback, detail) { + try { + return callback(detail); + } catch(e) { + TRY_CATCH_ERROR.error = e; + return TRY_CATCH_ERROR; + } + } - /** - Returns true if it is possible to catalog a list of available - classes in the resolver for a given type. + function invokeCallback(settled, promise, callback, detail) { + var hasCallback = isFunction(callback), + value, error, succeeded, failed; - @method canCatalogEntriesByType - @param {string} type The type. e.g. "model", "controller", "route" - @return {boolean} whether a list is available for this type. - */ - canCatalogEntriesByType: function(type) { - if (type === 'model' || type === 'template') return false; - return true; - }, + if (hasCallback) { + value = tryCatch(callback, detail); - /** - Returns the available classes a given type. + if (value === TRY_CATCH_ERROR) { + failed = true; + error = value.error; + value = null; + } else { + succeeded = true; + } - @method catalogEntriesByType - @param {string} type The type. e.g. "model", "controller", "route" - @return {Array} An array of strings. - */ - catalogEntriesByType: function(type) { - var namespaces = Ember.A(Namespace.NAMESPACES), types = Ember.A(), self = this; - var typeSuffixRegex = new RegExp(classify(type) + "$"); + if (promise === value) { + reject(promise, withOwnPromise()); + return; + } - namespaces.forEach(function(namespace) { - if (namespace !== Ember) { - for (var key in namespace) { - if (!namespace.hasOwnProperty(key)) { continue; } - if (typeSuffixRegex.test(key)) { - var klass = namespace[key]; - if (typeOf(klass) === 'class') { - types.push(dasherize(key.replace(typeSuffixRegex, ''))); - } - } - } - } + } else { + value = detail; + succeeded = true; + } + + if (promise._state !== PENDING) { + // noop + } else if (hasCallback && succeeded) { + resolve(promise, value); + } else if (failed) { + reject(promise, error); + } else if (settled === FULFILLED) { + fulfill(promise, value); + } else if (settled === REJECTED) { + reject(promise, value); + } + } + + function initializePromise(promise, resolver) { + try { + resolver(function resolvePromise(value){ + resolve(promise, value); + }, function rejectPromise(reason) { + reject(promise, reason); }); - return types; + } catch(e) { + reject(promise, e); } - }); + } - __exports__["default"] = ContainerDebugAdapter; + __exports__.noop = noop; + __exports__.resolve = resolve; + __exports__.reject = reject; + __exports__.fulfill = fulfill; + __exports__.subscribe = subscribe; + __exports__.publish = publish; + __exports__.publishRejection = publishRejection; + __exports__.initializePromise = initializePromise; + __exports__.invokeCallback = invokeCallback; + __exports__.FULFILLED = FULFILLED; + __exports__.REJECTED = REJECTED; + __exports__.PENDING = PENDING; }); -define("ember-extension-support/data_adapter", - ["ember-metal/core","ember-metal/property_get","ember-metal/run_loop","ember-runtime/system/string","ember-runtime/system/namespace","ember-runtime/system/object","ember-runtime/system/native_array","ember-application/system/application","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { +enifed("rsvp/all-settled", + ["./enumerator","./promise","./utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; - var Ember = __dependency1__["default"]; - var get = __dependency2__.get; - var run = __dependency3__["default"]; - var dasherize = __dependency4__.dasherize; - var Namespace = __dependency5__["default"]; - var EmberObject = __dependency6__["default"]; - var A = __dependency7__.A; - var Application = __dependency8__["default"]; + var Enumerator = __dependency1__["default"]; + var makeSettledResult = __dependency1__.makeSettledResult; + var Promise = __dependency2__["default"]; + var o_create = __dependency3__.o_create; - /** - @module ember - @submodule ember-extension-support - */ + function AllSettled(Constructor, entries, label) { + this._superConstructor(Constructor, entries, false /* don't abort on reject */, label); + } - /** - The `DataAdapter` helps a data persistence library - interface with tools that debug Ember such - as the [Ember Extension](https://github.com/tildeio/ember-extension) - for Chrome and Firefox. + AllSettled.prototype = o_create(Enumerator.prototype); + AllSettled.prototype._superConstructor = Enumerator; + AllSettled.prototype._makeResult = makeSettledResult; + AllSettled.prototype._validationError = function() { + return new Error('allSettled must be called with an array'); + }; - This class will be extended by a persistence library - which will override some of the methods with - library-specific code. + /** + `RSVP.allSettled` is similar to `RSVP.all`, but instead of implementing + a fail-fast method, it waits until all the promises have returned and + shows you all the results. This is useful if you want to handle multiple + promises' failure states together as a set. - The methods likely to be overridden are: + Returns a promise that is fulfilled when all the given promises have been + settled. The return promise is fulfilled with an array of the states of + the promises passed into the `promises` array argument. - * `getFilters` - * `detect` - * `columnsForType` - * `getRecords` - * `getRecordColumnValues` - * `getRecordKeywords` - * `getRecordFilterValues` - * `getRecordColor` - * `observeRecord` + Each state object will either indicate fulfillment or rejection, and + provide the corresponding value or reason. The states will take one of + the following formats: - The adapter will need to be registered - in the application's container as `dataAdapter:main` + ```javascript + { state: 'fulfilled', value: value } + or + { state: 'rejected', reason: reason } + ``` Example: ```javascript - Application.initializer({ - name: "data-adapter", + var promise1 = RSVP.Promise.resolve(1); + var promise2 = RSVP.Promise.reject(new Error('2')); + var promise3 = RSVP.Promise.reject(new Error('3')); + var promises = [ promise1, promise2, promise3 ]; - initialize: function(container, application) { - application.register('data-adapter:main', DS.DataAdapter); - } + RSVP.allSettled(promises).then(function(array){ + // array == [ + // { state: 'fulfilled', value: 1 }, + // { state: 'rejected', reason: Error }, + // { state: 'rejected', reason: Error } + // ] + // Note that for the second item, reason.message will be '2', and for the + // third item, reason.message will be '3'. + }, function(error) { + // Not run. (This block would only be called if allSettled had failed, + // for instance if passed an incorrect argument type.) }); ``` - @class DataAdapter - @namespace Ember - @extends EmberObject + @method allSettled + @static + @for RSVP + @param {Array} promises + @param {String} label - optional string that describes the promise. + Useful for tooling. + @return {Promise} promise that is fulfilled with an array of the settled + states of the constituent promises. */ - var DataAdapter = EmberObject.extend({ - init: function() { - this._super(); - this.releaseMethods = A(); - }, - - /** - The container of the application being debugged. - This property will be injected - on creation. - - @property container - @default null - @since 1.3.0 - */ - container: null, - - - /** - The container-debug-adapter which is used - to list all models. - - @property containerDebugAdapter - @default undefined - @since 1.5.0 - **/ - containerDebugAdapter: undefined, - - /** - Number of attributes to send - as columns. (Enough to make the record - identifiable). - - @private - @property attributeLimit - @default 3 - @since 1.3.0 - */ - attributeLimit: 3, - - /** - Stores all methods that clear observers. - These methods will be called on destruction. - - @private - @property releaseMethods - @since 1.3.0 - */ - releaseMethods: A(), - - /** - Specifies how records can be filtered. - Records returned will need to have a `filterValues` - property with a key for every name in the returned array. - - @public - @method getFilters - @return {Array} List of objects defining filters. - The object should have a `name` and `desc` property. - */ - getFilters: function() { - return A(); - }, - /** - Fetch the model types and observe them for changes. + __exports__["default"] = function allSettled(entries, label) { + return new AllSettled(Promise, entries, label).promise; + } + }); +enifed("rsvp/all", + ["./promise","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Promise = __dependency1__["default"]; - @public - @method watchModelTypes + /** + This is a convenient alias for `RSVP.Promise.all`. - @param {Function} typesAdded Callback to call to add types. - Takes an array of objects containing wrapped types (returned from `wrapModelType`). + @method all + @static + @for RSVP + @param {Array} array Array of promises. + @param {String} label An optional label. This is useful + for tooling. + */ + __exports__["default"] = function all(array, label) { + return Promise.all(array, label); + } + }); +enifed("rsvp/asap", + ["exports"], + function(__exports__) { + "use strict"; + var len = 0; - @param {Function} typesUpdated Callback to call when a type has changed. - Takes an array of objects containing wrapped types. + __exports__["default"] = function asap(callback, arg) { + queue[len] = callback; + queue[len + 1] = arg; + len += 2; + if (len === 2) { + // If len is 1, that means that we need to schedule an async flush. + // If additional callbacks are queued before the queue is flushed, they + // will be processed by this flush that we are scheduling. + scheduleFlush(); + } + } - @return {Function} Method to call to remove all observers - */ - watchModelTypes: function(typesAdded, typesUpdated) { - var modelTypes = this.getModelTypes(), - self = this, typesToSend, releaseMethods = A(); + var browserWindow = (typeof window !== 'undefined') ? window : undefined + var browserGlobal = browserWindow || {}; + var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; - typesToSend = modelTypes.map(function(type) { - var klass = type.klass; - var wrapped = self.wrapModelType(klass, type.name); - releaseMethods.push(self.observeModelType(klass, typesUpdated)); - return wrapped; - }); + // test for web worker but not in IE10 + var isWorker = typeof Uint8ClampedArray !== 'undefined' && + typeof importScripts !== 'undefined' && + typeof MessageChannel !== 'undefined'; - typesAdded(typesToSend); + // node + function useNextTick() { + return function() { + process.nextTick(flush); + }; + } - var release = function() { - releaseMethods.forEach(function(fn) { fn(); }); - self.releaseMethods.removeObject(release); - }; - this.releaseMethods.pushObject(release); - return release; - }, + // vertx + function useVertxTimer() { + return function() { + vertxNext(flush); + }; + } - _nameToClass: function(type) { - if (typeof type === 'string') { - type = this.container.lookupFactory('model:' + type); - } - return type; - }, + function useMutationObserver() { + var iterations = 0; + var observer = new BrowserMutationObserver(flush); + var node = document.createTextNode(''); + observer.observe(node, { characterData: true }); - /** - Fetch the records of a given type and observe them for changes. + return function() { + node.data = (iterations = ++iterations % 2); + }; + } - @public - @method watchRecords + // web worker + function useMessageChannel() { + var channel = new MessageChannel(); + channel.port1.onmessage = flush; + return function () { + channel.port2.postMessage(0); + }; + } - @param {Function} recordsAdded Callback to call to add records. - Takes an array of objects containing wrapped records. - The object should have the following properties: - columnValues: {Object} key and value of a table cell - object: {Object} the actual record object + function useSetTimeout() { + return function() { + setTimeout(flush, 1); + }; + } - @param {Function} recordsUpdated Callback to call when a record has changed. - Takes an array of objects containing wrapped records. + var queue = new Array(1000); + function flush() { + for (var i = 0; i < len; i+=2) { + var callback = queue[i]; + var arg = queue[i+1]; - @param {Function} recordsRemoved Callback to call when a record has removed. - Takes the following parameters: - index: the array index where the records were removed - count: the number of records removed + callback(arg); - @return {Function} Method to call to remove all observers - */ - watchRecords: function(type, recordsAdded, recordsUpdated, recordsRemoved) { - var self = this, releaseMethods = A(), records = this.getRecords(type), release; + queue[i] = undefined; + queue[i+1] = undefined; + } - var recordUpdated = function(updatedRecord) { - recordsUpdated([updatedRecord]); - }; + len = 0; + } - var recordsToSend = records.map(function(record) { - releaseMethods.push(self.observeRecord(record, recordUpdated)); - return self.wrapRecord(record); - }); + function attemptVertex() { + try { + var vertx = eriuqer('vertx'); + var vertxNext = vertx.runOnLoop || vertx.runOnContext; + return useVertxTimer(); + } catch(e) { + return useSetTimeout(); + } + } + var scheduleFlush; + // Decide what async method to use to triggering processing of queued callbacks: + if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') { + scheduleFlush = useNextTick(); + } else if (BrowserMutationObserver) { + scheduleFlush = useMutationObserver(); + } else if (isWorker) { + scheduleFlush = useMessageChannel(); + } else if (browserWindow === undefined && typeof eriuqer === 'function') { + scheduleFlush = attemptVertex(); + } else { + scheduleFlush = useSetTimeout(); + } + }); +enifed("rsvp/config", + ["./events","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var EventTarget = __dependency1__["default"]; - var contentDidChange = function(array, idx, removedCount, addedCount) { - for (var i = idx; i < idx + addedCount; i++) { - var record = array.objectAt(i); - var wrapped = self.wrapRecord(record); - releaseMethods.push(self.observeRecord(record, recordUpdated)); - recordsAdded([wrapped]); - } + var config = { + instrument: false + }; - if (removedCount) { - recordsRemoved(idx, removedCount); - } - }; + EventTarget.mixin(config); - var observer = { didChange: contentDidChange, willChange: Ember.K }; - records.addArrayObserver(self, observer); + function configure(name, value) { + if (name === 'onerror') { + // handle for legacy users that expect the actual + // error to be passed to their function added via + // `RSVP.configure('onerror', someFunctionHere);` + config.on('error', value); + return; + } - release = function() { - releaseMethods.forEach(function(fn) { fn(); }); - records.removeArrayObserver(self, observer); - self.releaseMethods.removeObject(release); - }; + if (arguments.length === 2) { + config[name] = value; + } else { + return config[name]; + } + } - recordsAdded(recordsToSend); + __exports__.config = config; + __exports__.configure = configure; + }); +enifed("rsvp/defer", + ["./promise","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Promise = __dependency1__["default"]; - this.releaseMethods.pushObject(release); - return release; - }, + /** + `RSVP.defer` returns an object similar to jQuery's `$.Deferred`. + `RSVP.defer` should be used when porting over code reliant on `$.Deferred`'s + interface. New code should use the `RSVP.Promise` constructor instead. - /** - Clear all observers before destruction - @private - @method willDestroy - */ - willDestroy: function() { - this._super(); - this.releaseMethods.forEach(function(fn) { - fn(); - }); - }, + The object returned from `RSVP.defer` is a plain object with three properties: - /** - Detect whether a class is a model. + * promise - an `RSVP.Promise`. + * reject - a function that causes the `promise` property on this object to + become rejected + * resolve - a function that causes the `promise` property on this object to + become fulfilled. - Test that against the model class - of your persistence library + Example: - @private - @method detect - @param {Class} klass The class to test - @return boolean Whether the class is a model class or not - */ - detect: function(klass) { - return false; - }, + ```javascript + var deferred = RSVP.defer(); - /** - Get the columns for a given model type. + deferred.resolve("Success!"); - @private - @method columnsForType - @param {Class} type The model type - @return {Array} An array of columns of the following format: - name: {String} name of the column - desc: {String} Humanized description (what would show in a table column name) - */ - columnsForType: function(type) { - return A(); - }, + defered.promise.then(function(value){ + // value here is "Success!" + }); + ``` - /** - Adds observers to a model type class. + @method defer + @static + @for RSVP + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Object} + */ - @private - @method observeModelType - @param {Class} type The model type class - @param {Function} typesUpdated Called when a type is modified. - @return {Function} The function to call to remove observers - */ + __exports__["default"] = function defer(label) { + var deferred = { }; - observeModelType: function(type, typesUpdated) { - var self = this, records = this.getRecords(type); + deferred['promise'] = new Promise(function(resolve, reject) { + deferred['resolve'] = resolve; + deferred['reject'] = reject; + }, label); - var onChange = function() { - typesUpdated([self.wrapModelType(type)]); + return deferred; + } + }); +enifed("rsvp/enumerator", + ["./utils","./-internal","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var isArray = __dependency1__.isArray; + var isMaybeThenable = __dependency1__.isMaybeThenable; + + var noop = __dependency2__.noop; + var reject = __dependency2__.reject; + var fulfill = __dependency2__.fulfill; + var subscribe = __dependency2__.subscribe; + var FULFILLED = __dependency2__.FULFILLED; + var REJECTED = __dependency2__.REJECTED; + var PENDING = __dependency2__.PENDING; + + function makeSettledResult(state, position, value) { + if (state === FULFILLED) { + return { + state: 'fulfilled', + value: value }; - var observer = { - didChange: function() { - run.scheduleOnce('actions', this, onChange); - }, - willChange: Ember.K + } else { + return { + state: 'rejected', + reason: value }; + } + } - records.addArrayObserver(this, observer); + __exports__.makeSettledResult = makeSettledResult;function Enumerator(Constructor, input, abortOnReject, label) { + this._instanceConstructor = Constructor; + this.promise = new Constructor(noop, label); + this._abortOnReject = abortOnReject; - var release = function() { - records.removeArrayObserver(self, observer); - }; + if (this._validateInput(input)) { + this._input = input; + this.length = input.length; + this._remaining = input.length; - return release; - }, + this._init(); + if (this.length === 0) { + fulfill(this.promise, this._result); + } else { + this.length = this.length || 0; + this._enumerate(); + if (this._remaining === 0) { + fulfill(this.promise, this._result); + } + } + } else { + reject(this.promise, this._validationError()); + } + } - /** - Wraps a given model type and observes changes to it. + Enumerator.prototype._validateInput = function(input) { + return isArray(input); + }; - @private - @method wrapModelType - @param {Class} type A model class - @param {String} Optional name of the class - @return {Object} contains the wrapped type and the function to remove observers - Format: - type: {Object} the wrapped type - The wrapped type has the following format: - name: {String} name of the type - count: {Integer} number of records available - columns: {Columns} array of columns to describe the record - object: {Class} the actual Model type class - release: {Function} The function to remove observers - */ - wrapModelType: function(type, name) { - var release, records = this.getRecords(type), - typeToSend, self = this; + Enumerator.prototype._validationError = function() { + return new Error('Array Methods must be provided an Array'); + }; - typeToSend = { - name: name || type.toString(), - count: get(records, 'length'), - columns: this.columnsForType(type), - object: type - }; + Enumerator.prototype._init = function() { + this._result = new Array(this.length); + }; + __exports__["default"] = Enumerator; - return typeToSend; - }, + Enumerator.prototype._enumerate = function() { + var length = this.length; + var promise = this.promise; + var input = this._input; + for (var i = 0; promise._state === PENDING && i < length; i++) { + this._eachEntry(input[i], i); + } + }; - /** - Fetches all models defined in the application. + Enumerator.prototype._eachEntry = function(entry, i) { + var c = this._instanceConstructor; + if (isMaybeThenable(entry)) { + if (entry.constructor === c && entry._state !== PENDING) { + entry._onerror = null; + this._settledAt(entry._state, i, entry._result); + } else { + this._willSettleAt(c.resolve(entry), i); + } + } else { + this._remaining--; + this._result[i] = this._makeResult(FULFILLED, i, entry); + } + }; - @private - @method getModelTypes - @return {Array} Array of model types - */ - getModelTypes: function() { - var types, self = this, - containerDebugAdapter = this.get('containerDebugAdapter'); + Enumerator.prototype._settledAt = function(state, i, value) { + var promise = this.promise; - if (containerDebugAdapter.canCatalogEntriesByType('model')) { - types = containerDebugAdapter.catalogEntriesByType('model'); + if (promise._state === PENDING) { + this._remaining--; + + if (this._abortOnReject && state === REJECTED) { + reject(promise, value); } else { - types = this._getObjectsOnNamespaces(); + this._result[i] = this._makeResult(state, i, value); } + } - // New adapters return strings instead of classes - types = A(types).map(function(name) { - return { - klass: self._nameToClass(name), - name: name - }; - }); - types = A(types).filter(function(type) { - return self.detect(type.klass); - }); + if (this._remaining === 0) { + fulfill(promise, this._result); + } + }; - return A(types); - }, + Enumerator.prototype._makeResult = function(state, i, value) { + return value; + }; - /** - Loops over all namespaces and all objects - attached to them + Enumerator.prototype._willSettleAt = function(promise, i) { + var enumerator = this; - @private - @method _getObjectsOnNamespaces - @return {Array} Array of model type strings - */ - _getObjectsOnNamespaces: function() { - var namespaces = A(Namespace.NAMESPACES), - types = A(), - self = this; + subscribe(promise, undefined, function(value) { + enumerator._settledAt(FULFILLED, i, value); + }, function(reason) { + enumerator._settledAt(REJECTED, i, reason); + }); + }; + }); +enifed("rsvp/events", + ["exports"], + function(__exports__) { + "use strict"; + function indexOf(callbacks, callback) { + for (var i=0, l=callbacks.length; i 0) { - router['initialURL'] = url; - run(app, 'advanceReadiness'); - delete router['initialURL']; - } else { - run(app, app.handleURL, url); - } + object.trigger('foo', { name: 'bar' }); + // 'bar' logged to the console + ``` - return wait(app); - } + @method trigger + @for RSVP.EventTarget + @private + @param {String} eventName name of the event to be triggered + @param {Any} options optional value to be passed to any event handlers for + the given `eventName` + */ + trigger: function(eventName, options) { + var allCallbacks = callbacksFor(this), callbacks, callback; - function click(app, selector, context) { - var $el = findWithAssert(app, selector, context); - run($el, 'mousedown'); + if (callbacks = allCallbacks[eventName]) { + // Don't cache the callbacks.length since it may grow + for (var i=0; i 1; + }; - run($el, 'trigger', event); + RSVP.filter(promises, filterFn).then(function(result){ + // result is [ 2, 3 ] + }); + ``` - return wait(app); - } + If any of the `promises` given to `RSVP.filter` are rejected, the first promise + that is rejected will be given as an argument to the returned promise's + rejection handler. For example: - function keyEvent(app, selector, context, type, keyCode) { - if (typeof keyCode === 'undefined') { - keyCode = type; - type = context; - context = null; - } + ```javascript + var promise1 = RSVP.resolve(1); + var promise2 = RSVP.reject(new Error('2')); + var promise3 = RSVP.reject(new Error('3')); + var promises = [ promise1, promise2, promise3 ]; - return triggerEvent(app, selector, context, type, { keyCode: keyCode, which: keyCode }); - } + var filterFn = function(item){ + return item > 1; + }; - function fillIn(app, selector, context, text) { - var $el; - if (typeof text === 'undefined') { - text = context; - context = null; - } - $el = findWithAssert(app, selector, context); - run(function() { - $el.val(text).change(); + RSVP.filter(promises, filterFn).then(function(array){ + // Code here never runs because there are rejected promises! + }, function(reason) { + // reason.message === '2' }); - return wait(app); - } + ``` - function findWithAssert(app, selector, context) { - var $el = find(app, selector, context); - if ($el.length === 0) { - throw new EmberError("Element " + selector + " not found."); - } - return $el; - } + `RSVP.filter` will also wait for any promises returned from `filterFn`. + For instance, you may want to fetch a list of users then return a subset + of those users based on some asynchronous operation: - function find(app, selector, context) { - var $el; - context = context || get(app, 'rootElement'); - $el = app.$(selector, context); + ```javascript - return $el; - } + var alice = { name: 'alice' }; + var bob = { name: 'bob' }; + var users = [ alice, bob ]; - function andThen(app, callback) { - return wait(app, callback(app)); - } + var promises = users.map(function(user){ + return RSVP.resolve(user); + }); - function wait(app, value) { - return Test.promise(function(resolve) { - // If this is the first async promise, kick off the async test - if (++countAsync === 1) { - Test.adapter.asyncStart(); + var filterFn = function(user){ + // Here, Alice has permissions to create a blog post, but Bob does not. + return getPrivilegesForUser(user).then(function(privs){ + return privs.can_create_blog_post === true; + }); + }; + RSVP.filter(promises, filterFn).then(function(users){ + // true, because the server told us only Alice can create a blog post. + users.length === 1; + // false, because Alice is the only user present in `users` + users[0] === bob; + }); + ``` + + @method filter + @static + @for RSVP + @param {Array} promises + @param {Function} filterFn - function to be called on each resolved value to + filter the final results. + @param {String} label optional string describing the promise. Useful for + tooling. + @return {Promise} + */ + __exports__["default"] = function filter(promises, filterFn, label) { + return Promise.all(promises, label).then(function(values) { + if (!isFunction(filterFn)) { + throw new TypeError("You must pass a function as filter's second argument."); } - // Every 10ms, poll for the async thing to have finished - var watcher = setInterval(function() { - // 1. If the router is loading, keep polling - var routerIsLoading = !!app.__container__.lookup('router:main').router.activeTransition; - if (routerIsLoading) { return; } + var length = values.length; + var filtered = new Array(length); - // 2. If there are pending Ajax requests, keep polling - if (Test.pendingAjaxRequests) { return; } + for (var i = 0; i < length; i++) { + filtered[i] = filterFn(values[i]); + } - // 3. If there are scheduled timers or we are inside of a run loop, keep polling - if (run.hasScheduledTimers() || run.currentRunLoop) { return; } - if (Test.waiters && Test.waiters.any(function(waiter) { - var context = waiter[0]; - var callback = waiter[1]; - return !callback.call(context); - })) { return; } - // Stop polling - clearInterval(watcher); + return Promise.all(filtered, label).then(function(filtered) { + var results = new Array(length); + var newLength = 0; - // If this is the last async promise, end the async test - if (--countAsync === 0) { - Test.adapter.asyncEnd(); + for (var i = 0; i < length; i++) { + if (filtered[i]) { + results[newLength] = values[i]; + newLength++; + } } - // Synchronously resolve the promise - run(null, resolve, value); - }, 10); - }); + results.length = newLength; + return results; + }); + }); } + }); +enifed("rsvp/hash-settled", + ["./promise","./enumerator","./promise-hash","./utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + var Promise = __dependency1__["default"]; + var makeSettledResult = __dependency2__.makeSettledResult; + var PromiseHash = __dependency3__["default"]; + var Enumerator = __dependency2__["default"]; + var o_create = __dependency4__.o_create; + function HashSettled(Constructor, object, label) { + this._superConstructor(Constructor, object, false, label); + } - /** - * Loads a route, sets up any controllers, and renders any templates associated - * with the route as though a real user had triggered the route change while - * using your app. - * - * Example: - * - * ```javascript - * visit('posts/index').then(function() { - * // assert something - * }); - * ``` - * - * @method visit - * @param {String} url the name of the route - * @return {RSVP.Promise} - */ - asyncHelper('visit', visit); - - /** - * Clicks an element and triggers any actions triggered by the element's `click` - * event. - * - * Example: - * - * ```javascript - * click('.some-jQuery-selector').then(function() { - * // assert something - * }); - * ``` - * - * @method click - * @param {String} selector jQuery selector for finding element on the DOM - * @return {RSVP.Promise} - */ - asyncHelper('click', click); - - /** - * Simulates a key event, e.g. `keypress`, `keydown`, `keyup` with the desired keyCode - * - * Example: - * - * ```javascript - * keyEvent('.some-jQuery-selector', 'keypress', 13).then(function() { - * // assert something - * }); - * ``` - * - * @method keyEvent - * @param {String} selector jQuery selector for finding element on the DOM - * @param {String} type the type of key event, e.g. `keypress`, `keydown`, `keyup` - * @param {Number} keyCode the keyCode of the simulated key event - * @return {RSVP.Promise} - * @since 1.5.0 - */ - asyncHelper('keyEvent', keyEvent); + HashSettled.prototype = o_create(PromiseHash.prototype); + HashSettled.prototype._superConstructor = Enumerator; + HashSettled.prototype._makeResult = makeSettledResult; - /** - * Fills in an input element with some text. - * - * Example: - * - * ```javascript - * fillIn('#email', 'you@example.com').then(function() { - * // assert something - * }); - * ``` - * - * @method fillIn - * @param {String} selector jQuery selector finding an input element on the DOM - * to fill text with - * @param {String} text text to place inside the input element - * @return {RSVP.Promise} - */ - asyncHelper('fillIn', fillIn); + HashSettled.prototype._validationError = function() { + return new Error('hashSettled must be called with an object'); + }; /** - * Finds an element in the context of the app's container element. A simple alias - * for `app.$(selector)`. - * - * Example: - * - * ```javascript - * var $el = find('.my-selector'); - * ``` - * - * @method find - * @param {String} selector jQuery string selector for element lookup - * @return {Object} jQuery object representing the results of the query - */ - helper('find', find); + `RSVP.hashSettled` is similar to `RSVP.allSettled`, but takes an object + instead of an array for its `promises` argument. - /** - * Like `find`, but throws an error if the element selector returns no results. - * - * Example: - * - * ```javascript - * var $el = findWithAssert('.doesnt-exist'); // throws error - * ``` - * - * @method findWithAssert - * @param {String} selector jQuery selector string for finding an element within - * the DOM - * @return {Object} jQuery object representing the results of the query - * @throws {Error} throws error if jQuery object returned has a length of 0 - */ - helper('findWithAssert', findWithAssert); + Unlike `RSVP.all` or `RSVP.hash`, which implement a fail-fast method, + but like `RSVP.allSettled`, `hashSettled` waits until all the + constituent promises have returned and then shows you all the results + with their states and values/reasons. This is useful if you want to + handle multiple promises' failure states together as a set. - /** - Causes the run loop to process any pending events. This is used to ensure that - any async operations from other helpers (or your assertions) have been processed. + Returns a promise that is fulfilled when all the given promises have been + settled, or rejected if the passed parameters are invalid. - This is most often used as the return value for the helper functions (see 'click', - 'fillIn','visit',etc). + The returned promise is fulfilled with a hash that has the same key names as + the `promises` object argument. If any of the values in the object are not + promises, they will be copied over to the fulfilled object and marked with state + 'fulfilled'. Example: ```javascript - Ember.Test.registerAsyncHelper('loginUser', function(app, username, password) { - visit('secured/path/here') - .fillIn('#username', username) - .fillIn('#password', username) - .click('.submit') + var promises = { + myPromise: RSVP.Promise.resolve(1), + yourPromise: RSVP.Promise.resolve(2), + theirPromise: RSVP.Promise.resolve(3), + notAPromise: 4 + }; - return wait(); + RSVP.hashSettled(promises).then(function(hash){ + // hash here is an object that looks like: + // { + // myPromise: { state: 'fulfilled', value: 1 }, + // yourPromise: { state: 'fulfilled', value: 2 }, + // theirPromise: { state: 'fulfilled', value: 3 }, + // notAPromise: { state: 'fulfilled', value: 4 } + // } }); + ``` - @method wait - @param {Object} value The value to be returned. - @return {RSVP.Promise} - */ - asyncHelper('wait', wait); - asyncHelper('andThen', andThen); - + If any of the `promises` given to `RSVP.hash` are rejected, the state will + be set to 'rejected' and the reason for rejection provided. - /** - Returns the currently active route name. + Example: - Example: + ```javascript + var promises = { + myPromise: RSVP.Promise.resolve(1), + rejectedPromise: RSVP.Promise.reject(new Error('rejection')), + anotherRejectedPromise: RSVP.Promise.reject(new Error('more rejection')), + }; - ```javascript - function validateRouteName(){ - equal(currentRouteName(), 'some.path', "correct route was transitioned into."); - } + RSVP.hashSettled(promises).then(function(hash){ + // hash here is an object that looks like: + // { + // myPromise: { state: 'fulfilled', value: 1 }, + // rejectedPromise: { state: 'rejected', reason: Error }, + // anotherRejectedPromise: { state: 'rejected', reason: Error }, + // } + // Note that for rejectedPromise, reason.message == 'rejection', + // and for anotherRejectedPromise, reason.message == 'more rejection'. + }); + ``` - visit('/some/path').then(validateRouteName) - ``` + An important note: `RSVP.hashSettled` is intended for plain JavaScript objects that + are just a set of keys and values. `RSVP.hashSettled` will NOT preserve prototype + chains. - @method currentRouteName - @return {Object} The name of the currently active route. - @since 1.5.0 - */ - helper('currentRouteName', currentRouteName); + Example: - /** - Returns the current path. + ```javascript + function MyConstructor(){ + this.example = RSVP.Promise.resolve('Example'); + } - Example: + MyConstructor.prototype = { + protoProperty: RSVP.Promise.resolve('Proto Property') + }; - ```javascript - function validateURL(){ - equal(currentPath(), 'some.path.index', "correct path was transitioned into."); - } + var myObject = new MyConstructor(); - click('#some-link-id').then(validateURL); - ``` + RSVP.hashSettled(myObject).then(function(hash){ + // protoProperty will not be present, instead you will just have an + // object that looks like: + // { + // example: { state: 'fulfilled', value: 'Example' } + // } + // + // hash.hasOwnProperty('protoProperty'); // false + // 'undefined' === typeof hash.protoProperty + }); + ``` - @method currentPath - @return {Object} The currently active path. - @since 1.5.0 + @method hashSettled + @for RSVP + @param {Object} promises + @param {String} label optional string that describes the promise. + Useful for tooling. + @return {Promise} promise that is fulfilled when when all properties of `promises` + have been settled. + @static */ - helper('currentPath', currentPath); + __exports__["default"] = function hashSettled(object, label) { + return new HashSettled(Promise, object, label).promise; + } + }); +enifed("rsvp/hash", + ["./promise","./promise-hash","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var Promise = __dependency1__["default"]; + var PromiseHash = __dependency2__["default"]; /** - Returns the current URL. + `RSVP.hash` is similar to `RSVP.all`, but takes an object instead of an array + for its `promises` argument. - Example: + Returns a promise that is fulfilled when all the given promises have been + fulfilled, or rejected if any of them become rejected. The returned promise + is fulfilled with a hash that has the same key names as the `promises` object + argument. If any of the values in the object are not promises, they will + simply be copied over to the fulfilled object. - ```javascript - function validateURL(){ - equal(currentURL(), '/some/path', "correct URL was transitioned into."); - } + Example: - click('#some-link-id').then(validateURL); - ``` + ```javascript + var promises = { + myPromise: RSVP.resolve(1), + yourPromise: RSVP.resolve(2), + theirPromise: RSVP.resolve(3), + notAPromise: 4 + }; - @method currentURL - @return {Object} The currently active URL. - @since 1.5.0 - */ - helper('currentURL', currentURL); + RSVP.hash(promises).then(function(hash){ + // hash here is an object that looks like: + // { + // myPromise: 1, + // yourPromise: 2, + // theirPromise: 3, + // notAPromise: 4 + // } + }); + ```` - /** - Triggers the given event on the element identified by the provided selector. + If any of the `promises` given to `RSVP.hash` are rejected, the first promise + that is rejected will be given as the reason to the rejection handler. Example: ```javascript - triggerEvent('#some-elem-id', 'blur'); - ``` - - This is actually used internally by the `keyEvent` helper like so: + var promises = { + myPromise: RSVP.resolve(1), + rejectedPromise: RSVP.reject(new Error('rejectedPromise')), + anotherRejectedPromise: RSVP.reject(new Error('anotherRejectedPromise')), + }; - ```javascript - triggerEvent('#some-elem-id', 'keypress', { keyCode: 13 }); + RSVP.hash(promises).then(function(hash){ + // Code here never runs because there are rejected promises! + }, function(reason) { + // reason.message === 'rejectedPromise' + }); ``` - @method triggerEvent - @param {String} selector jQuery selector for finding element on the DOM - @param {String} type The event type to be triggered. - @param {String} options The options to be passed to jQuery.Event. - @return {RSVP.Promise} - @since 1.5.0 - */ - asyncHelper('triggerEvent', triggerEvent); - }); -define("ember-testing/initializers", - ["ember-runtime/system/lazy_load"], - function(__dependency1__) { - "use strict"; - var onLoad = __dependency1__.onLoad; - - var name = 'deferReadiness in `testing` mode'; + An important note: `RSVP.hash` is intended for plain JavaScript objects that + are just a set of keys and values. `RSVP.hash` will NOT preserve prototype + chains. - onLoad('Ember.Application', function(Application) { - if (!Application.initializers[name]) { - Application.initializer({ - name: name, + Example: - initialize: function(container, application){ - if (application.testing) { - application.deferReadiness(); - } - } - }); + ```javascript + function MyConstructor(){ + this.example = RSVP.resolve('Example'); } - }); - }); -define("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"]; - - // to setup initializer - // to handle various edge cases - - var setupForTesting = __dependency4__["default"]; - var Test = __dependency5__["default"]; - var Adapter = __dependency6__["default"]; - var QUnitAdapter = __dependency7__["default"]; - // adds helpers to helpers object in Test - - /** - Ember Testing - - @module ember - @submodule ember-testing - @requires ember-application - */ - Ember.Test = Test; - Ember.Test.Adapter = Adapter; - Ember.Test.QUnitAdapter = QUnitAdapter; - Ember.setupForTesting = setupForTesting; - }); -define("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"]; + MyConstructor.prototype = { + protoProperty: RSVP.resolve('Proto Property') + }; - var Test; + var myObject = new MyConstructor(); - function incrementAjaxPendingRequests(){ - Test.pendingAjaxRequests++; - } + RSVP.hash(myObject).then(function(hash){ + // protoProperty will not be present, instead you will just have an + // object that looks like: + // { + // example: 'Example' + // } + // + // hash.hasOwnProperty('protoProperty'); // false + // 'undefined' === typeof hash.protoProperty + }); + ``` - function decrementAjaxPendingRequests(){ - Ember.assert("An ajaxComplete event which would cause the number of pending AJAX " + - "requests to be negative has been triggered. This is most likely " + - "caused by AJAX events that were started before calling " + - "`injectTestHelpers()`.", Test.pendingAjaxRequests !== 0); - Test.pendingAjaxRequests--; + @method hash + @static + @for RSVP + @param {Object} promises + @param {String} label optional string that describes the promise. + Useful for tooling. + @return {Promise} promise that is fulfilled when all properties of `promises` + have been fulfilled, or rejected if any of them become rejected. + */ + __exports__["default"] = function hash(object, label) { + return new PromiseHash(Promise, object, label).promise; } + }); +enifed("rsvp/instrument", + ["./config","./utils","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var config = __dependency1__.config; + var now = __dependency2__.now; - /** - Sets Ember up for testing. This is useful to perform - basic setup steps in order to unit test. + var queue = []; - Use `App.setupForTesting` to perform integration tests (full - application testing). + function scheduleFlush() { + setTimeout(function() { + var entry; + for (var i = 0; i < queue.length; i++) { + entry = queue[i]; - @method setupForTesting - @namespace Ember - @since 1.5.0 - */ - function setupForTesting() { - if (!Test) { Test = requireModule('ember-testing/test')['default']; } + var payload = entry.payload; - Ember.testing = true; + payload.guid = payload.key + payload.id; + payload.childGuid = payload.key + payload.childId; + if (payload.error) { + payload.stack = payload.error.stack; + } - // if adapter is not manually set default to QUnit - if (!Test.adapter) { - Test.adapter = QUnitAdapter.create(); - } + config.trigger(entry.name, entry.payload); + } + queue.length = 0; + }, 50); + } - if (!Test.pendingAjaxRequests) { - Test.pendingAjaxRequests = 0; + __exports__["default"] = function instrument(eventName, promise, child) { + if (1 === queue.push({ + name: eventName, + payload: { + key: promise._guidKey, + id: promise._id, + eventName: eventName, + detail: promise._result, + childId: child && child._id, + label: promise._label, + timeStamp: now(), + error: config["instrument-with-stack"] ? new Error(promise._label) : null + }})) { + scheduleFlush(); + } } - - jQuery(document).off('ajaxSend', incrementAjaxPendingRequests); - jQuery(document).off('ajaxComplete', decrementAjaxPendingRequests); - jQuery(document).on('ajaxSend', incrementAjaxPendingRequests); - jQuery(document).on('ajaxComplete', decrementAjaxPendingRequests); - }; - - __exports__["default"] = setupForTesting; }); -define("ember-testing/support", - ["ember-metal/core","ember-views/system/jquery"], - function(__dependency1__, __dependency2__) { +enifed("rsvp/map", + ["./promise","./utils","exports"], + function(__dependency1__, __dependency2__, __exports__) { "use strict"; - var Ember = __dependency1__["default"]; - var jQuery = __dependency2__["default"]; + var Promise = __dependency1__["default"]; + var isFunction = __dependency2__.isFunction; /** - @module ember - @submodule ember-testing - */ + `RSVP.map` is similar to JavaScript's native `map` method, except that it + waits for all promises to become fulfilled before running the `mapFn` on + each item in given to `promises`. `RSVP.map` returns a promise that will + become fulfilled with the result of running `mapFn` on the values the promises + become fulfilled with. - var $ = jQuery; + For example: - /** - This method creates a checkbox and triggers the click event to fire the - passed in handler. It is used to correct for a bug in older versions - of jQuery (e.g 1.8.3). + ```javascript - @private - @method testCheckboxClick - */ - function testCheckboxClick(handler) { - $('') - .css({ position: 'absolute', left: '-1000px', top: '-1000px' }) - .appendTo('body') - .on('click', handler) - .trigger('click') - .remove(); - } + var promise1 = RSVP.resolve(1); + var promise2 = RSVP.resolve(2); + var promise3 = RSVP.resolve(3); + var promises = [ promise1, promise2, promise3 ]; - $(function() { - /* - Determine whether a checkbox checked using jQuery's "click" method will have - the correct value for its checked property. + var mapFn = function(item){ + return item + 1; + }; - 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. - */ - 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; - } - } - }; - } + RSVP.map(promises, mapFn).then(function(result){ + // result is [ 2, 3, 4 ] }); + ``` - // 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); + If any of the `promises` given to `RSVP.map` are rejected, the first promise + that is rejected will be given as an argument to the returned promise's + rejection handler. For example: + + ```javascript + var promise1 = RSVP.resolve(1); + var promise2 = RSVP.reject(new Error('2')); + var promise3 = RSVP.reject(new Error('3')); + var promises = [ promise1, promise2, promise3 ]; + + var mapFn = function(item){ + return item + 1; + }; + + RSVP.map(promises, mapFn).then(function(array){ + // Code here never runs because there are rejected promises! + }, function(reason) { + // reason.message === '2' }); - }); - }); -define("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"]; + ``` - /** - @module ember - @submodule ember-testing - */ - var slice = [].slice, - helpers = {}, - injectHelpersCallbacks = []; + `RSVP.map` will also wait if a promise is returned from `mapFn`. For example, + say you want to get all comments from a set of blog posts, but you need + the blog posts first because they contain a url to those comments. - /** - This is a container for an assortment of testing related functionality: + ```javscript - * 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. + var mapFn = function(blogPost){ + // getComments does some ajax and returns an RSVP.Promise that is fulfilled + // with some comments data + return getComments(blogPost.comments_url); + }; - @class Test - @namespace Ember + // getBlogPosts does some ajax and returns an RSVP.Promise that is fulfilled + // with some blog post data + RSVP.map(getBlogPosts(), mapFn).then(function(comments){ + // comments is the result of asking the server for the comments + // of all blog posts returned from getBlogPosts() + }); + ``` + + @method map + @static + @for RSVP + @param {Array} promises + @param {Function} mapFn function to be called on each fulfilled promise. + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} promise that is fulfilled with the result of calling + `mapFn` on each fulfilled promise or value when they become fulfilled. + The promise will be rejected if any of the given `promises` become rejected. + @static */ - var Test = { + __exports__["default"] = function map(promises, mapFn, label) { + return Promise.all(promises, label).then(function(values) { + if (!isFunction(mapFn)) { + throw new TypeError("You must pass a function as map's second argument."); + } - /** - `registerHelper` is used to register a test helper that will be injected - when `App.injectTestHelpers` is called. + var length = values.length; + var results = new Array(length); - The helper method will always be called with the current Application as - the first parameter. + for (var i = 0; i < length; i++) { + results[i] = mapFn(values[i]); + } - For example: + return Promise.all(results, label); + }); + } + }); +enifed("rsvp/node", + ["./promise","./-internal","./utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var Promise = __dependency1__["default"]; + var noop = __dependency2__.noop; + var resolve = __dependency2__.resolve; + var reject = __dependency2__.reject; + var isArray = __dependency3__.isArray; - ```javascript - Ember.Test.registerHelper('boot', function(app) { - Ember.run(app, app.advanceReadiness); - }); - ``` + function Result() { + this.value = undefined; + } - This helper can later be called without arguments because it will be - called with `app` as the first parameter. + var ERROR = new Result(); + var GET_THEN_ERROR = new Result(); - ```javascript - App = Ember.Application.create(); - App.injectTestHelpers(); - boot(); - ``` + function getThen(obj) { + try { + return obj.then; + } catch(error) { + ERROR.value= error; + return ERROR; + } + } - @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 } - }; - }, - /** - `registerAsyncHelper` is used to register an async test helper that will be injected - when `App.injectTestHelpers` is called. + function tryApply(f, s, a) { + try { + f.apply(s, a); + } catch(error) { + ERROR.value = error; + return ERROR; + } + } - The helper method will always be called with the current Application as - the first parameter. + function makeObject(_, argumentNames) { + var obj = {}; + var name; + var i; + var length = _.length; + var args = new Array(length); - For example: + for (var x = 0; x < length; x++) { + args[x] = _[x]; + } - ```javascript - Ember.Test.registerAsyncHelper('boot', function(app) { - Ember.run(app, app.advanceReadiness); - }); - ``` + for (i = 0; i < argumentNames.length; i++) { + name = argumentNames[i]; + obj[name] = args[i + 1]; + } - 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. + return obj; + } + function arrayResult(_) { + var length = _.length; + var args = new Array(length - 1); - For example: + for (var i = 1; i < length; i++) { + args[i - 1] = _[i]; + } - ```javascript - Ember.Test.registerAsyncHelper('deletePost', function(app, postId) { - click('.delete-' + postId); - }); + return args; + } - // ... in your test - visit('/post/2'); - deletePost(2); - visit('/post/3'); - deletePost(3); - ``` + function wrapThenable(then, promise) { + return { + then: function(onFulFillment, onRejection) { + return then.call(promise, onFulFillment, onRejection); + } + }; + } - @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 } - }; - }, + /** + `RSVP.denodeify` takes a 'node-style' function and returns a function that + will return an `RSVP.Promise`. You can use `denodeify` in Node.js or the + browser when you'd prefer to use promises over using callbacks. For example, + `denodeify` transforms the following: - /** - Remove a previously added helper method. + ```javascript + var fs = require('fs'); - Example: + fs.readFile('myfile.txt', function(err, data){ + if (err) return handleError(err); + handleData(data); + }); + ``` - ```javascript - Ember.Test.unregisterHelper('wait'); - ``` + into: - @public - @method unregisterHelper - @param {String} name The helper to remove. - */ - unregisterHelper: function(name) { - delete helpers[name]; - delete Test.Promise.prototype[name]; - }, + ```javascript + var fs = require('fs'); + var readFile = RSVP.denodeify(fs.readFile); - /** - Used to register callbacks to be fired whenever `App.injectTestHelpers` - is called. + readFile('myfile.txt').then(handleData, handleError); + ``` - The callback will receive the current application as an argument. + If the node function has multiple success parameters, then `denodeify` + just returns the first one: - Example: + ```javascript + var request = RSVP.denodeify(require('request')); - ```javascript - Ember.Test.onInjectHelpers(function() { - Ember.$(document).ajaxSend(function() { - Test.pendingAjaxRequests++; - }); + request('http://example.com').then(function(res) { + // ... + }); + ``` - Ember.$(document).ajaxComplete(function() { - Test.pendingAjaxRequests--; - }); - }); - ``` + However, if you need all success parameters, setting `denodeify`'s + second parameter to `true` causes it to return all success parameters + as an array: - @public - @method onInjectHelpers - @param {Function} callback The function to be called. - */ - onInjectHelpers: function(callback) { - injectHelpersCallbacks.push(callback); - }, + ```javascript + var request = RSVP.denodeify(require('request'), true); - /** - 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. + request('http://example.com').then(function(result) { + // result[0] -> res + // result[1] -> body + }); + ``` - This method should be returned by async helpers such as `wait`. + Or if you pass it an array with names it returns the parameters as a hash: - @public - @method promise - @param {Function} resolver The function used to resolve the promise. - */ - promise: function(resolver) { - return new Test.Promise(resolver); - }, + ```javascript + var request = RSVP.denodeify(require('request'), ['res', 'body']); - /** - Used to allow ember-testing to communicate with a specific testing - framework. + request('http://example.com').then(function(result) { + // result.res + // result.body + }); + ``` - You can manually set it before calling `App.setupForTesting()`. + Sometimes you need to retain the `this`: - Example: + ```javascript + var app = require('express')(); + var render = RSVP.denodeify(app.render.bind(app)); + ``` - ```javascript - Ember.Test.adapter = MyCustomAdapter.create() - ``` + The denodified function inherits from the original function. It works in all + environments, except IE 10 and below. Consequently all properties of the original + function are available to you. However, any properties you change on the + denodeified function won't be changed on the original function. Example: - If you do not set it, ember-testing will default to `Ember.Test.QUnitAdapter`. + ```javascript + var request = RSVP.denodeify(require('request')), + cookieJar = request.jar(); // <- Inheritance is used here - @public - @property adapter - @type {Class} The adapter to be used. - @default Ember.Test.QUnitAdapter - */ - adapter: null, + request('http://example.com', {jar: cookieJar}).then(function(res) { + // cookieJar.cookies holds now the cookies returned by example.com + }); + ``` - /** - Replacement for `Ember.RSVP.resolve` - The only difference is this uses - and instance of `Ember.Test.Promise` + Using `denodeify` makes it easier to compose asynchronous operations instead + of using callbacks. For example, instead of: - @public - @method resolve - @param {Mixed} The value to resolve - @since 1.2.0 - */ - resolve: function(val) { - return Test.promise(function(resolve) { - return resolve(val); + ```javascript + var fs = require('fs'); + + fs.readFile('myfile.txt', function(err, data){ + if (err) { ... } // Handle error + fs.writeFile('myfile2.txt', data, function(err){ + if (err) { ... } // Handle error + console.log('done') }); - }, + }); + ``` - /** - 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. + you can chain the operations together using `then` from the returned promise: - For example: + ```javascript + var fs = require('fs'); + var readFile = RSVP.denodeify(fs.readFile); + var writeFile = RSVP.denodeify(fs.writeFile); - ```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. + readFile('myfile.txt').then(function(data){ + return writeFile('myfile2.txt', data); + }).then(function(){ + console.log('done') + }).catch(function(error){ + // Handle error + }); + ``` - For example: + @method denodeify + @static + @for RSVP + @param {Function} nodeFunc a 'node-style' function that takes a callback as + its last argument. The callback expects an error to be passed as its first + argument (if an error occurred, otherwise null), and the value from the + operation as its second argument ('function(err, value){ }'). + @param {Boolean|Array} argumentNames An optional paramter that if set + to `true` causes the promise to fulfill with the callback's success arguments + as an array. This is useful if the node function has multiple success + paramters. If you set this paramter to an array with names, the promise will + fulfill with a hash with these names as keys and the success parameters as + values. + @return {Function} a function that wraps `nodeFunc` to return an + `RSVP.Promise` + @static + */ + __exports__["default"] = function denodeify(nodeFunc, options) { + var fn = function() { + var self = this; + var l = arguments.length; + var args = new Array(l + 1); + var arg; + var promiseInput = false; + + for (var i = 0; i < l; ++i) { + arg = arguments[i]; + + if (!promiseInput) { + // TODO: clean this up + promiseInput = needsPromiseInput(arg); + if (promiseInput === GET_THEN_ERROR) { + var p = new Promise(noop); + reject(p, GET_THEN_ERROR.value); + return p; + } else if (promiseInput && promiseInput !== true) { + arg = wrapThenable(promiseInput, arg); + } + } + args[i] = arg; + } - ```javascript - Ember.Test.registerWaiter(MyDB, MyDB.hasPendingTransactions); - ``` + var promise = new Promise(noop); - @public - @method registerWaiter - @param {Object} context (optional) - @param {Function} callback - @since 1.2.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]); - }, - /** - `unregisterWaiter` is used to unregister a callback that was - registered with `registerWaiter`. + args[l] = function(err, val) { + if (err) + reject(promise, err); + else if (options === undefined) + resolve(promise, val); + else if (options === true) + resolve(promise, arrayResult(arguments)); + else if (isArray(options)) + resolve(promise, makeObject(arguments, options)); + else + resolve(promise, val); + }; - @public - @method unregisterWaiter - @param {Object} context (optional) - @param {Function} callback - @since 1.2.0 - */ - unregisterWaiter: function(context, callback) { - var pair; - if (!this.waiters) { return; } - if (arguments.length === 1) { - callback = context; - context = null; + if (promiseInput) { + return handlePromiseInput(promise, args, nodeFunc, self); + } else { + return handleValueInput(promise, args, nodeFunc, self); } - 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, - meta = helpers[name].meta; + fn.__proto__ = nodeFunc; - return function() { - var args = slice.call(arguments), - lastPromise = Test.lastPromise; + return fn; + } - args.unshift(app); + function handleValueInput(promise, args, nodeFunc, self) { + var result = tryApply(nodeFunc, self, args); + if (result === ERROR) { + reject(promise, result.value); + } + return promise; + } - // some helpers are not async and - // need to return a value immediately. - // example: `find` - if (!meta.wait) { - return fn.apply(app, args); + function handlePromiseInput(promise, args, nodeFunc, self){ + return Promise.all(args).then(function(args){ + var result = tryApply(nodeFunc, self, args); + if (result === ERROR) { + reject(promise, result.value); } + return promise; + }); + } - if (!lastPromise) { - // It's the first async helper in current context - lastPromise = fn.apply(app, args); + function needsPromiseInput(arg) { + if (arg && typeof arg === 'object') { + if (arg.constructor === Promise) { + return true; } 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); - }); - }); + return getThen(arg); } - - return lastPromise; - }; - } - - function run(fn) { - if (!emberRun.currentRunLoop) { - emberRun(fn); } else { - fn(); + return false; } } + }); +enifed("rsvp/promise-hash", + ["./enumerator","./-internal","./utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var Enumerator = __dependency1__["default"]; + var PENDING = __dependency2__.PENDING; + var o_create = __dependency3__.o_create; + + function PromiseHash(Constructor, object, label) { + this._superConstructor(Constructor, object, true, label); + } + + __exports__["default"] = PromiseHash; + + PromiseHash.prototype = o_create(Enumerator.prototype); + PromiseHash.prototype._superConstructor = Enumerator; + PromiseHash.prototype._init = function() { + this._result = {}; + }; + + PromiseHash.prototype._validateInput = function(input) { + return input && typeof input === 'object'; + }; - 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. + PromiseHash.prototype._validationError = function() { + return new Error('Promise.hash must be called with an object'); + }; - @property testHelpers - @type {Object} - @default {} - */ - testHelpers: {}, + PromiseHash.prototype._enumerate = function() { + var promise = this.promise; + var input = this._input; + var results = []; - /** - This property will contain the original methods that were registered - on the `helperContainer` before `injectTestHelpers` is called. + for (var key in input) { + if (promise._state === PENDING && input.hasOwnProperty(key)) { + results.push({ + position: key, + entry: input[key] + }); + } + } - When `removeTestHelpers` is called, these methods are restored to the - `helperContainer`. + var length = results.length; + this._remaining = length; + var result; - @property originalMethods - @type {Object} - @default {} - @private - @since 1.3.0 - */ - originalMethods: {}, + for (var i = 0; promise._state === PENDING && i < length; i++) { + result = results[i]; + this._eachEntry(result.entry, result.position); + } + }; + }); +enifed("rsvp/promise", + ["./config","./instrument","./utils","./-internal","./promise/all","./promise/race","./promise/resolve","./promise/reject","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { + "use strict"; + var config = __dependency1__.config; + var instrument = __dependency2__["default"]; + var isFunction = __dependency3__.isFunction; + var now = __dependency3__.now; - /** - This property indicates whether or not this application is currently in - testing mode. This is set when `setupForTesting` is called on the current - application. + var noop = __dependency4__.noop; + var subscribe = __dependency4__.subscribe; + var initializePromise = __dependency4__.initializePromise; + var invokeCallback = __dependency4__.invokeCallback; + var FULFILLED = __dependency4__.FULFILLED; + var REJECTED = __dependency4__.REJECTED; - @property testing - @type {Boolean} - @default false - @since 1.3.0 - */ - testing: false, + var all = __dependency5__["default"]; + var race = __dependency6__["default"]; + var Resolve = __dependency7__["default"]; + var Reject = __dependency8__["default"]; - /** - 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). + var guidKey = 'rsvp_' + now() + '-'; + var counter = 0; - Example: + function needsResolver() { + throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); + } - ``` - App.setupForTesting(); - ``` + function needsNew() { + throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); + } + __exports__["default"] = Promise; + /** + Promise objects represent the eventual result of an asynchronous operation. The + primary way of interacting with a promise is through its `then` method, which + registers callbacks to receive either a promise’s eventual value or the reason + why the promise cannot be fulfilled. - @method setupForTesting - */ - setupForTesting: function() { - setupForTesting(); + Terminology + ----------- - this.testing = true; + - `promise` is an object or function with a `then` method whose behavior conforms to this specification. + - `thenable` is an object or function that defines a `then` method. + - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). + - `exception` is a value that is thrown using the throw statement. + - `reason` is a value that indicates why a promise was rejected. + - `settled` the final resting state of a promise, fulfilled or rejected. - this.Router.reopen({ - location: 'none' - }); - }, + A promise can be in one of three states: pending, fulfilled, or rejected. - /** - This will be used as the container to inject the test helpers into. By - default the helpers are injected into `window`. + Promises that are fulfilled have a fulfillment value and are in the fulfilled + state. Promises that are rejected have a rejection reason and are in the + rejected state. A fulfillment value is never a thenable. - @property helperContainer - @type {Object} The object to be used for test helpers. - @default window - @since 1.2.0 - */ - helperContainer: window, + Promises can also be said to *resolve* a value. If this value is also a + promise, then the original promise's settled state will match the value's + settled state. So a promise that *resolves* a promise that rejects will + itself reject, and a promise that *resolves* a promise that fulfills will + itself fulfill. - /** - 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`). - Any callbacks registered with `onInjectHelpers` will be called once the - helpers have been injected. + Basic Usage: + ------------ - Example: - ``` - App.injectTestHelpers(); - ``` + ```js + var promise = new Promise(function(resolve, reject) { + // on success + resolve(value); - @method injectTestHelpers - */ - injectTestHelpers: function(helperContainer) { - if (helperContainer) { this.helperContainer = helperContainer; } + // on failure + reject(reason); + }); - 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); - } + promise.then(function(value) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` - for(var i = 0, l = injectHelpersCallbacks.length; i < l; i++) { - injectHelpersCallbacks[i](this); - } - }, + Advanced Usage: + --------------- - /** - This removes all helpers that have been registered, and resets and functions - that were overridden by the helpers. + Promises shine when abstracting away asynchronous interactions such as + `XMLHttpRequest`s. - Example: + ```js + function getJSON(url) { + return new Promise(function(resolve, reject){ + var xhr = new XMLHttpRequest(); - ```javascript - App.removeTestHelpers(); - ``` + xhr.open('GET', url); + xhr.onreadystatechange = handler; + xhr.responseType = 'json'; + xhr.setRequestHeader('Accept', 'application/json'); + xhr.send(); - @public - @method removeTestHelpers - */ - removeTestHelpers: function() { - for (var name in helpers) { - this.helperContainer[name] = this.originalMethods[name]; - delete this.testHelpers[name]; - delete this.originalMethods[name]; - } + function handler() { + if (this.readyState === this.DONE) { + if (this.status === 200) { + resolve(this.response); + } else { + reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); + } + } + }; + }); } - }); - // 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); - }); - } - }; - } + getJSON('/posts.json').then(function(json) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` - Test.Promise = function() { - RSVP.Promise.apply(this, arguments); - Test.lastPromise = this; - }; + Unlike callbacks, promises are great composable primitives. - Test.Promise.prototype = create(RSVP.Promise.prototype); - Test.Promise.prototype.constructor = Test.Promise; + ```js + Promise.all([ + getJSON('/posts'), + getJSON('/comments') + ]).then(function(values){ + values[0] // => postsJSON + values[1] // => commentsJSON - // 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); - }; + return values; + }); + ``` - // 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; + @class RSVP.Promise + @param {function} resolver + @param {String} label optional string for labeling the promise. + Useful for tooling. + @constructor + */ + function Promise(resolver, label) { + this._id = counter++; + this._label = label; + this._state = undefined; + this._result = undefined; + this._subscribers = []; - // Reset lastPromise for nested helpers - Test.lastPromise = null; + if (config.instrument) { + instrument('created', this); + } - value = fn(val); + if (noop !== resolver) { + if (!isFunction(resolver)) { + needsResolver(); + } - lastPromise = Test.lastPromise; + if (!(this instanceof Promise)) { + needsNew(); + } - // 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; + initializePromise(this, resolver); } } - __exports__["default"] = Test; - }); -})(); + Promise.cast = Resolve; // deprecated + Promise.all = all; + Promise.race = race; + Promise.resolve = Resolve; + Promise.reject = Reject; -define("container/container", - ["container/inheriting_dict","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var InheritingDict = __dependency1__["default"]; + Promise.prototype = { + constructor: Promise, - // A lightweight container that helps to assemble and decouple components. - // Public api for the container is still in flux. - // The public api, specified on the application namespace should be considered the stable api. - function Container(parent) { - this.parent = parent; - this.children = []; + _guidKey: guidKey, - this.resolver = parent && parent.resolver || function() {}; + _onerror: function (reason) { + config.trigger('error', reason); + }, - this.registry = new InheritingDict(parent && parent.registry); - this.cache = new InheritingDict(parent && parent.cache); - this.factoryCache = new InheritingDict(parent && parent.factoryCache); - this.resolveCache = new InheritingDict(parent && parent.resolveCache); - this.typeInjections = new InheritingDict(parent && parent.typeInjections); - this.injections = {}; + /** + The primary way of interacting with a promise is through its `then` method, + which registers callbacks to receive either a promise's eventual value or the + reason why the promise cannot be fulfilled. - this.factoryTypeInjections = new InheritingDict(parent && parent.factoryTypeInjections); - this.factoryInjections = {}; + ```js + findUser().then(function(user){ + // user is available + }, function(reason){ + // user is unavailable, and you are given the reason why + }); + ``` - this._options = new InheritingDict(parent && parent._options); - this._typeOptions = new InheritingDict(parent && parent._typeOptions); - } + Chaining + -------- - Container.prototype = { + The return value of `then` is itself a promise. This second, 'downstream' + promise is resolved with the return value of the first promise's fulfillment + or rejection handler, or rejected if the handler throws an exception. - /** - @property parent - @type Container - @default null - */ - parent: null, + ```js + findUser().then(function (user) { + return user.name; + }, function (reason) { + return 'default name'; + }).then(function (userName) { + // If `findUser` fulfilled, `userName` will be the user's name, otherwise it + // will be `'default name'` + }); - /** - @property children - @type Array - @default [] - */ - children: null, + findUser().then(function (user) { + throw new Error('Found user, but still unhappy'); + }, function (reason) { + throw new Error('`findUser` rejected and we're unhappy'); + }).then(function (value) { + // never reached + }, function (reason) { + // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. + // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. + }); + ``` + If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. - /** - @property resolver - @type function - */ - resolver: null, + ```js + findUser().then(function (user) { + throw new PedagogicalException('Upstream error'); + }).then(function (value) { + // never reached + }).then(function (value) { + // never reached + }, function (reason) { + // The `PedgagocialException` is propagated all the way down to here + }); + ``` - /** - @property registry - @type InheritingDict - */ - registry: null, + Assimilation + ------------ - /** - @property cache - @type InheritingDict - */ - cache: null, + Sometimes the value you want to propagate to a downstream promise can only be + retrieved asynchronously. This can be achieved by returning a promise in the + fulfillment or rejection handler. The downstream promise will then be pending + until the returned promise is settled. This is called *assimilation*. - /** - @property typeInjections - @type InheritingDict - */ - typeInjections: null, + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // The user's comments are now available + }); + ``` - /** - @property injections - @type Object - @default {} - */ - injections: null, + If the assimliated promise rejects, then the downstream promise will also reject. - /** - @private + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // If `findCommentsByAuthor` fulfills, we'll have the value here + }, function (reason) { + // If `findCommentsByAuthor` rejects, we'll have the reason here + }); + ``` - @property _options - @type InheritingDict - @default null - */ - _options: null, + Simple Example + -------------- - /** - @private + Synchronous Example - @property _typeOptions - @type InheritingDict - */ - _typeOptions: null, + ```javascript + var result; - /** - Returns a new child of the current container. These children are configured - to correctly inherit from the current container. + try { + result = findResult(); + // success + } catch(reason) { + // failure + } + ``` - @method child - @return {Container} - */ - child: function() { - var container = new Container(this); - this.children.push(container); - return container; - }, + Errback Example - /** - Sets a key-value pair on the current container. If a parent container, - has the same key, once set on a child, the parent and child will diverge - as expected. + ```js + findResult(function(result, err){ + if (err) { + // failure + } else { + // success + } + }); + ``` - @method set - @param {Object} object - @param {String} key - @param {any} value - */ - set: function(object, key, value) { - object[key] = value; - }, + Promise Example; - /** - Registers a factory for later injection. + ```javascript + findResult().then(function(result){ + // success + }, function(reason){ + // failure + }); + ``` - Example: + Advanced Example + -------------- - ```javascript - var container = new Container(); + Synchronous Example - container.register('model:user', Person, {singleton: false }); - container.register('fruit:favorite', Orange); - container.register('communication:main', Email, {singleton: false}); - ``` + ```javascript + var author, books; - @method register - @param {String} fullName - @param {Function} factory - @param {Object} options - */ - register: function(fullName, factory, options) { - validateFullName(fullName); + try { + author = findAuthor(); + books = findBooksByAuthor(author); + // success + } catch(reason) { + // failure + } + ``` - if (factory === undefined) { - throw new TypeError('Attempting to register an unknown factory: `' + fullName + '`'); - } + Errback Example - var normalizedName = this.normalize(fullName); + ```js - if (this.cache.has(normalizedName)) { - throw new Error('Cannot re-register: `' + fullName +'`, as it has already been looked up.'); + function foundBooks(books) { + + } + + function failure(reason) { + + } + + findAuthor(function(author, err){ + if (err) { + failure(err); + // failure + } else { + try { + findBoooksByAuthor(author, function(books, err) { + if (err) { + failure(err); + } else { + try { + foundBooks(books); + } catch(reason) { + failure(reason); + } + } + }); + } catch(error) { + failure(err); + } + // success } + }); + ``` - this.registry.set(normalizedName, factory); - this._options.set(normalizedName, options || {}); - }, - - /** - Unregister a fullName + Promise Example; - ```javascript - var container = new Container(); - container.register('model:user', User); + ```javascript + findAuthor(). + then(findBooksByAuthor). + then(function(books){ + // found books + }).catch(function(reason){ + // something went wrong + }); + ``` - container.lookup('model:user') instanceof User //=> true + @method then + @param {Function} onFulfilled + @param {Function} onRejected + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} + */ + then: function(onFulfillment, onRejection, label) { + var parent = this; + var state = parent._state; - container.unregister('model:user') - container.lookup('model:user') === undefined //=> true - ``` + if (state === FULFILLED && !onFulfillment || state === REJECTED && !onRejection) { + if (config.instrument) { + instrument('chained', this, this); + } + return this; + } - @method unregister - @param {String} fullName - */ - unregister: function(fullName) { - validateFullName(fullName); + parent._onerror = null; - var normalizedName = this.normalize(fullName); + var child = new this.constructor(noop, label); + var result = parent._result; - this.registry.remove(normalizedName); - this.cache.remove(normalizedName); - this.factoryCache.remove(normalizedName); - this.resolveCache.remove(normalizedName); - this._options.remove(normalizedName); - }, + if (config.instrument) { + instrument('chained', parent, child); + } - /** - Given a fullName return the corresponding factory. + if (state) { + var callback = arguments[state - 1]; + config.async(function(){ + invokeCallback(state, child, callback, result); + }); + } else { + subscribe(parent, child, onFulfillment, onRejection); + } - By default `resolve` will retrieve the factory from - its container's registry. + return child; + }, - ```javascript - var container = new Container(); - container.register('api:twitter', Twitter); + /** + `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same + as the catch block of a try/catch statement. - container.resolve('api:twitter') // => Twitter - ``` + ```js + function findAuthor(){ + throw new Error('couldn't find that author'); + } - Optionally the container can be provided with a custom resolver. - If provided, `resolve` will first provide the custom resolver - the opportunity to resolve the fullName, otherwise it will fallback - to the registry. + // synchronous + try { + findAuthor(); + } catch(reason) { + // something went wrong + } - ```javascript - var container = new Container(); - container.resolver = function(fullName) { - // lookup via the module system of choice - }; + // async with promises + findAuthor().catch(function(reason){ + // something went wrong + }); + ``` - // the twitter factory is added to the module system - container.resolve('api:twitter') // => Twitter - ``` + @method catch + @param {Function} onRejection + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} + */ + 'catch': function(onRejection, label) { + return this.then(null, onRejection, label); + }, - @method resolve - @param {String} fullName - @return {Function} fullName's factory - */ - resolve: function(fullName) { - validateFullName(fullName); + /** + `finally` will be invoked regardless of the promise's fate just as native + try/catch/finally behaves - var normalizedName = this.normalize(fullName); - var cached = this.resolveCache.get(normalizedName); + Synchronous example: - if (cached) { return cached; } + ```js + findAuthor() { + if (Math.random() > 0.5) { + throw new Error(); + } + return new Author(); + } - var resolved = this.resolver(normalizedName) || this.registry.get(normalizedName); + try { + return findAuthor(); // succeed or fail + } catch(error) { + return findOtherAuther(); + } finally { + // always runs + // doesn't affect the return value + } + ``` - this.resolveCache.set(normalizedName, resolved); + Asynchronous example: - return resolved; - }, + ```js + findAuthor().catch(function(reason){ + return findOtherAuther(); + }).finally(function(){ + // author was either found, or not + }); + ``` - /** - A hook that can be used to describe how the resolver will - attempt to find the factory. + @method finally + @param {Function} callback + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} + */ + 'finally': function(callback, label) { + var constructor = this.constructor; - For example, the default Ember `.describe` returns the full - class name (including namespace) where Ember's resolver expects - to find the `fullName`. + return this.then(function(value) { + return constructor.resolve(callback()).then(function(){ + return value; + }); + }, function(reason) { + return constructor.resolve(callback()).then(function(){ + throw reason; + }); + }, label); + } + }; + }); +enifed("rsvp/promise/all", + ["../enumerator","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Enumerator = __dependency1__["default"]; - @method describe - @param {String} fullName - @return {string} described fullName - */ - describe: function(fullName) { - return fullName; - }, + /** + `RSVP.Promise.all` accepts an array of promises, and returns a new promise which + is fulfilled with an array of fulfillment values for the passed promises, or + rejected with the reason of the first passed promise to be rejected. It casts all + elements of the passed iterable to promises as it runs this algorithm. - /** - A hook to enable custom fullName normalization behaviour + Example: - @method normalize - @param {String} fullName - @return {string} normalized fullName - */ - normalize: function(fullName) { - return fullName; - }, + ```javascript + var promise1 = RSVP.resolve(1); + var promise2 = RSVP.resolve(2); + var promise3 = RSVP.resolve(3); + var promises = [ promise1, promise2, promise3 ]; - /** - @method makeToString + RSVP.Promise.all(promises).then(function(array){ + // The array here would be [ 1, 2, 3 ]; + }); + ``` - @param {any} factory - @param {string} fullName - @return {function} toString function - */ - makeToString: function(factory, fullName) { - return factory.toString(); - }, + If any of the `promises` given to `RSVP.all` are rejected, the first promise + that is rejected will be given as an argument to the returned promises's + rejection handler. For example: - /** - Given a fullName return a corresponding instance. + Example: - The default behaviour is for lookup to return a singleton instance. - The singleton is scoped to the container, allowing multiple containers - to all have their own locally scoped singletons. + ```javascript + var promise1 = RSVP.resolve(1); + var promise2 = RSVP.reject(new Error("2")); + var promise3 = RSVP.reject(new Error("3")); + var promises = [ promise1, promise2, promise3 ]; - ```javascript - var container = new Container(); - container.register('api:twitter', Twitter); + RSVP.Promise.all(promises).then(function(array){ + // Code here never runs because there are rejected promises! + }, function(error) { + // error.message === "2" + }); + ``` - var twitter = container.lookup('api:twitter'); + @method all + @static + @param {Array} entries array of promises + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} promise that is fulfilled when all `promises` have been + fulfilled, or rejected if any of them become rejected. + @static + */ + __exports__["default"] = function all(entries, label) { + return new Enumerator(this, entries, true /* abort on reject */, label).promise; + } + }); +enifed("rsvp/promise/race", + ["../utils","../-internal","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var isArray = __dependency1__.isArray; - twitter instanceof Twitter; // => true + var noop = __dependency2__.noop; + var resolve = __dependency2__.resolve; + var reject = __dependency2__.reject; + var subscribe = __dependency2__.subscribe; + var PENDING = __dependency2__.PENDING; - // by default the container will return singletons - var twitter2 = container.lookup('api:twitter'); - twitter2 instanceof Twitter; // => true + /** + `RSVP.Promise.race` returns a new promise which is settled in the same way as the + first passed promise to settle. - twitter === twitter2; //=> true - ``` + Example: - If singletons are not wanted an optional flag can be provided at lookup. + ```javascript + var promise1 = new RSVP.Promise(function(resolve, reject){ + setTimeout(function(){ + resolve('promise 1'); + }, 200); + }); - ```javascript - var container = new Container(); - container.register('api:twitter', Twitter); + var promise2 = new RSVP.Promise(function(resolve, reject){ + setTimeout(function(){ + resolve('promise 2'); + }, 100); + }); - var twitter = container.lookup('api:twitter', { singleton: false }); - var twitter2 = container.lookup('api:twitter', { singleton: false }); + RSVP.Promise.race([promise1, promise2]).then(function(result){ + // result === 'promise 2' because it was resolved before promise1 + // was resolved. + }); + ``` - twitter === twitter2; //=> false - ``` + `RSVP.Promise.race` is deterministic in that only the state of the first + settled promise matters. For example, even if other promises given to the + `promises` array argument are resolved, but the first settled promise has + become rejected before the other promises became fulfilled, the returned + promise will become rejected: - @method lookup - @param {String} fullName - @param {Object} options - @return {any} - */ - lookup: function(fullName, options) { - validateFullName(fullName); - return lookup(this, this.normalize(fullName), options); - }, + ```javascript + var promise1 = new RSVP.Promise(function(resolve, reject){ + setTimeout(function(){ + resolve('promise 1'); + }, 200); + }); - /** - Given a fullName return the corresponding factory. + var promise2 = new RSVP.Promise(function(resolve, reject){ + setTimeout(function(){ + reject(new Error('promise 2')); + }, 100); + }); - @method lookupFactory - @param {String} fullName - @return {any} - */ - lookupFactory: function(fullName) { - validateFullName(fullName); - return factoryFor(this, this.normalize(fullName)); - }, + RSVP.Promise.race([promise1, promise2]).then(function(result){ + // Code here never runs + }, function(reason){ + // reason.message === 'promise 2' because promise 2 became rejected before + // promise 1 became fulfilled + }); + ``` - /** - Given a fullName check if the container is aware of its factory - or singleton instance. + An example real-world use case is implementing timeouts: - @method has - @param {String} fullName - @return {Boolean} - */ - has: function(fullName) { - validateFullName(fullName); - return has(this, this.normalize(fullName)); - }, + ```javascript + RSVP.Promise.race([ajax('foo.json'), timeout(5000)]) + ``` - /** - Allow registering options for all factories of a type. + @method race + @static + @param {Array} promises array of promises to observe + @param {String} label optional string for describing the promise returned. + Useful for tooling. + @return {Promise} a promise which settles in the same way as the first passed + promise to settle. + */ + __exports__["default"] = function race(entries, label) { + /*jshint validthis:true */ + var Constructor = this; - ```javascript - var container = new Container(); + var promise = new Constructor(noop, label); - // if all of type `connection` must not be singletons - container.optionsForType('connection', { singleton: false }); + if (!isArray(entries)) { + reject(promise, new TypeError('You must pass an array to race.')); + return promise; + } - container.register('connection:twitter', TwitterConnection); - container.register('connection:facebook', FacebookConnection); + var length = entries.length; - var twitter = container.lookup('connection:twitter'); - var twitter2 = container.lookup('connection:twitter'); + function onFulfillment(value) { + resolve(promise, value); + } - twitter === twitter2; // => false + function onRejection(reason) { + reject(promise, reason); + } - var facebook = container.lookup('connection:facebook'); - var facebook2 = container.lookup('connection:facebook'); + for (var i = 0; promise._state === PENDING && i < length; i++) { + subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection); + } - facebook === facebook2; // => false - ``` + return promise; + } + }); +enifed("rsvp/promise/reject", + ["../-internal","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var noop = __dependency1__.noop; + var _reject = __dependency1__.reject; - @method optionsForType - @param {String} type - @param {Object} options - */ - optionsForType: function(type, options) { - if (this.parent) { illegalChildOperation('optionsForType'); } + /** + `RSVP.Promise.reject` returns a promise rejected with the passed `reason`. + It is shorthand for the following: - this._typeOptions.set(type, options); - }, + ```javascript + var promise = new RSVP.Promise(function(resolve, reject){ + reject(new Error('WHOOPS')); + }); - /** - @method options - @param {String} type - @param {Object} options - */ - options: function(type, options) { - this.optionsForType(type, options); - }, + promise.then(function(value){ + // Code here doesn't run because the promise is rejected! + }, function(reason){ + // reason.message === 'WHOOPS' + }); + ``` - /** - Used only via `injection`. + Instead of writing the above, your code now simply becomes the following: - Provides a specialized form of injection, specifically enabling - all objects of one type to be injected with a reference to another - object. + ```javascript + var promise = RSVP.Promise.reject(new Error('WHOOPS')); - For example, provided each object of type `controller` needed a `router`. - one would do the following: + promise.then(function(value){ + // Code here doesn't run because the promise is rejected! + }, function(reason){ + // reason.message === 'WHOOPS' + }); + ``` - ```javascript - var container = new Container(); + @method reject + @static + @param {Any} reason value that the returned promise will be rejected with. + @param {String} label optional string for identifying the returned promise. + Useful for tooling. + @return {Promise} a promise rejected with the given `reason`. + */ + __exports__["default"] = function reject(reason, label) { + /*jshint validthis:true */ + var Constructor = this; + var promise = new Constructor(noop, label); + _reject(promise, reason); + return promise; + } + }); +enifed("rsvp/promise/resolve", + ["../-internal","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var noop = __dependency1__.noop; + var _resolve = __dependency1__.resolve; - container.register('router:main', Router); - container.register('controller:user', UserController); - container.register('controller:post', PostController); + /** + `RSVP.Promise.resolve` returns a promise that will become resolved with the + passed `value`. It is shorthand for the following: - container.typeInjection('controller', 'router', 'router:main'); + ```javascript + var promise = new RSVP.Promise(function(resolve, reject){ + resolve(1); + }); - var user = container.lookup('controller:user'); - var post = container.lookup('controller:post'); + promise.then(function(value){ + // value === 1 + }); + ``` - user.router instanceof Router; //=> true - post.router instanceof Router; //=> true + Instead of writing the above, your code now simply becomes the following: - // both controllers share the same router - user.router === post.router; //=> true - ``` + ```javascript + var promise = RSVP.Promise.resolve(1); - @private - @method typeInjection - @param {String} type - @param {String} property - @param {String} fullName - */ - typeInjection: function(type, property, fullName) { - validateFullName(fullName); - if (this.parent) { illegalChildOperation('typeInjection'); } + promise.then(function(value){ + // value === 1 + }); + ``` - var fullNameType = fullName.split(':')[0]; - if(fullNameType === type) { - throw new Error('Cannot inject a `' + fullName + '` on other ' + type + '(s). Register the `' + fullName + '` as a different type and perform the typeInjection.'); - } - addTypeInjection(this.typeInjections, type, property, fullName); - }, + @method resolve + @static + @param {Any} value value that the returned promise will be resolved with + @param {String} label optional string for identifying the returned promise. + Useful for tooling. + @return {Promise} a promise that will become fulfilled with the given + `value` + */ + __exports__["default"] = function resolve(object, label) { + /*jshint validthis:true */ + var Constructor = this; - /** - Defines injection rules. + if (object && typeof object === 'object' && object.constructor === Constructor) { + return object; + } - These rules are used to inject dependencies onto objects when they - are instantiated. + var promise = new Constructor(noop, label); + _resolve(promise, object); + return promise; + } + }); +enifed("rsvp/race", + ["./promise","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Promise = __dependency1__["default"]; - Two forms of injections are possible: + /** + This is a convenient alias for `RSVP.Promise.race`. - * Injecting one fullName on another fullName - * Injecting one fullName on a type + @method race + @static + @for RSVP + @param {Array} array Array of promises. + @param {String} label An optional label. This is useful + for tooling. + */ + __exports__["default"] = function race(array, label) { + return Promise.race(array, label); + } + }); +enifed("rsvp/reject", + ["./promise","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Promise = __dependency1__["default"]; - Example: + /** + This is a convenient alias for `RSVP.Promise.reject`. - ```javascript - var container = new Container(); + @method reject + @static + @for RSVP + @param {Any} reason value that the returned promise will be rejected with. + @param {String} label optional string for identifying the returned promise. + Useful for tooling. + @return {Promise} a promise rejected with the given `reason`. + */ + __exports__["default"] = function reject(reason, label) { + return Promise.reject(reason, label); + } + }); +enifed("rsvp/resolve", + ["./promise","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Promise = __dependency1__["default"]; - container.register('source:main', Source); - container.register('model:user', User); - container.register('model:post', Post); + /** + This is a convenient alias for `RSVP.Promise.resolve`. - // injecting one fullName on another fullName - // eg. each user model gets a post model - container.injection('model:user', 'post', 'model:post'); + @method resolve + @static + @for RSVP + @param {Any} value value that the returned promise will be resolved with + @param {String} label optional string for identifying the returned promise. + Useful for tooling. + @return {Promise} a promise that will become fulfilled with the given + `value` + */ + __exports__["default"] = function resolve(value, label) { + return Promise.resolve(value, label); + } + }); +enifed("rsvp/rethrow", + ["exports"], + function(__exports__) { + "use strict"; + /** + `RSVP.rethrow` will rethrow an error on the next turn of the JavaScript event + loop in order to aid debugging. - // injecting one fullName on another type - container.injection('model', 'source', 'source:main'); + Promises A+ specifies that any exceptions that occur with a promise must be + caught by the promises implementation and bubbled to the last handler. For + this reason, it is recommended that you always specify a second rejection + handler function to `then`. However, `RSVP.rethrow` will throw the exception + outside of the promise, so it bubbles up to your console if in the browser, + or domain/cause uncaught exception in Node. `rethrow` will also throw the + error again so the error can be handled by the promise per the spec. - var user = container.lookup('model:user'); - var post = container.lookup('model:post'); + ```javascript + function throws(){ + throw new Error('Whoops!'); + } - user.source instanceof Source; //=> true - post.source instanceof Source; //=> true + var promise = new RSVP.Promise(function(resolve, reject){ + throws(); + }); - user.post instanceof Post; //=> true + promise.catch(RSVP.rethrow).then(function(){ + // Code here doesn't run because the promise became rejected due to an + // error! + }, function (err){ + // handle the error here + }); + ``` - // and both models share the same source - user.source === post.source; //=> true - ``` + The 'Whoops' error will be thrown on the next turn of the event loop + and you can watch for it in your console. You can also handle it using a + rejection handler given to `.then` or `.catch` on the returned promise. - @method injection - @param {String} factoryName - @param {String} property - @param {String} injectionName - */ - injection: function(fullName, property, injectionName) { - if (this.parent) { illegalChildOperation('injection'); } + @method rethrow + @static + @for RSVP + @param {Error} reason reason the promise became rejected. + @throws Error + @static + */ + __exports__["default"] = function rethrow(reason) { + setTimeout(function() { + throw reason; + }); + throw reason; + } + }); +enifed("rsvp/utils", + ["exports"], + function(__exports__) { + "use strict"; + function objectOrFunction(x) { + return typeof x === 'function' || (typeof x === 'object' && x !== null); + } - validateFullName(injectionName); - var normalizedInjectionName = this.normalize(injectionName); + __exports__.objectOrFunction = objectOrFunction;function isFunction(x) { + return typeof x === 'function'; + } - if (fullName.indexOf(':') === -1) { - return this.typeInjection(fullName, property, normalizedInjectionName); - } + __exports__.isFunction = isFunction;function isMaybeThenable(x) { + return typeof x === 'object' && x !== null; + } - validateFullName(fullName); - var normalizedName = this.normalize(fullName); + __exports__.isMaybeThenable = isMaybeThenable;var _isArray; + if (!Array.isArray) { + _isArray = function (x) { + return Object.prototype.toString.call(x) === '[object Array]'; + }; + } else { + _isArray = Array.isArray; + } - addInjection(this.injections, normalizedName, property, normalizedInjectionName); - }, + var isArray = _isArray; + __exports__.isArray = isArray; + // Date.now is not available in browsers < IE9 + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now#Compatibility + var now = Date.now || function() { return new Date().getTime(); }; + __exports__.now = now; + function F() { } + var o_create = (Object.create || function (o) { + if (arguments.length > 1) { + throw new Error('Second argument not supported'); + } + if (typeof o !== 'object') { + throw new TypeError('Argument must be an object'); + } + F.prototype = o; + return new F(); + }); + __exports__.o_create = o_create; + }); +enifed("simple-html-tokenizer", + ["./simple-html-tokenizer/tokenizer","./simple-html-tokenizer/tokenize","./simple-html-tokenizer/generator","./simple-html-tokenizer/generate","./simple-html-tokenizer/tokens","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; + /*jshint boss:true*/ + var Tokenizer = __dependency1__["default"]; + var tokenize = __dependency2__["default"]; + var Generator = __dependency3__["default"]; + var generate = __dependency4__["default"]; + var StartTag = __dependency5__.StartTag; + var EndTag = __dependency5__.EndTag; + var Chars = __dependency5__.Chars; + var Comment = __dependency5__.Comment; + + __exports__.Tokenizer = Tokenizer; + __exports__.tokenize = tokenize; + __exports__.Generator = Generator; + __exports__.generate = generate; + __exports__.StartTag = StartTag; + __exports__.EndTag = EndTag; + __exports__.Chars = Chars; + __exports__.Comment = Comment; + }); +enifed("simple-html-tokenizer/char-refs/full", + ["exports"], + function(__exports__) { + "use strict"; + __exports__["default"] = { + AElig: [198], + AMP: [38], + Aacute: [193], + Abreve: [258], + Acirc: [194], + Acy: [1040], + Afr: [120068], + Agrave: [192], + Alpha: [913], + Amacr: [256], + And: [10835], + Aogon: [260], + Aopf: [120120], + ApplyFunction: [8289], + Aring: [197], + Ascr: [119964], + Assign: [8788], + Atilde: [195], + Auml: [196], + Backslash: [8726], + Barv: [10983], + Barwed: [8966], + Bcy: [1041], + Because: [8757], + Bernoullis: [8492], + Beta: [914], + Bfr: [120069], + Bopf: [120121], + Breve: [728], + Bscr: [8492], + Bumpeq: [8782], + CHcy: [1063], + COPY: [169], + Cacute: [262], + Cap: [8914], + CapitalDifferentialD: [8517], + Cayleys: [8493], + Ccaron: [268], + Ccedil: [199], + Ccirc: [264], + Cconint: [8752], + Cdot: [266], + Cedilla: [184], + CenterDot: [183], + Cfr: [8493], + Chi: [935], + CircleDot: [8857], + CircleMinus: [8854], + CirclePlus: [8853], + CircleTimes: [8855], + ClockwiseContourIntegral: [8754], + CloseCurlyDoubleQuote: [8221], + CloseCurlyQuote: [8217], + Colon: [8759], + Colone: [10868], + Congruent: [8801], + Conint: [8751], + ContourIntegral: [8750], + Copf: [8450], + Coproduct: [8720], + CounterClockwiseContourIntegral: [8755], + Cross: [10799], + Cscr: [119966], + Cup: [8915], + CupCap: [8781], + DD: [8517], + DDotrahd: [10513], + DJcy: [1026], + DScy: [1029], + DZcy: [1039], + Dagger: [8225], + Darr: [8609], + Dashv: [10980], + Dcaron: [270], + Dcy: [1044], + Del: [8711], + Delta: [916], + Dfr: [120071], + DiacriticalAcute: [180], + DiacriticalDot: [729], + DiacriticalDoubleAcute: [733], + DiacriticalGrave: [96], + DiacriticalTilde: [732], + Diamond: [8900], + DifferentialD: [8518], + Dopf: [120123], + Dot: [168], + DotDot: [8412], + DotEqual: [8784], + DoubleContourIntegral: [8751], + DoubleDot: [168], + DoubleDownArrow: [8659], + DoubleLeftArrow: [8656], + DoubleLeftRightArrow: [8660], + DoubleLeftTee: [10980], + DoubleLongLeftArrow: [10232], + DoubleLongLeftRightArrow: [10234], + DoubleLongRightArrow: [10233], + DoubleRightArrow: [8658], + DoubleRightTee: [8872], + DoubleUpArrow: [8657], + DoubleUpDownArrow: [8661], + DoubleVerticalBar: [8741], + DownArrow: [8595], + DownArrowBar: [10515], + DownArrowUpArrow: [8693], + DownBreve: [785], + DownLeftRightVector: [10576], + DownLeftTeeVector: [10590], + DownLeftVector: [8637], + DownLeftVectorBar: [10582], + DownRightTeeVector: [10591], + DownRightVector: [8641], + DownRightVectorBar: [10583], + DownTee: [8868], + DownTeeArrow: [8615], + Downarrow: [8659], + Dscr: [119967], + Dstrok: [272], + ENG: [330], + ETH: [208], + Eacute: [201], + Ecaron: [282], + Ecirc: [202], + Ecy: [1069], + Edot: [278], + Efr: [120072], + Egrave: [200], + Element: [8712], + Emacr: [274], + EmptySmallSquare: [9723], + EmptyVerySmallSquare: [9643], + Eogon: [280], + Eopf: [120124], + Epsilon: [917], + Equal: [10869], + EqualTilde: [8770], + Equilibrium: [8652], + Escr: [8496], + Esim: [10867], + Eta: [919], + Euml: [203], + Exists: [8707], + ExponentialE: [8519], + Fcy: [1060], + Ffr: [120073], + FilledSmallSquare: [9724], + FilledVerySmallSquare: [9642], + Fopf: [120125], + ForAll: [8704], + Fouriertrf: [8497], + Fscr: [8497], + GJcy: [1027], + GT: [62], + Gamma: [915], + Gammad: [988], + Gbreve: [286], + Gcedil: [290], + Gcirc: [284], + Gcy: [1043], + Gdot: [288], + Gfr: [120074], + Gg: [8921], + Gopf: [120126], + GreaterEqual: [8805], + GreaterEqualLess: [8923], + GreaterFullEqual: [8807], + GreaterGreater: [10914], + GreaterLess: [8823], + GreaterSlantEqual: [10878], + GreaterTilde: [8819], + Gscr: [119970], + Gt: [8811], + HARDcy: [1066], + Hacek: [711], + Hat: [94], + Hcirc: [292], + Hfr: [8460], + HilbertSpace: [8459], + Hopf: [8461], + HorizontalLine: [9472], + Hscr: [8459], + Hstrok: [294], + HumpDownHump: [8782], + HumpEqual: [8783], + IEcy: [1045], + IJlig: [306], + IOcy: [1025], + Iacute: [205], + Icirc: [206], + Icy: [1048], + Idot: [304], + Ifr: [8465], + Igrave: [204], + Im: [8465], + Imacr: [298], + ImaginaryI: [8520], + Implies: [8658], + Int: [8748], + Integral: [8747], + Intersection: [8898], + InvisibleComma: [8291], + InvisibleTimes: [8290], + Iogon: [302], + Iopf: [120128], + Iota: [921], + Iscr: [8464], + Itilde: [296], + Iukcy: [1030], + Iuml: [207], + Jcirc: [308], + Jcy: [1049], + Jfr: [120077], + Jopf: [120129], + Jscr: [119973], + Jsercy: [1032], + Jukcy: [1028], + KHcy: [1061], + KJcy: [1036], + Kappa: [922], + Kcedil: [310], + Kcy: [1050], + Kfr: [120078], + Kopf: [120130], + Kscr: [119974], + LJcy: [1033], + LT: [60], + Lacute: [313], + Lambda: [923], + Lang: [10218], + Laplacetrf: [8466], + Larr: [8606], + Lcaron: [317], + Lcedil: [315], + Lcy: [1051], + LeftAngleBracket: [10216], + LeftArrow: [8592], + LeftArrowBar: [8676], + LeftArrowRightArrow: [8646], + LeftCeiling: [8968], + LeftDoubleBracket: [10214], + LeftDownTeeVector: [10593], + LeftDownVector: [8643], + LeftDownVectorBar: [10585], + LeftFloor: [8970], + LeftRightArrow: [8596], + LeftRightVector: [10574], + LeftTee: [8867], + LeftTeeArrow: [8612], + LeftTeeVector: [10586], + LeftTriangle: [8882], + LeftTriangleBar: [10703], + LeftTriangleEqual: [8884], + LeftUpDownVector: [10577], + LeftUpTeeVector: [10592], + LeftUpVector: [8639], + LeftUpVectorBar: [10584], + LeftVector: [8636], + LeftVectorBar: [10578], + Leftarrow: [8656], + Leftrightarrow: [8660], + LessEqualGreater: [8922], + LessFullEqual: [8806], + LessGreater: [8822], + LessLess: [10913], + LessSlantEqual: [10877], + LessTilde: [8818], + Lfr: [120079], + Ll: [8920], + Lleftarrow: [8666], + Lmidot: [319], + LongLeftArrow: [10229], + LongLeftRightArrow: [10231], + LongRightArrow: [10230], + Longleftarrow: [10232], + Longleftrightarrow: [10234], + Longrightarrow: [10233], + Lopf: [120131], + LowerLeftArrow: [8601], + LowerRightArrow: [8600], + Lscr: [8466], + Lsh: [8624], + Lstrok: [321], + Lt: [8810], + Map: [10501], + Mcy: [1052], + MediumSpace: [8287], + Mellintrf: [8499], + Mfr: [120080], + MinusPlus: [8723], + Mopf: [120132], + Mscr: [8499], + Mu: [924], + NJcy: [1034], + Nacute: [323], + Ncaron: [327], + Ncedil: [325], + Ncy: [1053], + NegativeMediumSpace: [8203], + NegativeThickSpace: [8203], + NegativeThinSpace: [8203], + NegativeVeryThinSpace: [8203], + NestedGreaterGreater: [8811], + NestedLessLess: [8810], + NewLine: [10], + Nfr: [120081], + NoBreak: [8288], + NonBreakingSpace: [160], + Nopf: [8469], + Not: [10988], + NotCongruent: [8802], + NotCupCap: [8813], + NotDoubleVerticalBar: [8742], + NotElement: [8713], + NotEqual: [8800], + NotEqualTilde: [8770, 824], + NotExists: [8708], + NotGreater: [8815], + NotGreaterEqual: [8817], + NotGreaterFullEqual: [8807, 824], + NotGreaterGreater: [8811, 824], + NotGreaterLess: [8825], + NotGreaterSlantEqual: [10878, 824], + NotGreaterTilde: [8821], + NotHumpDownHump: [8782, 824], + NotHumpEqual: [8783, 824], + NotLeftTriangle: [8938], + NotLeftTriangleBar: [10703, 824], + NotLeftTriangleEqual: [8940], + NotLess: [8814], + NotLessEqual: [8816], + NotLessGreater: [8824], + NotLessLess: [8810, 824], + NotLessSlantEqual: [10877, 824], + NotLessTilde: [8820], + NotNestedGreaterGreater: [10914, 824], + NotNestedLessLess: [10913, 824], + NotPrecedes: [8832], + NotPrecedesEqual: [10927, 824], + NotPrecedesSlantEqual: [8928], + NotReverseElement: [8716], + NotRightTriangle: [8939], + NotRightTriangleBar: [10704, 824], + NotRightTriangleEqual: [8941], + NotSquareSubset: [8847, 824], + NotSquareSubsetEqual: [8930], + NotSquareSuperset: [8848, 824], + NotSquareSupersetEqual: [8931], + NotSubset: [8834, 8402], + NotSubsetEqual: [8840], + NotSucceeds: [8833], + NotSucceedsEqual: [10928, 824], + NotSucceedsSlantEqual: [8929], + NotSucceedsTilde: [8831, 824], + NotSuperset: [8835, 8402], + NotSupersetEqual: [8841], + NotTilde: [8769], + NotTildeEqual: [8772], + NotTildeFullEqual: [8775], + NotTildeTilde: [8777], + NotVerticalBar: [8740], + Nscr: [119977], + Ntilde: [209], + Nu: [925], + OElig: [338], + Oacute: [211], + Ocirc: [212], + Ocy: [1054], + Odblac: [336], + Ofr: [120082], + Ograve: [210], + Omacr: [332], + Omega: [937], + Omicron: [927], + Oopf: [120134], + OpenCurlyDoubleQuote: [8220], + OpenCurlyQuote: [8216], + Or: [10836], + Oscr: [119978], + Oslash: [216], + Otilde: [213], + Otimes: [10807], + Ouml: [214], + OverBar: [8254], + OverBrace: [9182], + OverBracket: [9140], + OverParenthesis: [9180], + PartialD: [8706], + Pcy: [1055], + Pfr: [120083], + Phi: [934], + Pi: [928], + PlusMinus: [177], + Poincareplane: [8460], + Popf: [8473], + Pr: [10939], + Precedes: [8826], + PrecedesEqual: [10927], + PrecedesSlantEqual: [8828], + PrecedesTilde: [8830], + Prime: [8243], + Product: [8719], + Proportion: [8759], + Proportional: [8733], + Pscr: [119979], + Psi: [936], + QUOT: [34], + Qfr: [120084], + Qopf: [8474], + Qscr: [119980], + RBarr: [10512], + REG: [174], + Racute: [340], + Rang: [10219], + Rarr: [8608], + Rarrtl: [10518], + Rcaron: [344], + Rcedil: [342], + Rcy: [1056], + Re: [8476], + ReverseElement: [8715], + ReverseEquilibrium: [8651], + ReverseUpEquilibrium: [10607], + Rfr: [8476], + Rho: [929], + RightAngleBracket: [10217], + RightArrow: [8594], + RightArrowBar: [8677], + RightArrowLeftArrow: [8644], + RightCeiling: [8969], + RightDoubleBracket: [10215], + RightDownTeeVector: [10589], + RightDownVector: [8642], + RightDownVectorBar: [10581], + RightFloor: [8971], + RightTee: [8866], + RightTeeArrow: [8614], + RightTeeVector: [10587], + RightTriangle: [8883], + RightTriangleBar: [10704], + RightTriangleEqual: [8885], + RightUpDownVector: [10575], + RightUpTeeVector: [10588], + RightUpVector: [8638], + RightUpVectorBar: [10580], + RightVector: [8640], + RightVectorBar: [10579], + Rightarrow: [8658], + Ropf: [8477], + RoundImplies: [10608], + Rrightarrow: [8667], + Rscr: [8475], + Rsh: [8625], + RuleDelayed: [10740], + SHCHcy: [1065], + SHcy: [1064], + SOFTcy: [1068], + Sacute: [346], + Sc: [10940], + Scaron: [352], + Scedil: [350], + Scirc: [348], + Scy: [1057], + Sfr: [120086], + ShortDownArrow: [8595], + ShortLeftArrow: [8592], + ShortRightArrow: [8594], + ShortUpArrow: [8593], + Sigma: [931], + SmallCircle: [8728], + Sopf: [120138], + Sqrt: [8730], + Square: [9633], + SquareIntersection: [8851], + SquareSubset: [8847], + SquareSubsetEqual: [8849], + SquareSuperset: [8848], + SquareSupersetEqual: [8850], + SquareUnion: [8852], + Sscr: [119982], + Star: [8902], + Sub: [8912], + Subset: [8912], + SubsetEqual: [8838], + Succeeds: [8827], + SucceedsEqual: [10928], + SucceedsSlantEqual: [8829], + SucceedsTilde: [8831], + SuchThat: [8715], + Sum: [8721], + Sup: [8913], + Superset: [8835], + SupersetEqual: [8839], + Supset: [8913], + THORN: [222], + TRADE: [8482], + TSHcy: [1035], + TScy: [1062], + Tab: [9], + Tau: [932], + Tcaron: [356], + Tcedil: [354], + Tcy: [1058], + Tfr: [120087], + Therefore: [8756], + Theta: [920], + ThickSpace: [8287, 8202], + ThinSpace: [8201], + Tilde: [8764], + TildeEqual: [8771], + TildeFullEqual: [8773], + TildeTilde: [8776], + Topf: [120139], + TripleDot: [8411], + Tscr: [119983], + Tstrok: [358], + Uacute: [218], + Uarr: [8607], + Uarrocir: [10569], + Ubrcy: [1038], + Ubreve: [364], + Ucirc: [219], + Ucy: [1059], + Udblac: [368], + Ufr: [120088], + Ugrave: [217], + Umacr: [362], + UnderBar: [95], + UnderBrace: [9183], + UnderBracket: [9141], + UnderParenthesis: [9181], + Union: [8899], + UnionPlus: [8846], + Uogon: [370], + Uopf: [120140], + UpArrow: [8593], + UpArrowBar: [10514], + UpArrowDownArrow: [8645], + UpDownArrow: [8597], + UpEquilibrium: [10606], + UpTee: [8869], + UpTeeArrow: [8613], + Uparrow: [8657], + Updownarrow: [8661], + UpperLeftArrow: [8598], + UpperRightArrow: [8599], + Upsi: [978], + Upsilon: [933], + Uring: [366], + Uscr: [119984], + Utilde: [360], + Uuml: [220], + VDash: [8875], + Vbar: [10987], + Vcy: [1042], + Vdash: [8873], + Vdashl: [10982], + Vee: [8897], + Verbar: [8214], + Vert: [8214], + VerticalBar: [8739], + VerticalLine: [124], + VerticalSeparator: [10072], + VerticalTilde: [8768], + VeryThinSpace: [8202], + Vfr: [120089], + Vopf: [120141], + Vscr: [119985], + Vvdash: [8874], + Wcirc: [372], + Wedge: [8896], + Wfr: [120090], + Wopf: [120142], + Wscr: [119986], + Xfr: [120091], + Xi: [926], + Xopf: [120143], + Xscr: [119987], + YAcy: [1071], + YIcy: [1031], + YUcy: [1070], + Yacute: [221], + Ycirc: [374], + Ycy: [1067], + Yfr: [120092], + Yopf: [120144], + Yscr: [119988], + Yuml: [376], + ZHcy: [1046], + Zacute: [377], + Zcaron: [381], + Zcy: [1047], + Zdot: [379], + ZeroWidthSpace: [8203], + Zeta: [918], + Zfr: [8488], + Zopf: [8484], + Zscr: [119989], + aacute: [225], + abreve: [259], + ac: [8766], + acE: [8766, 819], + acd: [8767], + acirc: [226], + acute: [180], + acy: [1072], + aelig: [230], + af: [8289], + afr: [120094], + agrave: [224], + alefsym: [8501], + aleph: [8501], + alpha: [945], + amacr: [257], + amalg: [10815], + amp: [38], + and: [8743], + andand: [10837], + andd: [10844], + andslope: [10840], + andv: [10842], + ang: [8736], + ange: [10660], + angle: [8736], + angmsd: [8737], + angmsdaa: [10664], + angmsdab: [10665], + angmsdac: [10666], + angmsdad: [10667], + angmsdae: [10668], + angmsdaf: [10669], + angmsdag: [10670], + angmsdah: [10671], + angrt: [8735], + angrtvb: [8894], + angrtvbd: [10653], + angsph: [8738], + angst: [197], + angzarr: [9084], + aogon: [261], + aopf: [120146], + ap: [8776], + apE: [10864], + apacir: [10863], + ape: [8778], + apid: [8779], + apos: [39], + approx: [8776], + approxeq: [8778], + aring: [229], + ascr: [119990], + ast: [42], + asymp: [8776], + asympeq: [8781], + atilde: [227], + auml: [228], + awconint: [8755], + awint: [10769], + bNot: [10989], + backcong: [8780], + backepsilon: [1014], + backprime: [8245], + backsim: [8765], + backsimeq: [8909], + barvee: [8893], + barwed: [8965], + barwedge: [8965], + bbrk: [9141], + bbrktbrk: [9142], + bcong: [8780], + bcy: [1073], + bdquo: [8222], + becaus: [8757], + because: [8757], + bemptyv: [10672], + bepsi: [1014], + bernou: [8492], + beta: [946], + beth: [8502], + between: [8812], + bfr: [120095], + bigcap: [8898], + bigcirc: [9711], + bigcup: [8899], + bigodot: [10752], + bigoplus: [10753], + bigotimes: [10754], + bigsqcup: [10758], + bigstar: [9733], + bigtriangledown: [9661], + bigtriangleup: [9651], + biguplus: [10756], + bigvee: [8897], + bigwedge: [8896], + bkarow: [10509], + blacklozenge: [10731], + blacksquare: [9642], + blacktriangle: [9652], + blacktriangledown: [9662], + blacktriangleleft: [9666], + blacktriangleright: [9656], + blank: [9251], + blk12: [9618], + blk14: [9617], + blk34: [9619], + block: [9608], + bne: [61, 8421], + bnequiv: [8801, 8421], + bnot: [8976], + bopf: [120147], + bot: [8869], + bottom: [8869], + bowtie: [8904], + boxDL: [9559], + boxDR: [9556], + boxDl: [9558], + boxDr: [9555], + boxH: [9552], + boxHD: [9574], + boxHU: [9577], + boxHd: [9572], + boxHu: [9575], + boxUL: [9565], + boxUR: [9562], + boxUl: [9564], + boxUr: [9561], + boxV: [9553], + boxVH: [9580], + boxVL: [9571], + boxVR: [9568], + boxVh: [9579], + boxVl: [9570], + boxVr: [9567], + boxbox: [10697], + boxdL: [9557], + boxdR: [9554], + boxdl: [9488], + boxdr: [9484], + boxh: [9472], + boxhD: [9573], + boxhU: [9576], + boxhd: [9516], + boxhu: [9524], + boxminus: [8863], + boxplus: [8862], + boxtimes: [8864], + boxuL: [9563], + boxuR: [9560], + boxul: [9496], + boxur: [9492], + boxv: [9474], + boxvH: [9578], + boxvL: [9569], + boxvR: [9566], + boxvh: [9532], + boxvl: [9508], + boxvr: [9500], + bprime: [8245], + breve: [728], + brvbar: [166], + bscr: [119991], + bsemi: [8271], + bsim: [8765], + bsime: [8909], + bsol: [92], + bsolb: [10693], + bsolhsub: [10184], + bull: [8226], + bullet: [8226], + bump: [8782], + bumpE: [10926], + bumpe: [8783], + bumpeq: [8783], + cacute: [263], + cap: [8745], + capand: [10820], + capbrcup: [10825], + capcap: [10827], + capcup: [10823], + capdot: [10816], + caps: [8745, 65024], + caret: [8257], + caron: [711], + ccaps: [10829], + ccaron: [269], + ccedil: [231], + ccirc: [265], + ccups: [10828], + ccupssm: [10832], + cdot: [267], + cedil: [184], + cemptyv: [10674], + cent: [162], + centerdot: [183], + cfr: [120096], + chcy: [1095], + check: [10003], + checkmark: [10003], + chi: [967], + cir: [9675], + cirE: [10691], + circ: [710], + circeq: [8791], + circlearrowleft: [8634], + circlearrowright: [8635], + circledR: [174], + circledS: [9416], + circledast: [8859], + circledcirc: [8858], + circleddash: [8861], + cire: [8791], + cirfnint: [10768], + cirmid: [10991], + cirscir: [10690], + clubs: [9827], + clubsuit: [9827], + colon: [58], + colone: [8788], + coloneq: [8788], + comma: [44], + commat: [64], + comp: [8705], + compfn: [8728], + complement: [8705], + complexes: [8450], + cong: [8773], + congdot: [10861], + conint: [8750], + copf: [120148], + coprod: [8720], + copy: [169], + copysr: [8471], + crarr: [8629], + cross: [10007], + cscr: [119992], + csub: [10959], + csube: [10961], + csup: [10960], + csupe: [10962], + ctdot: [8943], + cudarrl: [10552], + cudarrr: [10549], + cuepr: [8926], + cuesc: [8927], + cularr: [8630], + cularrp: [10557], + cup: [8746], + cupbrcap: [10824], + cupcap: [10822], + cupcup: [10826], + cupdot: [8845], + cupor: [10821], + cups: [8746, 65024], + curarr: [8631], + curarrm: [10556], + curlyeqprec: [8926], + curlyeqsucc: [8927], + curlyvee: [8910], + curlywedge: [8911], + curren: [164], + curvearrowleft: [8630], + curvearrowright: [8631], + cuvee: [8910], + cuwed: [8911], + cwconint: [8754], + cwint: [8753], + cylcty: [9005], + dArr: [8659], + dHar: [10597], + dagger: [8224], + daleth: [8504], + darr: [8595], + dash: [8208], + dashv: [8867], + dbkarow: [10511], + dblac: [733], + dcaron: [271], + dcy: [1076], + dd: [8518], + ddagger: [8225], + ddarr: [8650], + ddotseq: [10871], + deg: [176], + delta: [948], + demptyv: [10673], + dfisht: [10623], + dfr: [120097], + dharl: [8643], + dharr: [8642], + diam: [8900], + diamond: [8900], + diamondsuit: [9830], + diams: [9830], + die: [168], + digamma: [989], + disin: [8946], + div: [247], + divide: [247], + divideontimes: [8903], + divonx: [8903], + djcy: [1106], + dlcorn: [8990], + dlcrop: [8973], + dollar: [36], + dopf: [120149], + dot: [729], + doteq: [8784], + doteqdot: [8785], + dotminus: [8760], + dotplus: [8724], + dotsquare: [8865], + doublebarwedge: [8966], + downarrow: [8595], + downdownarrows: [8650], + downharpoonleft: [8643], + downharpoonright: [8642], + drbkarow: [10512], + drcorn: [8991], + drcrop: [8972], + dscr: [119993], + dscy: [1109], + dsol: [10742], + dstrok: [273], + dtdot: [8945], + dtri: [9663], + dtrif: [9662], + duarr: [8693], + duhar: [10607], + dwangle: [10662], + dzcy: [1119], + dzigrarr: [10239], + eDDot: [10871], + eDot: [8785], + eacute: [233], + easter: [10862], + ecaron: [283], + ecir: [8790], + ecirc: [234], + ecolon: [8789], + ecy: [1101], + edot: [279], + ee: [8519], + efDot: [8786], + efr: [120098], + eg: [10906], + egrave: [232], + egs: [10902], + egsdot: [10904], + el: [10905], + elinters: [9191], + ell: [8467], + els: [10901], + elsdot: [10903], + emacr: [275], + empty: [8709], + emptyset: [8709], + emptyv: [8709], + emsp: [8195], + emsp13: [8196], + emsp14: [8197], + eng: [331], + ensp: [8194], + eogon: [281], + eopf: [120150], + epar: [8917], + eparsl: [10723], + eplus: [10865], + epsi: [949], + epsilon: [949], + epsiv: [1013], + eqcirc: [8790], + eqcolon: [8789], + eqsim: [8770], + eqslantgtr: [10902], + eqslantless: [10901], + equals: [61], + equest: [8799], + equiv: [8801], + equivDD: [10872], + eqvparsl: [10725], + erDot: [8787], + erarr: [10609], + escr: [8495], + esdot: [8784], + esim: [8770], + eta: [951], + eth: [240], + euml: [235], + euro: [8364], + excl: [33], + exist: [8707], + expectation: [8496], + exponentiale: [8519], + fallingdotseq: [8786], + fcy: [1092], + female: [9792], + ffilig: [64259], + fflig: [64256], + ffllig: [64260], + ffr: [120099], + filig: [64257], + fjlig: [102, 106], + flat: [9837], + fllig: [64258], + fltns: [9649], + fnof: [402], + fopf: [120151], + forall: [8704], + fork: [8916], + forkv: [10969], + fpartint: [10765], + frac12: [189], + frac13: [8531], + frac14: [188], + frac15: [8533], + frac16: [8537], + frac18: [8539], + frac23: [8532], + frac25: [8534], + frac34: [190], + frac35: [8535], + frac38: [8540], + frac45: [8536], + frac56: [8538], + frac58: [8541], + frac78: [8542], + frasl: [8260], + frown: [8994], + fscr: [119995], + gE: [8807], + gEl: [10892], + gacute: [501], + gamma: [947], + gammad: [989], + gap: [10886], + gbreve: [287], + gcirc: [285], + gcy: [1075], + gdot: [289], + ge: [8805], + gel: [8923], + geq: [8805], + geqq: [8807], + geqslant: [10878], + ges: [10878], + gescc: [10921], + gesdot: [10880], + gesdoto: [10882], + gesdotol: [10884], + gesl: [8923, 65024], + gesles: [10900], + gfr: [120100], + gg: [8811], + ggg: [8921], + gimel: [8503], + gjcy: [1107], + gl: [8823], + glE: [10898], + gla: [10917], + glj: [10916], + gnE: [8809], + gnap: [10890], + gnapprox: [10890], + gne: [10888], + gneq: [10888], + gneqq: [8809], + gnsim: [8935], + gopf: [120152], + grave: [96], + gscr: [8458], + gsim: [8819], + gsime: [10894], + gsiml: [10896], + gt: [62], + gtcc: [10919], + gtcir: [10874], + gtdot: [8919], + gtlPar: [10645], + gtquest: [10876], + gtrapprox: [10886], + gtrarr: [10616], + gtrdot: [8919], + gtreqless: [8923], + gtreqqless: [10892], + gtrless: [8823], + gtrsim: [8819], + gvertneqq: [8809, 65024], + gvnE: [8809, 65024], + hArr: [8660], + hairsp: [8202], + half: [189], + hamilt: [8459], + hardcy: [1098], + harr: [8596], + harrcir: [10568], + harrw: [8621], + hbar: [8463], + hcirc: [293], + hearts: [9829], + heartsuit: [9829], + hellip: [8230], + hercon: [8889], + hfr: [120101], + hksearow: [10533], + hkswarow: [10534], + hoarr: [8703], + homtht: [8763], + hookleftarrow: [8617], + hookrightarrow: [8618], + hopf: [120153], + horbar: [8213], + hscr: [119997], + hslash: [8463], + hstrok: [295], + hybull: [8259], + hyphen: [8208], + iacute: [237], + ic: [8291], + icirc: [238], + icy: [1080], + iecy: [1077], + iexcl: [161], + iff: [8660], + ifr: [120102], + igrave: [236], + ii: [8520], + iiiint: [10764], + iiint: [8749], + iinfin: [10716], + iiota: [8489], + ijlig: [307], + imacr: [299], + image: [8465], + imagline: [8464], + imagpart: [8465], + imath: [305], + imof: [8887], + imped: [437], + "in": [8712], + incare: [8453], + infin: [8734], + infintie: [10717], + inodot: [305], + "int": [8747], + intcal: [8890], + integers: [8484], + intercal: [8890], + intlarhk: [10775], + intprod: [10812], + iocy: [1105], + iogon: [303], + iopf: [120154], + iota: [953], + iprod: [10812], + iquest: [191], + iscr: [119998], + isin: [8712], + isinE: [8953], + isindot: [8949], + isins: [8948], + isinsv: [8947], + isinv: [8712], + it: [8290], + itilde: [297], + iukcy: [1110], + iuml: [239], + jcirc: [309], + jcy: [1081], + jfr: [120103], + jmath: [567], + jopf: [120155], + jscr: [119999], + jsercy: [1112], + jukcy: [1108], + kappa: [954], + kappav: [1008], + kcedil: [311], + kcy: [1082], + kfr: [120104], + kgreen: [312], + khcy: [1093], + kjcy: [1116], + kopf: [120156], + kscr: [120000], + lAarr: [8666], + lArr: [8656], + lAtail: [10523], + lBarr: [10510], + lE: [8806], + lEg: [10891], + lHar: [10594], + lacute: [314], + laemptyv: [10676], + lagran: [8466], + lambda: [955], + lang: [10216], + langd: [10641], + langle: [10216], + lap: [10885], + laquo: [171], + larr: [8592], + larrb: [8676], + larrbfs: [10527], + larrfs: [10525], + larrhk: [8617], + larrlp: [8619], + larrpl: [10553], + larrsim: [10611], + larrtl: [8610], + lat: [10923], + latail: [10521], + late: [10925], + lates: [10925, 65024], + lbarr: [10508], + lbbrk: [10098], + lbrace: [123], + lbrack: [91], + lbrke: [10635], + lbrksld: [10639], + lbrkslu: [10637], + lcaron: [318], + lcedil: [316], + lceil: [8968], + lcub: [123], + lcy: [1083], + ldca: [10550], + ldquo: [8220], + ldquor: [8222], + ldrdhar: [10599], + ldrushar: [10571], + ldsh: [8626], + le: [8804], + leftarrow: [8592], + leftarrowtail: [8610], + leftharpoondown: [8637], + leftharpoonup: [8636], + leftleftarrows: [8647], + leftrightarrow: [8596], + leftrightarrows: [8646], + leftrightharpoons: [8651], + leftrightsquigarrow: [8621], + leftthreetimes: [8907], + leg: [8922], + leq: [8804], + leqq: [8806], + leqslant: [10877], + les: [10877], + lescc: [10920], + lesdot: [10879], + lesdoto: [10881], + lesdotor: [10883], + lesg: [8922, 65024], + lesges: [10899], + lessapprox: [10885], + lessdot: [8918], + lesseqgtr: [8922], + lesseqqgtr: [10891], + lessgtr: [8822], + lesssim: [8818], + lfisht: [10620], + lfloor: [8970], + lfr: [120105], + lg: [8822], + lgE: [10897], + lhard: [8637], + lharu: [8636], + lharul: [10602], + lhblk: [9604], + ljcy: [1113], + ll: [8810], + llarr: [8647], + llcorner: [8990], + llhard: [10603], + lltri: [9722], + lmidot: [320], + lmoust: [9136], + lmoustache: [9136], + lnE: [8808], + lnap: [10889], + lnapprox: [10889], + lne: [10887], + lneq: [10887], + lneqq: [8808], + lnsim: [8934], + loang: [10220], + loarr: [8701], + lobrk: [10214], + longleftarrow: [10229], + longleftrightarrow: [10231], + longmapsto: [10236], + longrightarrow: [10230], + looparrowleft: [8619], + looparrowright: [8620], + lopar: [10629], + lopf: [120157], + loplus: [10797], + lotimes: [10804], + lowast: [8727], + lowbar: [95], + loz: [9674], + lozenge: [9674], + lozf: [10731], + lpar: [40], + lparlt: [10643], + lrarr: [8646], + lrcorner: [8991], + lrhar: [8651], + lrhard: [10605], + lrm: [8206], + lrtri: [8895], + lsaquo: [8249], + lscr: [120001], + lsh: [8624], + lsim: [8818], + lsime: [10893], + lsimg: [10895], + lsqb: [91], + lsquo: [8216], + lsquor: [8218], + lstrok: [322], + lt: [60], + ltcc: [10918], + ltcir: [10873], + ltdot: [8918], + lthree: [8907], + ltimes: [8905], + ltlarr: [10614], + ltquest: [10875], + ltrPar: [10646], + ltri: [9667], + ltrie: [8884], + ltrif: [9666], + lurdshar: [10570], + luruhar: [10598], + lvertneqq: [8808, 65024], + lvnE: [8808, 65024], + mDDot: [8762], + macr: [175], + male: [9794], + malt: [10016], + maltese: [10016], + map: [8614], + mapsto: [8614], + mapstodown: [8615], + mapstoleft: [8612], + mapstoup: [8613], + marker: [9646], + mcomma: [10793], + mcy: [1084], + mdash: [8212], + measuredangle: [8737], + mfr: [120106], + mho: [8487], + micro: [181], + mid: [8739], + midast: [42], + midcir: [10992], + middot: [183], + minus: [8722], + minusb: [8863], + minusd: [8760], + minusdu: [10794], + mlcp: [10971], + mldr: [8230], + mnplus: [8723], + models: [8871], + mopf: [120158], + mp: [8723], + mscr: [120002], + mstpos: [8766], + mu: [956], + multimap: [8888], + mumap: [8888], + nGg: [8921, 824], + nGt: [8811, 8402], + nGtv: [8811, 824], + nLeftarrow: [8653], + nLeftrightarrow: [8654], + nLl: [8920, 824], + nLt: [8810, 8402], + nLtv: [8810, 824], + nRightarrow: [8655], + nVDash: [8879], + nVdash: [8878], + nabla: [8711], + nacute: [324], + nang: [8736, 8402], + nap: [8777], + napE: [10864, 824], + napid: [8779, 824], + napos: [329], + napprox: [8777], + natur: [9838], + natural: [9838], + naturals: [8469], + nbsp: [160], + nbump: [8782, 824], + nbumpe: [8783, 824], + ncap: [10819], + ncaron: [328], + ncedil: [326], + ncong: [8775], + ncongdot: [10861, 824], + ncup: [10818], + ncy: [1085], + ndash: [8211], + ne: [8800], + neArr: [8663], + nearhk: [10532], + nearr: [8599], + nearrow: [8599], + nedot: [8784, 824], + nequiv: [8802], + nesear: [10536], + nesim: [8770, 824], + nexist: [8708], + nexists: [8708], + nfr: [120107], + ngE: [8807, 824], + nge: [8817], + ngeq: [8817], + ngeqq: [8807, 824], + ngeqslant: [10878, 824], + nges: [10878, 824], + ngsim: [8821], + ngt: [8815], + ngtr: [8815], + nhArr: [8654], + nharr: [8622], + nhpar: [10994], + ni: [8715], + nis: [8956], + nisd: [8954], + niv: [8715], + njcy: [1114], + nlArr: [8653], + nlE: [8806, 824], + nlarr: [8602], + nldr: [8229], + nle: [8816], + nleftarrow: [8602], + nleftrightarrow: [8622], + nleq: [8816], + nleqq: [8806, 824], + nleqslant: [10877, 824], + nles: [10877, 824], + nless: [8814], + nlsim: [8820], + nlt: [8814], + nltri: [8938], + nltrie: [8940], + nmid: [8740], + nopf: [120159], + not: [172], + notin: [8713], + notinE: [8953, 824], + notindot: [8949, 824], + notinva: [8713], + notinvb: [8951], + notinvc: [8950], + notni: [8716], + notniva: [8716], + notnivb: [8958], + notnivc: [8957], + npar: [8742], + nparallel: [8742], + nparsl: [11005, 8421], + npart: [8706, 824], + npolint: [10772], + npr: [8832], + nprcue: [8928], + npre: [10927, 824], + nprec: [8832], + npreceq: [10927, 824], + nrArr: [8655], + nrarr: [8603], + nrarrc: [10547, 824], + nrarrw: [8605, 824], + nrightarrow: [8603], + nrtri: [8939], + nrtrie: [8941], + nsc: [8833], + nsccue: [8929], + nsce: [10928, 824], + nscr: [120003], + nshortmid: [8740], + nshortparallel: [8742], + nsim: [8769], + nsime: [8772], + nsimeq: [8772], + nsmid: [8740], + nspar: [8742], + nsqsube: [8930], + nsqsupe: [8931], + nsub: [8836], + nsubE: [10949, 824], + nsube: [8840], + nsubset: [8834, 8402], + nsubseteq: [8840], + nsubseteqq: [10949, 824], + nsucc: [8833], + nsucceq: [10928, 824], + nsup: [8837], + nsupE: [10950, 824], + nsupe: [8841], + nsupset: [8835, 8402], + nsupseteq: [8841], + nsupseteqq: [10950, 824], + ntgl: [8825], + ntilde: [241], + ntlg: [8824], + ntriangleleft: [8938], + ntrianglelefteq: [8940], + ntriangleright: [8939], + ntrianglerighteq: [8941], + nu: [957], + num: [35], + numero: [8470], + numsp: [8199], + nvDash: [8877], + nvHarr: [10500], + nvap: [8781, 8402], + nvdash: [8876], + nvge: [8805, 8402], + nvgt: [62, 8402], + nvinfin: [10718], + nvlArr: [10498], + nvle: [8804, 8402], + nvlt: [60, 8402], + nvltrie: [8884, 8402], + nvrArr: [10499], + nvrtrie: [8885, 8402], + nvsim: [8764, 8402], + nwArr: [8662], + nwarhk: [10531], + nwarr: [8598], + nwarrow: [8598], + nwnear: [10535], + oS: [9416], + oacute: [243], + oast: [8859], + ocir: [8858], + ocirc: [244], + ocy: [1086], + odash: [8861], + odblac: [337], + odiv: [10808], + odot: [8857], + odsold: [10684], + oelig: [339], + ofcir: [10687], + ofr: [120108], + ogon: [731], + ograve: [242], + ogt: [10689], + ohbar: [10677], + ohm: [937], + oint: [8750], + olarr: [8634], + olcir: [10686], + olcross: [10683], + oline: [8254], + olt: [10688], + omacr: [333], + omega: [969], + omicron: [959], + omid: [10678], + ominus: [8854], + oopf: [120160], + opar: [10679], + operp: [10681], + oplus: [8853], + or: [8744], + orarr: [8635], + ord: [10845], + order: [8500], + orderof: [8500], + ordf: [170], + ordm: [186], + origof: [8886], + oror: [10838], + orslope: [10839], + orv: [10843], + oscr: [8500], + oslash: [248], + osol: [8856], + otilde: [245], + otimes: [8855], + otimesas: [10806], + ouml: [246], + ovbar: [9021], + par: [8741], + para: [182], + parallel: [8741], + parsim: [10995], + parsl: [11005], + part: [8706], + pcy: [1087], + percnt: [37], + period: [46], + permil: [8240], + perp: [8869], + pertenk: [8241], + pfr: [120109], + phi: [966], + phiv: [981], + phmmat: [8499], + phone: [9742], + pi: [960], + pitchfork: [8916], + piv: [982], + planck: [8463], + planckh: [8462], + plankv: [8463], + plus: [43], + plusacir: [10787], + plusb: [8862], + pluscir: [10786], + plusdo: [8724], + plusdu: [10789], + pluse: [10866], + plusmn: [177], + plussim: [10790], + plustwo: [10791], + pm: [177], + pointint: [10773], + popf: [120161], + pound: [163], + pr: [8826], + prE: [10931], + prap: [10935], + prcue: [8828], + pre: [10927], + prec: [8826], + precapprox: [10935], + preccurlyeq: [8828], + preceq: [10927], + precnapprox: [10937], + precneqq: [10933], + precnsim: [8936], + precsim: [8830], + prime: [8242], + primes: [8473], + prnE: [10933], + prnap: [10937], + prnsim: [8936], + prod: [8719], + profalar: [9006], + profline: [8978], + profsurf: [8979], + prop: [8733], + propto: [8733], + prsim: [8830], + prurel: [8880], + pscr: [120005], + psi: [968], + puncsp: [8200], + qfr: [120110], + qint: [10764], + qopf: [120162], + qprime: [8279], + qscr: [120006], + quaternions: [8461], + quatint: [10774], + quest: [63], + questeq: [8799], + quot: [34], + rAarr: [8667], + rArr: [8658], + rAtail: [10524], + rBarr: [10511], + rHar: [10596], + race: [8765, 817], + racute: [341], + radic: [8730], + raemptyv: [10675], + rang: [10217], + rangd: [10642], + range: [10661], + rangle: [10217], + raquo: [187], + rarr: [8594], + rarrap: [10613], + rarrb: [8677], + rarrbfs: [10528], + rarrc: [10547], + rarrfs: [10526], + rarrhk: [8618], + rarrlp: [8620], + rarrpl: [10565], + rarrsim: [10612], + rarrtl: [8611], + rarrw: [8605], + ratail: [10522], + ratio: [8758], + rationals: [8474], + rbarr: [10509], + rbbrk: [10099], + rbrace: [125], + rbrack: [93], + rbrke: [10636], + rbrksld: [10638], + rbrkslu: [10640], + rcaron: [345], + rcedil: [343], + rceil: [8969], + rcub: [125], + rcy: [1088], + rdca: [10551], + rdldhar: [10601], + rdquo: [8221], + rdquor: [8221], + rdsh: [8627], + real: [8476], + realine: [8475], + realpart: [8476], + reals: [8477], + rect: [9645], + reg: [174], + rfisht: [10621], + rfloor: [8971], + rfr: [120111], + rhard: [8641], + rharu: [8640], + rharul: [10604], + rho: [961], + rhov: [1009], + rightarrow: [8594], + rightarrowtail: [8611], + rightharpoondown: [8641], + rightharpoonup: [8640], + rightleftarrows: [8644], + rightleftharpoons: [8652], + rightrightarrows: [8649], + rightsquigarrow: [8605], + rightthreetimes: [8908], + ring: [730], + risingdotseq: [8787], + rlarr: [8644], + rlhar: [8652], + rlm: [8207], + rmoust: [9137], + rmoustache: [9137], + rnmid: [10990], + roang: [10221], + roarr: [8702], + robrk: [10215], + ropar: [10630], + ropf: [120163], + roplus: [10798], + rotimes: [10805], + rpar: [41], + rpargt: [10644], + rppolint: [10770], + rrarr: [8649], + rsaquo: [8250], + rscr: [120007], + rsh: [8625], + rsqb: [93], + rsquo: [8217], + rsquor: [8217], + rthree: [8908], + rtimes: [8906], + rtri: [9657], + rtrie: [8885], + rtrif: [9656], + rtriltri: [10702], + ruluhar: [10600], + rx: [8478], + sacute: [347], + sbquo: [8218], + sc: [8827], + scE: [10932], + scap: [10936], + scaron: [353], + sccue: [8829], + sce: [10928], + scedil: [351], + scirc: [349], + scnE: [10934], + scnap: [10938], + scnsim: [8937], + scpolint: [10771], + scsim: [8831], + scy: [1089], + sdot: [8901], + sdotb: [8865], + sdote: [10854], + seArr: [8664], + searhk: [10533], + searr: [8600], + searrow: [8600], + sect: [167], + semi: [59], + seswar: [10537], + setminus: [8726], + setmn: [8726], + sext: [10038], + sfr: [120112], + sfrown: [8994], + sharp: [9839], + shchcy: [1097], + shcy: [1096], + shortmid: [8739], + shortparallel: [8741], + shy: [173], + sigma: [963], + sigmaf: [962], + sigmav: [962], + sim: [8764], + simdot: [10858], + sime: [8771], + simeq: [8771], + simg: [10910], + simgE: [10912], + siml: [10909], + simlE: [10911], + simne: [8774], + simplus: [10788], + simrarr: [10610], + slarr: [8592], + smallsetminus: [8726], + smashp: [10803], + smeparsl: [10724], + smid: [8739], + smile: [8995], + smt: [10922], + smte: [10924], + smtes: [10924, 65024], + softcy: [1100], + sol: [47], + solb: [10692], + solbar: [9023], + sopf: [120164], + spades: [9824], + spadesuit: [9824], + spar: [8741], + sqcap: [8851], + sqcaps: [8851, 65024], + sqcup: [8852], + sqcups: [8852, 65024], + sqsub: [8847], + sqsube: [8849], + sqsubset: [8847], + sqsubseteq: [8849], + sqsup: [8848], + sqsupe: [8850], + sqsupset: [8848], + sqsupseteq: [8850], + squ: [9633], + square: [9633], + squarf: [9642], + squf: [9642], + srarr: [8594], + sscr: [120008], + ssetmn: [8726], + ssmile: [8995], + sstarf: [8902], + star: [9734], + starf: [9733], + straightepsilon: [1013], + straightphi: [981], + strns: [175], + sub: [8834], + subE: [10949], + subdot: [10941], + sube: [8838], + subedot: [10947], + submult: [10945], + subnE: [10955], + subne: [8842], + subplus: [10943], + subrarr: [10617], + subset: [8834], + subseteq: [8838], + subseteqq: [10949], + subsetneq: [8842], + subsetneqq: [10955], + subsim: [10951], + subsub: [10965], + subsup: [10963], + succ: [8827], + succapprox: [10936], + succcurlyeq: [8829], + succeq: [10928], + succnapprox: [10938], + succneqq: [10934], + succnsim: [8937], + succsim: [8831], + sum: [8721], + sung: [9834], + sup: [8835], + sup1: [185], + sup2: [178], + sup3: [179], + supE: [10950], + supdot: [10942], + supdsub: [10968], + supe: [8839], + supedot: [10948], + suphsol: [10185], + suphsub: [10967], + suplarr: [10619], + supmult: [10946], + supnE: [10956], + supne: [8843], + supplus: [10944], + supset: [8835], + supseteq: [8839], + supseteqq: [10950], + supsetneq: [8843], + supsetneqq: [10956], + supsim: [10952], + supsub: [10964], + supsup: [10966], + swArr: [8665], + swarhk: [10534], + swarr: [8601], + swarrow: [8601], + swnwar: [10538], + szlig: [223], + target: [8982], + tau: [964], + tbrk: [9140], + tcaron: [357], + tcedil: [355], + tcy: [1090], + tdot: [8411], + telrec: [8981], + tfr: [120113], + there4: [8756], + therefore: [8756], + theta: [952], + thetasym: [977], + thetav: [977], + thickapprox: [8776], + thicksim: [8764], + thinsp: [8201], + thkap: [8776], + thksim: [8764], + thorn: [254], + tilde: [732], + times: [215], + timesb: [8864], + timesbar: [10801], + timesd: [10800], + tint: [8749], + toea: [10536], + top: [8868], + topbot: [9014], + topcir: [10993], + topf: [120165], + topfork: [10970], + tosa: [10537], + tprime: [8244], + trade: [8482], + triangle: [9653], + triangledown: [9663], + triangleleft: [9667], + trianglelefteq: [8884], + triangleq: [8796], + triangleright: [9657], + trianglerighteq: [8885], + tridot: [9708], + trie: [8796], + triminus: [10810], + triplus: [10809], + trisb: [10701], + tritime: [10811], + trpezium: [9186], + tscr: [120009], + tscy: [1094], + tshcy: [1115], + tstrok: [359], + twixt: [8812], + twoheadleftarrow: [8606], + twoheadrightarrow: [8608], + uArr: [8657], + uHar: [10595], + uacute: [250], + uarr: [8593], + ubrcy: [1118], + ubreve: [365], + ucirc: [251], + ucy: [1091], + udarr: [8645], + udblac: [369], + udhar: [10606], + ufisht: [10622], + ufr: [120114], + ugrave: [249], + uharl: [8639], + uharr: [8638], + uhblk: [9600], + ulcorn: [8988], + ulcorner: [8988], + ulcrop: [8975], + ultri: [9720], + umacr: [363], + uml: [168], + uogon: [371], + uopf: [120166], + uparrow: [8593], + updownarrow: [8597], + upharpoonleft: [8639], + upharpoonright: [8638], + uplus: [8846], + upsi: [965], + upsih: [978], + upsilon: [965], + upuparrows: [8648], + urcorn: [8989], + urcorner: [8989], + urcrop: [8974], + uring: [367], + urtri: [9721], + uscr: [120010], + utdot: [8944], + utilde: [361], + utri: [9653], + utrif: [9652], + uuarr: [8648], + uuml: [252], + uwangle: [10663], + vArr: [8661], + vBar: [10984], + vBarv: [10985], + vDash: [8872], + vangrt: [10652], + varepsilon: [1013], + varkappa: [1008], + varnothing: [8709], + varphi: [981], + varpi: [982], + varpropto: [8733], + varr: [8597], + varrho: [1009], + varsigma: [962], + varsubsetneq: [8842, 65024], + varsubsetneqq: [10955, 65024], + varsupsetneq: [8843, 65024], + varsupsetneqq: [10956, 65024], + vartheta: [977], + vartriangleleft: [8882], + vartriangleright: [8883], + vcy: [1074], + vdash: [8866], + vee: [8744], + veebar: [8891], + veeeq: [8794], + vellip: [8942], + verbar: [124], + vert: [124], + vfr: [120115], + vltri: [8882], + vnsub: [8834, 8402], + vnsup: [8835, 8402], + vopf: [120167], + vprop: [8733], + vrtri: [8883], + vscr: [120011], + vsubnE: [10955, 65024], + vsubne: [8842, 65024], + vsupnE: [10956, 65024], + vsupne: [8843, 65024], + vzigzag: [10650], + wcirc: [373], + wedbar: [10847], + wedge: [8743], + wedgeq: [8793], + weierp: [8472], + wfr: [120116], + wopf: [120168], + wp: [8472], + wr: [8768], + wreath: [8768], + wscr: [120012], + xcap: [8898], + xcirc: [9711], + xcup: [8899], + xdtri: [9661], + xfr: [120117], + xhArr: [10234], + xharr: [10231], + xi: [958], + xlArr: [10232], + xlarr: [10229], + xmap: [10236], + xnis: [8955], + xodot: [10752], + xopf: [120169], + xoplus: [10753], + xotime: [10754], + xrArr: [10233], + xrarr: [10230], + xscr: [120013], + xsqcup: [10758], + xuplus: [10756], + xutri: [9651], + xvee: [8897], + xwedge: [8896], + yacute: [253], + yacy: [1103], + ycirc: [375], + ycy: [1099], + yen: [165], + yfr: [120118], + yicy: [1111], + yopf: [120170], + yscr: [120014], + yucy: [1102], + yuml: [255], + zacute: [378], + zcaron: [382], + zcy: [1079], + zdot: [380], + zeetrf: [8488], + zeta: [950], + zfr: [120119], + zhcy: [1078], + zigrarr: [8669], + zopf: [120171], + zscr: [120015], + zwj: [8205], + zwnj: [8204] + }; + }); +enifed("simple-html-tokenizer/char-refs/min", + ["exports"], + function(__exports__) { + "use strict"; + __exports__["default"] = { + quot: [34], + amp: [38], + apos: [39], + lt: [60], + gt: [62] + }; + }); +enifed("simple-html-tokenizer/entity-parser", + ["exports"], + function(__exports__) { + "use strict"; + function EntityParser(namedCodepoints) { + this.namedCodepoints = namedCodepoints; + } - /** - Used only via `factoryInjection`. + EntityParser.prototype.parse = function (tokenizer) { + var input = tokenizer.input.slice(tokenizer["char"]); + var matches = input.match(/^#(?:x|X)([0-9A-Fa-f]+);/); + if (matches) { + tokenizer["char"] += matches[0].length; + return String.fromCharCode(parseInt(matches[1], 16)); + } + matches = input.match(/^#([0-9]+);/); + if (matches) { + tokenizer["char"] += matches[0].length; + return String.fromCharCode(parseInt(matches[1], 10)); + } + matches = input.match(/^([A-Za-z]+);/); + if (matches) { + var codepoints = this.namedCodepoints[matches[1]]; + if (codepoints) { + tokenizer["char"] += matches[0].length; + for (var i = 0, buffer = ''; i < codepoints.length; i++) { + buffer += String.fromCharCode(codepoints[i]); + } + return buffer; + } + } + }; - Provides a specialized form of injection, specifically enabling - all factory of one type to be injected with a reference to another - object. + __exports__["default"] = EntityParser; + }); +enifed("simple-html-tokenizer/generate", + ["./generator","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Generator = __dependency1__["default"]; - For example, provided each factory of type `model` needed a `store`. - one would do the following: + __exports__["default"] = function generate(tokens) { + var generator = new Generator(); + return generator.generate(tokens); + } + }); +enifed("simple-html-tokenizer/generator", + ["exports"], + function(__exports__) { + "use strict"; + var escape = (function () { + var test = /[&<>"'`]/; + var replace = /[&<>"'`]/g; + var map = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", + "`": "`" + }; + function escapeChar(char) { + return map["char"]; + } + return function escape(string) { + if(!test.test(string)) { + return string; + } + return string.replace(replace, escapeChar); + }; + }()); - ```javascript - var container = new Container(); + function Generator() { + this.escape = escape; + } - container.register('store:main', SomeStore); + Generator.prototype = { + generate: function (tokens) { + var buffer = ''; + var token; + for (var i=0; i true - ``` + if (token.attributes.length) { + out += " " + this.Attributes(token.attributes); + } - @private - @method factoryTypeInjection - @param {String} type - @param {String} property - @param {String} fullName - */ - factoryTypeInjection: function(type, property, fullName) { - if (this.parent) { illegalChildOperation('factoryTypeInjection'); } + out += ">"; - addTypeInjection(this.factoryTypeInjections, type, property, this.normalize(fullName)); + return out; }, - /** - Defines factory injection rules. - - Similar to regular injection rules, but are run against factories, via - `Container#lookupFactory`. - - These rules are used to inject objects onto factories when they - are looked up. + EndTag: function (token) { + return ""; + }, - Two forms of injections are possible: + Chars: function (token) { + return this.escape(token.chars); + }, - * Injecting one fullName on another fullName - * Injecting one fullName on a type + Comment: function (token) { + return ""; + }, - Example: + Attributes: function (attributes) { + var out = [], attribute; - ```javascript - var container = new Container(); + for (var i=0, l=attributes.length; i true - UserFactory.secondaryStore instanceof OtherStore; //=> false + return attrString; + } + }; - PostFactory.store instanceof Store; //=> true - PostFactory.secondaryStore instanceof OtherStore; //=> true + __exports__["default"] = Generator; + }); +enifed("simple-html-tokenizer/tokenize", + ["./tokenizer","./entity-parser","./char-refs/full","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var Tokenizer = __dependency1__["default"]; + var EntityParser = __dependency2__["default"]; + var namedCodepoints = __dependency3__["default"]; - // and both models share the same source instance - UserFactory.store === PostFactory.store; //=> true - ``` + __exports__["default"] = function tokenize(input) { + var tokenizer = new Tokenizer(input, new EntityParser(namedCodepoints)); + return tokenizer.tokenize(); + } + }); +enifed("simple-html-tokenizer/tokenizer", + ["./utils","./tokens","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var preprocessInput = __dependency1__.preprocessInput; + var isAlpha = __dependency1__.isAlpha; + var isSpace = __dependency1__.isSpace; + var StartTag = __dependency2__.StartTag; + var EndTag = __dependency2__.EndTag; + var Chars = __dependency2__.Chars; + var Comment = __dependency2__.Comment; - @method factoryInjection - @param {String} factoryName - @param {String} property - @param {String} injectionName - */ - factoryInjection: function(fullName, property, injectionName) { - if (this.parent) { illegalChildOperation('injection'); } + function Tokenizer(input, entityParser) { + this.input = preprocessInput(input); + this.entityParser = entityParser; + this["char"] = 0; + this.line = 1; + this.column = 0; - var normalizedName = this.normalize(fullName); - var normalizedInjectionName = this.normalize(injectionName); + this.state = 'data'; + this.token = null; + } - validateFullName(injectionName); + Tokenizer.prototype = { + tokenize: function() { + var tokens = [], token; - if (fullName.indexOf(':') === -1) { - return this.factoryTypeInjection(normalizedName, property, normalizedInjectionName); + while (true) { + token = this.lex(); + if (token === 'EOF') { break; } + if (token) { tokens.push(token); } } - validateFullName(fullName); + if (this.token) { + tokens.push(this.token); + } - addInjection(this.factoryInjections, normalizedName, property, normalizedInjectionName); + return tokens; }, - /** - A depth first traversal, destroying the container, its descendant containers and all - their managed objects. + tokenizePart: function(string) { + this.input += preprocessInput(string); + var tokens = [], token; - @method destroy - */ - destroy: function() { - for (var i=0, l=this.children.length; i") { + return this.emitToken(); + } else { + this.addToComment(char); + this.state = 'comment'; + } + }, - factoryInjections._toString = container.makeToString(factory, fullName); + commentStartDash: function(char) { + if (char === "-") { + this.state = 'commentEnd'; + } else if (char === ">") { + return this.emitToken(); + } else { + this.addToComment("-"); + this.state = 'comment'; + } + }, - injectedFactory = factory.extend(injections); - injectedFactory.reopenClass(factoryInjections); + comment: function(char) { + if (char === "-") { + this.state = 'commentEndDash'; + } else { + this.addToComment(char); + } + }, - cache.set(fullName, injectedFactory); + commentEndDash: function(char) { + if (char === "-") { + this.state = 'commentEnd'; + } else { + this.addToComment("-" + char); + this.state = 'comment'; + } + }, - return injectedFactory; - } - } + commentEnd: function(char) { + if (char === ">") { + return this.emitToken(); + } else { + this.addToComment("--" + char); + this.state = 'comment'; + } + }, - function injectionsFor(container, fullName) { - var splitName = fullName.split(":"), - type = splitName[0], - injections = []; + tagName: function(char) { + if (isSpace(char)) { + this.state = 'beforeAttributeName'; + } else if (char === "/") { + this.state = 'selfClosingStartTag'; + } else if (char === ">") { + return this.emitToken(); + } else { + this.addToTagName(char); + } + }, - injections = injections.concat(container.typeInjections.get(type) || []); - injections = injections.concat(container.injections[fullName] || []); + beforeAttributeName: function(char) { + if (isSpace(char)) { + return; + } else if (char === "/") { + this.state = 'selfClosingStartTag'; + } else if (char === ">") { + return this.emitToken(); + } else { + this.createAttribute(char); + } + }, - injections = buildInjections(container, injections); - injections._debugContainerKey = fullName; - injections.container = container; + attributeName: function(char) { + if (isSpace(char)) { + this.state = 'afterAttributeName'; + } else if (char === "/") { + this.state = 'selfClosingStartTag'; + } else if (char === "=") { + this.state = 'beforeAttributeValue'; + } else if (char === ">") { + return this.emitToken(); + } else { + this.addToAttributeName(char); + } + }, - return injections; - } + afterAttributeName: function(char) { + if (isSpace(char)) { + return; + } else if (char === "/") { + this.state = 'selfClosingStartTag'; + } else if (char === "=") { + this.state = 'beforeAttributeValue'; + } else if (char === ">") { + return this.emitToken(); + } else { + this.finalizeAttributeValue(); + this.createAttribute(char); + } + }, - function factoryInjectionsFor(container, fullName) { - var splitName = fullName.split(":"), - type = splitName[0], - factoryInjections = []; + beforeAttributeValue: function(char) { + if (isSpace(char)) { + return; + } else if (char === '"') { + this.state = 'attributeValueDoubleQuoted'; + this.markAttributeQuoted(true); + } else if (char === "'") { + this.state = 'attributeValueSingleQuoted'; + this.markAttributeQuoted(true); + } else if (char === ">") { + return this.emitToken(); + } else { + this.state = 'attributeValueUnquoted'; + this.markAttributeQuoted(false); + this.addToAttributeValue(char); + } + }, - factoryInjections = factoryInjections.concat(container.factoryTypeInjections.get(type) || []); - factoryInjections = factoryInjections.concat(container.factoryInjections[fullName] || []); + attributeValueDoubleQuoted: function(char) { + if (char === '"') { + this.finalizeAttributeValue(); + this.state = 'afterAttributeValueQuoted'; + } else if (char === "&") { + this.addToAttributeValue(this.consumeCharRef('"') || "&"); + } else { + this.addToAttributeValue(char); + } + }, - factoryInjections = buildInjections(container, factoryInjections); - factoryInjections._debugContainerKey = fullName; + attributeValueSingleQuoted: function(char) { + if (char === "'") { + this.finalizeAttributeValue(); + this.state = 'afterAttributeValueQuoted'; + } else if (char === "&") { + this.addToAttributeValue(this.consumeCharRef("'") || "&"); + } else { + this.addToAttributeValue(char); + } + }, - return factoryInjections; - } + attributeValueUnquoted: function(char) { + if (isSpace(char)) { + this.finalizeAttributeValue(); + this.state = 'beforeAttributeName'; + } else if (char === "&") { + this.addToAttributeValue(this.consumeCharRef(">") || "&"); + } else if (char === ">") { + return this.emitToken(); + } else { + this.addToAttributeValue(char); + } + }, - function instantiate(container, fullName) { - var factory = factoryFor(container, fullName); + afterAttributeValueQuoted: function(char) { + if (isSpace(char)) { + this.state = 'beforeAttributeName'; + } else if (char === "/") { + this.state = 'selfClosingStartTag'; + } else if (char === ">") { + return this.emitToken(); + } else { + this["char"]--; + this.state = 'beforeAttributeName'; + } + }, - if (option(container, fullName, 'instantiate') === false) { - return factory; - } + selfClosingStartTag: function(char) { + if (char === ">") { + this.selfClosing(); + return this.emitToken(); + } else { + this["char"]--; + this.state = 'beforeAttributeName'; + } + }, - if (factory) { - if (typeof factory.extend === 'function') { - // assume the factory was extendable and is already injected - return factory.create(); - } else { - // assume the factory was extendable - // to create time injections - // TODO: support new'ing for instantiation and merge injections for pure JS Functions - return factory.create(injectionsFor(container, fullName)); + endTagOpen: function(char) { + if (isAlpha(char)) { + this.createTag(EndTag, char.toLowerCase()); + } } } - } - - function eachDestroyable(container, callback) { - container.cache.eachLocal(function(key, value) { - if (option(container, key, 'instantiate') === false) { return; } - callback(value); - }); - } + }; - function resetCache(container) { - container.cache.eachLocal(function(key, value) { - if (option(container, key, 'instantiate') === false) { return; } - value.destroy(); - }); - container.cache.dict = {}; + __exports__["default"] = Tokenizer; + }); +enifed("simple-html-tokenizer/tokens", + ["exports"], + function(__exports__) { + "use strict"; + function StartTag(tagName, attributes, selfClosing) { + this.type = 'StartTag'; + this.tagName = tagName || ''; + this.attributes = attributes || []; + this.selfClosing = selfClosing === true; } - function addTypeInjection(rules, type, property, fullName) { - var injections = rules.get(type); - - if (!injections) { - injections = []; - rules.set(type, injections); - } - - injections.push({ - property: property, - fullName: fullName - }); + __exports__.StartTag = StartTag;function EndTag(tagName) { + this.type = 'EndTag'; + this.tagName = tagName || ''; } - var VALID_FULL_NAME_REGEXP = /^[^:]+.+:[^:]+$/; - function validateFullName(fullName) { - if (!VALID_FULL_NAME_REGEXP.test(fullName)) { - throw new TypeError('Invalid Fullname, expected: `type:name` got: ' + fullName); - } + __exports__.EndTag = EndTag;function Chars(chars) { + this.type = 'Chars'; + this.chars = chars || ""; } - function addInjection(rules, factoryName, property, injectionName) { - var injections = rules[factoryName] = rules[factoryName] || []; - injections.push({ property: property, fullName: injectionName }); + __exports__.Chars = Chars;function Comment(chars) { + this.type = 'Comment'; + this.chars = chars || ''; } - __exports__["default"] = Container; - });define("ember-runtime/ext/rsvp", - ["ember-metal/core","ember-metal/logger","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - var Logger = __dependency2__["default"]; - - var RSVP = requireModule("rsvp"); - var Test, testModuleName = 'ember-testing/test'; - - RSVP.onerrorDefault = function(error) { - if (error instanceof Error) { - if (Ember.testing) { - // ES6TODO: remove when possible - if (!Test && Ember.__loader.registry[testModuleName]) { - Test = requireModule(testModuleName)['default']; - } - - if (Test && Test.adapter) { - Test.adapter.exception(error); - } else { - throw error; - } - } else if (Ember.onerror) { - Ember.onerror(error); - } else { - Logger.error(error.stack); - Ember.assert(error, false); - } - } - }; - - RSVP.on('error', RSVP.onerrorDefault); - - __exports__["default"] = RSVP; - });define("ember-runtime/system/container", - ["ember-metal/property_set","exports"], - function(__dependency1__, __exports__) { + __exports__.Comment = Comment; + }); +enifed("simple-html-tokenizer/utils", + ["exports"], + function(__exports__) { "use strict"; - var set = __dependency1__["default"]; - - var Container = requireModule('container')["default"]; - Container.set = set; + function isSpace(char) { + return (/[\t\n\f ]/).test(char); + } - __exports__["default"] = Container; - });(function() { -// ensure that minispade loads the following modules first -// ensure that the global exports have occurred for above -// required packages -requireModule('ember-metal'); -requireModule('ember-runtime'); -requireModule('ember-handlebars'); -requireModule('ember-views'); -requireModule('ember-routing'); -requireModule('ember-application'); -requireModule('ember-extension-support'); - -// do this to ensure that Ember.Test is defined properly on the global -// if it is present. -if (Ember.__loader.registry['ember-testing']) { - requireModule('ember-testing'); -} - -/** -Ember - -@module ember -*/ - -function throwWithMessage(msg) { - return function() { - throw new Ember.Error(msg); - }; -} - -function generateRemovedClass(className) { - var msg = " has been moved into a plugin: https://github.com/emberjs/ember-states"; - - return { - extend: throwWithMessage(className + msg), - create: throwWithMessage(className + msg) - }; -} - -Ember.StateManager = generateRemovedClass("Ember.StateManager"); - -/** - This was exported to ember-states plugin for v 1.0.0 release. See: https://github.com/emberjs/ember-states - - @class StateManager - @namespace Ember -*/ - -Ember.State = generateRemovedClass("Ember.State"); - -/** - This was exported to ember-states plugin for v 1.0.0 release. See: https://github.com/emberjs/ember-states - - @class State - @namespace Ember -*/ + __exports__.isSpace = isSpace;function isAlpha(char) { + return (/[A-Za-z]/).test(char); + } -})(); + __exports__.isAlpha = isAlpha;function preprocessInput(input) { + return input.replace(/\r\n?/g, "\n"); + } + __exports__.preprocessInput = preprocessInput; + }); +requireModule("ember"); -})(); +})(); \ No newline at end of file diff --git a/examples/emberjs/bower_components/ember-data/ember-data.js b/examples/emberjs/node_modules/ember-data/ember-data.js similarity index 52% rename from examples/emberjs/bower_components/ember-data/ember-data.js rename to examples/emberjs/node_modules/ember-data/ember-data.js index ee476b5eb2..3550e71053 100644 --- a/examples/emberjs/bower_components/ember-data/ember-data.js +++ b/examples/emberjs/node_modules/ember-data/ember-data.js @@ -1,637 +1,478 @@ -/*! - * @overview Ember Data - * @copyright Copyright 2011-2014 Tilde Inc. and contributors. - * Portions Copyright 2011 LivingSocial Inc. - * @license Licensed under MIT license (see license.js) - * @version 1.0.0-beta.7+canary.b45e23ba - */ -(function(global) { -var define, requireModule, require, requirejs; - (function() { - var registry = {}, seen = {}; + "use strict"; + /** + @module ember-data + */ - define = function(name, deps, callback) { - registry[name] = { deps: deps, callback: callback }; - }; + var ember$data$lib$system$adapter$$get = Ember.get; - requirejs = require = requireModule = function(name) { - requirejs._eak_seen = registry; + var ember$data$lib$system$adapter$$errorProps = [ + 'description', + 'fileName', + 'lineNumber', + 'message', + 'name', + 'number', + 'stack' + ]; - if (seen[name]) { return seen[name]; } - seen[name] = {}; + /** + A `DS.InvalidError` is used by an adapter to signal the external API + was unable to process a request because the content was not + semantically correct or meaningful per the API. Usually this means a + record failed some form of server side validation. When a promise + from an adapter is rejected with a `DS.InvalidError` the record will + transition to the `invalid` state and the errors will be set to the + `errors` property on the record. - if (!registry[name]) { - throw new Error("Could not find module " + name); - } + This function should return the entire payload as received from the + server. Error object extraction and normalization of model errors + should be performed by `extractErrors` on the serializer. - var mod = registry[name], - deps = mod.deps, - callback = mod.callback, - reified = [], - exports; + Example - for (var i=0, l=deps.length; i "famous_people" + Note the `defaultSerializer` serializer has a lower priority than + a model specific serializer (i.e. `PostSerializer`) or the + `application` serializer. + + ```javascript + var DjangoAdapter = DS.Adapter.extend({ + defaultSerializer: 'django' + }); ``` - @method pathForType - @param {String} type - @returns String + @property defaultSerializer + @type {String} */ - pathForType: function(type) { - var decamelized = decamelize(type); - var underscored = underscore(decamelized); - return pluralize(underscored); - }, /** - The ActiveModelAdapter overrides the `ajaxError` method - to return a DS.InvalidError for all 422 Unprocessable Entity - responses. + The `find()` method is invoked when the store is asked for a record that + has not previously been loaded. In response to `find()` being called, you + should query your persistence layer for a record with the given ID. Once + found, you can asynchronously call the store's `push()` method to push + the record into the store. - A 422 HTTP response from the server generally implies that the request - was well formed but the API was unable to process it because the - content was not semantically correct or meaningful per the API. + Here is an example `find` implementation: - For more information on 422 HTTP Error code see 11.2 WebDAV RFC 4918 - https://tools.ietf.org/html/rfc4918#section-11.2 + ```javascript + App.ApplicationAdapter = DS.Adapter.extend({ + find: function(store, type, id) { + var url = [type.typeKey, id].join('/'); - @method ajaxError - @param jqXHR - @returns error + return new Ember.RSVP.Promise(function(resolve, reject) { + jQuery.getJSON(url).then(function(data) { + Ember.run(null, resolve, data); + }, function(jqXHR) { + jqXHR.then = null; // tame jQuery's ill mannered promises + Ember.run(null, reject, jqXHR); + }); + }); + } + }); + ``` + + @method find + @param {DS.Store} store + @param {subclass of DS.Model} type + @param {String} id + @return {Promise} promise */ - ajaxError: function(jqXHR) { - var error = this._super(jqXHR); + find: Ember.required(Function), - if (jqXHR && jqXHR.status === 422) { - var response = Ember.$.parseJSON(jqXHR.responseText), - errors = {}; + /** + The `findAll()` method is called when you call `find` on the store + without an ID (i.e. `store.find('post')`). - if (response.errors !== undefined) { - var jsonErrors = response.errors; + Example - forEach(Ember.keys(jsonErrors), function(key) { - errors[Ember.String.camelize(key)] = jsonErrors[key]; + ```javascript + App.ApplicationAdapter = DS.Adapter.extend({ + findAll: function(store, type, sinceToken) { + var url = type; + var query = { since: sinceToken }; + return new Ember.RSVP.Promise(function(resolve, reject) { + jQuery.getJSON(url, query).then(function(data) { + Ember.run(null, resolve, data); + }, function(jqXHR) { + jqXHR.then = null; // tame jQuery's ill mannered promises + Ember.run(null, reject, jqXHR); + }); }); } + }); + ``` - return new InvalidError(errors); - } else { - return error; - } - } - }); - - __exports__["default"] = ActiveModelAdapter; - }); -define("activemodel-adapter/lib/system/active_model_serializer", - ["../../../ember-inflector/lib/main","../../../ember-data/lib/serializers/rest_serializer","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var singularize = __dependency1__.singularize; - var RESTSerializer = __dependency2__["default"]; - /** - @module ember-data - */ + @private + @method findAll + @param {DS.Store} store + @param {subclass of DS.Model} type + @param {String} sinceToken + @return {Promise} promise + */ + findAll: null, - var get = Ember.get, - forEach = Ember.EnumerableUtils.forEach, - camelize = Ember.String.camelize, - capitalize = Ember.String.capitalize, - decamelize = Ember.String.decamelize, - underscore = Ember.String.underscore; + /** + This method is called when you call `find` on the store with a + query object as the second parameter (i.e. `store.find('person', { + page: 1 })`). - var ActiveModelSerializer = RESTSerializer.extend({ - // SERIALIZE + Example - /** - Converts camelcased attributes to underscored when serializing. + ```javascript + App.ApplicationAdapter = DS.Adapter.extend({ + findQuery: function(store, type, query) { + var url = type; + return new Ember.RSVP.Promise(function(resolve, reject) { + jQuery.getJSON(url, query).then(function(data) { + Ember.run(null, resolve, data); + }, function(jqXHR) { + jqXHR.then = null; // tame jQuery's ill mannered promises + Ember.run(null, reject, jqXHR); + }); + }); + } + }); + ``` - @method keyForAttribute - @param {String} attribute - @returns String + @private + @method findQuery + @param {DS.Store} store + @param {subclass of DS.Model} type + @param {Object} query + @param {DS.AdapterPopulatedRecordArray} recordArray + @return {Promise} promise */ - keyForAttribute: function(attr) { - return decamelize(attr); - }, + findQuery: null, /** - Underscores relationship names and appends "_id" or "_ids" when serializing - relationship keys. + If the globally unique IDs for your records should be generated on the client, + implement the `generateIdForRecord()` method. This method will be invoked + each time you create a new record, and the value returned from it will be + assigned to the record's `primaryKey`. - @method keyForRelationship - @param {String} key - @param {String} kind - @returns String - */ - keyForRelationship: function(key, kind) { - key = decamelize(key); - if (kind === "belongsTo") { - return key + "_id"; - } else if (kind === "hasMany") { - return singularize(key) + "_ids"; - } else { - return key; - } - }, + Most traditional REST-like HTTP APIs will not use this method. Instead, the ID + of the record will be set by the server, and your adapter will update the store + with the new ID when it calls `didCreateRecord()`. Only implement this method if + you intend to generate record IDs on the client-side. - /** - Does not serialize hasMany relationships by default. - */ - serializeHasMany: Ember.K, + The `generateIdForRecord()` method will be invoked with the requesting store as + the first parameter and the newly created record as the second parameter: - /** - Underscores the JSON root keys when serializing. + ```javascript + generateIdForRecord: function(store, record) { + var uuid = App.generateUUIDWithStatisticallyLowOddsOfCollision(); + return uuid; + } + ``` - @method serializeIntoHash - @param {Object} hash - @param {subclass of DS.Model} type + @method generateIdForRecord + @param {DS.Store} store @param {DS.Model} record - @param {Object} options + @return {String|Number} id */ - serializeIntoHash: function(data, type, record, options) { - var root = underscore(decamelize(type.typeKey)); - data[root] = this.serialize(record, options); - }, + generateIdForRecord: null, /** - Serializes a polymorphic type as a fully capitalized model name. + Proxies to the serializer's `serialize` method. - @method serializePolymorphicType - @param {DS.Model} record - @param {Object} json - @param relationship - */ - serializePolymorphicType: function(record, json, relationship) { - var key = relationship.key, - belongsTo = get(record, key); - key = this.keyForAttribute(key); - json[key + "_type"] = capitalize(camelize(belongsTo.constructor.typeKey)); - }, + Example - // EXTRACT + ```javascript + App.ApplicationAdapter = DS.Adapter.extend({ + createRecord: function(store, type, record) { + var data = this.serialize(record, { includeId: true }); + var url = type; - /** - Extracts the model typeKey from underscored root objects. + // ... + } + }); + ``` - @method typeForRoot - @param {String} root - @returns String the model's typeKey + @method serialize + @param {DS.Model} record + @param {Object} options + @return {Object} serialized record */ - typeForRoot: function(root) { - var camelized = camelize(root); - return singularize(camelized); + serialize: function(record, options) { + return ember$data$lib$system$adapter$$get(record, 'store').serializerFor(record.constructor.typeKey).serialize(record, options); }, /** - Add extra step to `DS.RESTSerializer.normalize` so links are - normalized. + Implement this method in a subclass to handle the creation of + new records. - If your payload looks like this + Serializes the record and send it to the server. - ```js - { - "post": { - "id": 1, - "title": "Rails is omakase", - "links": { "flagged_comments": "api/comments/flagged" } - } - } - ``` - The normalized version would look like this + Example - ```js - { - "post": { - "id": 1, - "title": "Rails is omakase", - "links": { "flaggedComments": "api/comments/flagged" } + ```javascript + App.ApplicationAdapter = DS.Adapter.extend({ + createRecord: function(store, type, record) { + var data = this.serialize(record, { includeId: true }); + var url = type; + + return new Ember.RSVP.Promise(function(resolve, reject) { + jQuery.ajax({ + type: 'POST', + url: url, + dataType: 'json', + data: data + }).then(function(data) { + Ember.run(null, resolve, data); + }, function(jqXHR) { + jqXHR.then = null; // tame jQuery's ill mannered promises + Ember.run(null, reject, jqXHR); + }); + }); } - } + }); ``` - @method normalize - @param {subclass of DS.Model} type - @param {Object} hash - @param {String} prop - @returns Object + @method createRecord + @param {DS.Store} store + @param {subclass of DS.Model} type the DS.Model class of the record + @param {DS.Model} record + @return {Promise} promise */ - - normalize: function(type, hash, prop) { - this.normalizeLinks(hash); - - return this._super(type, hash, prop); - }, + createRecord: Ember.required(Function), /** - Convert `snake_cased` links to `camelCase` + Implement this method in a subclass to handle the updating of + a record. - @method normalizeLinks - @param {Object} hash - */ + Serializes the record update and send it to the server. - normalizeLinks: function(data){ - if (data.links) { - var links = data.links; + Example - for (var link in links) { - var camelizedLink = camelize(link); + ```javascript + App.ApplicationAdapter = DS.Adapter.extend({ + updateRecord: function(store, type, record) { + var data = this.serialize(record, { includeId: true }); + var id = record.get('id'); + var url = [type, id].join('/'); - if (camelizedLink !== link) { - links[camelizedLink] = links[link]; - delete links[link]; - } + return new Ember.RSVP.Promise(function(resolve, reject) { + jQuery.ajax({ + type: 'PUT', + url: url, + dataType: 'json', + data: data + }).then(function(data) { + Ember.run(null, resolve, data); + }, function(jqXHR) { + jqXHR.then = null; // tame jQuery's ill mannered promises + Ember.run(null, reject, jqXHR); + }); + }); } - } - }, + }); + ``` + + @method updateRecord + @param {DS.Store} store + @param {subclass of DS.Model} type the DS.Model class of the record + @param {DS.Model} record + @return {Promise} promise + */ + updateRecord: Ember.required(Function), /** - Normalize the polymorphic type from the JSON. + Implement this method in a subclass to handle the deletion of + a record. - Normalize: - ```js - { - id: "1" - minion: { type: "evil_minion", id: "12"} - } - ``` + Sends a delete request for the record to the server. - To: - ```js - { - id: "1" - minion: { type: "evilMinion", id: "12"} + Example + + ```javascript + App.ApplicationAdapter = DS.Adapter.extend({ + deleteRecord: function(store, type, record) { + var data = this.serialize(record, { includeId: true }); + var id = record.get('id'); + var url = [type, id].join('/'); + + return new Ember.RSVP.Promise(function(resolve, reject) { + jQuery.ajax({ + type: 'DELETE', + url: url, + dataType: 'json', + data: data + }).then(function(data) { + Ember.run(null, resolve, data); + }, function(jqXHR) { + jqXHR.then = null; // tame jQuery's ill mannered promises + Ember.run(null, reject, jqXHR); + }); + }); } + }); ``` - @method normalizeRelationships - @private + @method deleteRecord + @param {DS.Store} store + @param {subclass of DS.Model} type the DS.Model class of the record + @param {DS.Model} record + @return {Promise} promise */ - normalizeRelationships: function(type, hash) { - var payloadKey, payload; - - if (this.keyForRelationship) { - type.eachRelationship(function(key, relationship) { - if (relationship.options.polymorphic) { - payloadKey = this.keyForAttribute(key); - payload = hash[payloadKey]; - if (payload && payload.type) { - payload.type = this.typeForRoot(payload.type); - } else if (payload && relationship.kind === "hasMany") { - var self = this; - forEach(payload, function(single) { - single.type = self.typeForRoot(single.type); - }); - } - } else { - payloadKey = this.keyForRelationship(key, relationship.kind); - payload = hash[payloadKey]; - } - - hash[key] = payload; - - if (key !== payloadKey) { - delete hash[payloadKey]; - } - }, this); - } - } - }); + deleteRecord: Ember.required(Function), - __exports__["default"] = ActiveModelSerializer; - }); -define("activemodel-adapter/lib/system/embedded_records_mixin", - ["../../../ember-inflector/lib/main","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var get = Ember.get; - var forEach = Ember.EnumerableUtils.forEach; + /** + By default the store will try to coalesce all `fetchRecord` calls within the same runloop + into as few requests as possible by calling groupRecordsForFindMany and passing it into a findMany call. + You can opt out of this behaviour by either not implementing the findMany hook or by setting + coalesceFindRequests to false - var pluralize = __dependency1__.pluralize; + @property coalesceFindRequests + @type {boolean} + */ + coalesceFindRequests: true, - /** - The EmbeddedRecordsMixin allows you to add embedded record support to your - serializers. - To set up embedded records, you include the mixin into the serializer and then - define your embedded relations. + /** + Find multiple records at once if coalesceFindRequests is true - ```js - App.PostSerializer = DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, { - attrs: { - comments: {embedded: 'always'} - } - }) - ``` - - Currently only `{embedded: 'always'}` records are supported. - - @class EmbeddedRecordsMixin - @namespace DS - */ - var EmbeddedRecordsMixin = Ember.Mixin.create({ - - /** - Serialize has-may relationship when it is configured as embedded objects. - - @method serializeHasMany + @method findMany + @param {DS.Store} store + @param {subclass of DS.Model} type the DS.Model class of the records + @param {Array} ids + @param {Array} records + @return {Promise} promise */ - serializeHasMany: function(record, json, relationship) { - var key = relationship.key, - attrs = get(this, 'attrs'), - embed = attrs && attrs[key] && attrs[key].embedded === 'always'; - - if (embed) { - json[this.keyForAttribute(key)] = get(record, key).map(function(relation) { - var data = relation.serialize(), - primaryKey = get(this, 'primaryKey'); - - data[primaryKey] = get(relation, primaryKey); - - return data; - }, this); - } - }, /** - Extract embedded objects out of the payload for a single object - and add them as sideloaded objects instead. - - @method extractSingle - */ - extractSingle: function(store, primaryType, payload, recordId, requestType) { - var root = this.keyForAttribute(primaryType.typeKey), - partial = payload[root]; + Organize records into groups, each of which is to be passed to separate + calls to `findMany`. - updatePayloadWithEmbedded(store, this, primaryType, partial, payload); + For example, if your api has nested URLs that depend on the parent, you will + want to group records by their parent. - return this._super(store, primaryType, payload, recordId, requestType); - }, - - /** - Extract embedded objects out of a standard payload - and add them as sideloaded objects instead. + The default implementation returns the records as a single group. - @method extractArray + @method groupRecordsForFindMany + @param {DS.Store} store + @param {Array} records + @return {Array} an array of arrays of records, each of which is to be + loaded separately by `findMany`. */ - extractArray: function(store, type, payload) { - var root = this.keyForAttribute(type.typeKey), - partials = payload[pluralize(root)]; - - forEach(partials, function(partial) { - updatePayloadWithEmbedded(store, this, type, partial, payload); - }, this); - - return this._super(store, type, payload); + groupRecordsForFindMany: function (store, records) { + return [records]; } }); - function updatePayloadWithEmbedded(store, serializer, type, partial, payload) { - var attrs = get(serializer, 'attrs'); - - if (!attrs) { - return; - } - - type.eachRelationship(function(key, relationship) { - var expandedKey, embeddedTypeKey, attribute, ids, - config = attrs[key], - serializer = store.serializerFor(relationship.type.typeKey), - primaryKey = get(serializer, "primaryKey"); - - if (relationship.kind !== "hasMany") { - return; - } - - if (config && (config.embedded === 'always' || config.embedded === 'load')) { - // underscore forces the embedded records to be side loaded. - // it is needed when main type === relationship.type - embeddedTypeKey = '_' + Ember.String.pluralize(relationship.type.typeKey); - expandedKey = this.keyForRelationship(key, relationship.kind); - attribute = this.keyForAttribute(key); - ids = []; - - if (!partial[attribute]) { - return; - } - - payload[embeddedTypeKey] = payload[embeddedTypeKey] || []; - - forEach(partial[attribute], function(data) { - var embeddedType = store.modelFor(relationship.type.typeKey); - updatePayloadWithEmbedded(store, serializer, embeddedType, data, payload); - ids.push(data[primaryKey]); - payload[embeddedTypeKey].push(data); - }); - - partial[expandedKey] = ids; - delete partial[attribute]; - } - }, serializer); - } - - __exports__["default"] = EmbeddedRecordsMixin; - }); -define("ember-data/lib/adapters", - ["./adapters/fixture_adapter","./adapters/rest_adapter","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var FixtureAdapter = __dependency1__["default"]; - var RESTAdapter = __dependency2__["default"]; - - __exports__.RESTAdapter = RESTAdapter; - __exports__.FixtureAdapter = FixtureAdapter; - }); -define("ember-data/lib/adapters/fixture_adapter", - ["../system/adapter","exports"], - function(__dependency1__, __exports__) { - "use strict"; + var ember$data$lib$system$adapter$$default = ember$data$lib$system$adapter$$Adapter; /** @module ember-data */ + var ember$data$lib$adapters$fixture_adapter$$get = Ember.get; + var ember$data$lib$adapters$fixture_adapter$$fmt = Ember.String.fmt; + var ember$data$lib$adapters$fixture_adapter$$indexOf = Ember.EnumerableUtils.indexOf; - var get = Ember.get, fmt = Ember.String.fmt, - indexOf = Ember.EnumerableUtils.indexOf; - - var counter = 0; + var ember$data$lib$adapters$fixture_adapter$$counter = 0; - var Adapter = __dependency1__["default"]; - - /** - `DS.FixtureAdapter` is an adapter that loads records from memory. - Its primarily used for development and testing. You can also use - `DS.FixtureAdapter` while working on the API but are not ready to - integrate yet. It is a fully functioning adapter. All CRUD methods - are implemented. You can also implement query logic that a remote - system would do. Its possible to do develop your entire application - with `DS.FixtureAdapter`. - - For information on how to use the `FixtureAdapter` in your - application please see the [FixtureAdapter - guide](/guides/models/the-fixture-adapter/). - - @class FixtureAdapter - @namespace DS - @extends DS.Adapter - */ - var FixtureAdapter = Adapter.extend({ + var ember$data$lib$adapters$fixture_adapter$$default = ember$data$lib$system$adapter$$default.extend({ // by default, fixtures are already in normalized form serializer: null, @@ -672,7 +513,7 @@ define("ember-data/lib/adapters/fixture_adapter", return fixtures.map(function(fixture){ var fixtureIdType = typeof fixture.id; if(fixtureIdType !== "number" && fixtureIdType !== "string"){ - throw new Error(fmt('the id property must be defined as a number or string for fixture %@', [fixture])); + throw new Error(ember$data$lib$adapters$fixture_adapter$$fmt('the id property must be defined as a number or string for fixture %@', [fixture])); } fixture.id = fixture.id + ''; return fixture; @@ -729,7 +570,7 @@ define("ember-data/lib/adapters/fixture_adapter", @return {String} id */ generateIdForRecord: function(store) { - return "fixture-" + counter++; + return "fixture-" + ember$data$lib$adapters$fixture_adapter$$counter++; }, /** @@ -740,13 +581,13 @@ define("ember-data/lib/adapters/fixture_adapter", @return {Promise} promise */ find: function(store, type, id) { - var fixtures = this.fixturesForType(type), - fixture; + var fixtures = this.fixturesForType(type); + var fixture; - Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures); + Ember.assert("Unable to find fixtures for model type "+type.toString() +". If you're defining your fixtures using `Model.FIXTURES = ...`, please change it to `Model.reopenClass({ FIXTURES: ... })`.", fixtures); if (fixtures) { - fixture = Ember.A(fixtures).findProperty('id', id); + fixture = Ember.A(fixtures).findBy('id', id); } if (fixture) { @@ -770,7 +611,7 @@ define("ember-data/lib/adapters/fixture_adapter", if (fixtures) { fixtures = fixtures.filter(function(item) { - return indexOf(ids, item.id) !== -1; + return ember$data$lib$adapters$fixture_adapter$$indexOf(ids, item.id) !== -1; }); } @@ -864,9 +705,7 @@ define("ember-data/lib/adapters/fixture_adapter", @return {Promise} promise */ deleteRecord: function(store, type, record) { - var fixture = this.mockJSON(store, type, record); - - this.deleteLoadedFixture(type, fixture); + this.deleteLoadedFixture(type, record); return this.simulateRemoteCall(function() { // no payload in a deletion @@ -883,8 +722,8 @@ define("ember-data/lib/adapters/fixture_adapter", deleteLoadedFixture: function(type, record) { var existingFixture = this.findExistingFixture(type, record); - if(existingFixture) { - var index = indexOf(type.FIXTURES, existingFixture); + if (existingFixture) { + var index = ember$data$lib$adapters$fixture_adapter$$indexOf(type.FIXTURES, existingFixture); type.FIXTURES.splice(index, 1); return true; } @@ -898,7 +737,7 @@ define("ember-data/lib/adapters/fixture_adapter", */ findExistingFixture: function(type, record) { var fixtures = this.fixturesForType(type); - var id = get(record, 'id'); + var id = ember$data$lib$adapters$fixture_adapter$$get(record, 'id'); return this.findFixtureById(fixtures, id); }, @@ -911,7 +750,7 @@ define("ember-data/lib/adapters/fixture_adapter", */ findFixtureById: function(fixtures, id) { return Ember.A(fixtures).find(function(r) { - if(''+get(r, 'id') === ''+id) { + if (''+ember$data$lib$adapters$fixture_adapter$$get(r, 'id') === ''+id) { return true; } else { return false; @@ -929,135 +768,161 @@ define("ember-data/lib/adapters/fixture_adapter", var adapter = this; return new Ember.RSVP.Promise(function(resolve) { - if (get(adapter, 'simulateRemoteResponse')) { + var value = Ember.copy(callback.call(context), true); + if (ember$data$lib$adapters$fixture_adapter$$get(adapter, 'simulateRemoteResponse')) { // Schedule with setTimeout Ember.run.later(function() { - resolve(callback.call(context)); - }, get(adapter, 'latency')); + resolve(value); + }, ember$data$lib$adapters$fixture_adapter$$get(adapter, 'latency')); } else { // Asynchronous, but at the of the runloop with zero latency Ember.run.schedule('actions', null, function() { - resolve(callback.call(context)); + resolve(value); }); } }, "DS: FixtureAdapter#simulateRemoteCall"); } }); - __exports__["default"] = FixtureAdapter; - }); -define("ember-data/lib/adapters/rest_adapter", - ["../system/adapter","exports"], - function(__dependency1__, __exports__) { - "use strict"; /** - @module ember-data + * Polyfill Ember.Map behavior for Ember <= 1.7 + * This can probably be removed before 1.0 final */ + var ember$data$lib$system$map$$mapForEach, ember$data$lib$system$map$$deleteFn; - var Adapter = __dependency1__["default"]; - var get = Ember.get, set = Ember.set; - var forEach = Ember.ArrayPolyfills.forEach; + function ember$data$lib$system$map$$OrderedSet(){ + Ember.OrderedSet.apply(this, arguments); + } - /** - The REST adapter allows your store to communicate with an HTTP server by - transmitting JSON via XHR. Most Ember.js apps that consume a JSON API - should use the REST adapter. + function ember$data$lib$system$map$$Map() { + Ember.Map.apply(this, arguments); + } - This adapter is designed around the idea that the JSON exchanged with - the server should be conventional. + function ember$data$lib$system$map$$MapWithDefault(){ + Ember.MapWithDefault.apply(this, arguments); + } - ## JSON Structure + var ember$data$lib$system$map$$testMap = Ember.Map.create(); + ember$data$lib$system$map$$testMap.set('key', 'value'); - The REST adapter expects the JSON returned from your server to follow - these conventions. + var ember$data$lib$system$map$$usesOldBehavior = false; - ### Object Root + ember$data$lib$system$map$$testMap.forEach(function(value, key){ + ember$data$lib$system$map$$usesOldBehavior = value === 'key' && key === 'value'; + }); - The JSON payload should be an object that contains the record inside a - root property. For example, in response to a `GET` request for - `/posts/1`, the JSON should look like this: + ember$data$lib$system$map$$Map.prototype = Ember.create(Ember.Map.prototype); + ember$data$lib$system$map$$MapWithDefault.prototype = Ember.create(Ember.MapWithDefault.prototype); + ember$data$lib$system$map$$OrderedSet.prototype = Ember.create(Ember.OrderedSet.prototype); - ```js - { - "post": { - "title": "I'm Running to Reform the W3C's Tag", - "author": "Yehuda Katz" - } - } - ``` + ember$data$lib$system$map$$OrderedSet.create = function(){ + return new ember$data$lib$system$map$$OrderedSet(); + }; - ### Conventional Names + /** + * returns a function that calls the original + * callback function in the correct order. + * if we are in pre-Ember.1.8 land, Map/MapWithDefault + * forEach calls with key, value, in that order. + * >= 1.8 forEach is called with the order value, key as per + * the ES6 spec. + */ + function ember$data$lib$system$map$$translate(valueKeyOrderedCallback){ + return function(key, value){ + valueKeyOrderedCallback.call(this, value, key); + }; + } - Attribute names in your JSON payload should be the camelCased versions of - the attributes in your Ember.js models. + // old, non ES6 compliant behavior + if (ember$data$lib$system$map$$usesOldBehavior){ + ember$data$lib$system$map$$mapForEach = function(callback, thisArg){ + this.__super$forEach(ember$data$lib$system$map$$translate(callback), thisArg); + }; - For example, if you have a `Person` model: + /* alias to remove */ + ember$data$lib$system$map$$deleteFn = function(thing){ + this.remove(thing); + }; - ```js - App.Person = DS.Model.extend({ - firstName: DS.attr('string'), - lastName: DS.attr('string'), - occupation: DS.attr('string') - }); - ``` + ember$data$lib$system$map$$Map.prototype.__super$forEach = Ember.Map.prototype.forEach; + ember$data$lib$system$map$$Map.prototype.forEach = ember$data$lib$system$map$$mapForEach; + ember$data$lib$system$map$$Map.prototype["delete"] = ember$data$lib$system$map$$deleteFn; - The JSON returned should look like this: + ember$data$lib$system$map$$MapWithDefault.prototype.forEach = ember$data$lib$system$map$$mapForEach; + ember$data$lib$system$map$$MapWithDefault.prototype.__super$forEach = Ember.MapWithDefault.prototype.forEach; + ember$data$lib$system$map$$MapWithDefault.prototype["delete"] = ember$data$lib$system$map$$deleteFn; - ```js - { - "person": { - "firstName": "Barack", - "lastName": "Obama", - "occupation": "President" - } + ember$data$lib$system$map$$OrderedSet.prototype["delete"] = ember$data$lib$system$map$$deleteFn; + } + + ember$data$lib$system$map$$MapWithDefault.constructor = ember$data$lib$system$map$$MapWithDefault; + ember$data$lib$system$map$$Map.constructor = ember$data$lib$system$map$$Map; + + ember$data$lib$system$map$$MapWithDefault.create = function(options){ + if (options) { + return new ember$data$lib$system$map$$MapWithDefault(options); + } else { + return new ember$data$lib$system$map$$Map(); } - ``` + }; - ## Customization + ember$data$lib$system$map$$Map.create = function(){ + return new this.constructor(); + }; - ### Endpoint path customization + var ember$data$lib$system$map$$default = ember$data$lib$system$map$$Map; + var ember$data$lib$adapters$rest_adapter$$get = Ember.get; + var ember$data$lib$adapters$rest_adapter$$forEach = Ember.ArrayPolyfills.forEach; - Endpoint paths can be prefixed with a `namespace` by setting the namespace - property on the adapter: + var ember$data$lib$adapters$rest_adapter$$default = ember$data$lib$system$adapter$$Adapter.extend({ + defaultSerializer: '-rest', - ```js - DS.RESTAdapter.reopen({ - namespace: 'api/1' - }); - ``` - Requests for `App.Person` would now target `/api/1/people/1`. + /** + By default the RESTAdapter will send each find request coming from a `store.find` + or from accessing a relationship separately to the server. If your server supports passing + ids as a query string, you can set coalesceFindRequests to true to coalesce all find requests + within a single runloop. - ### Host customization + For example, if you have an initial payload of + ```javascript + post: { + id:1, + comments: [1,2] + } + ``` - An adapter can target other hosts by setting the `host` property. + By default calling `post.get('comments')` will trigger the following requests(assuming the + comments haven't been loaded before): - ```js - DS.RESTAdapter.reopen({ - host: 'https://api.example.com' - }); - ``` + ``` + GET /comments/1 + GET /comments/2 + ``` - ### Headers customization + If you set coalesceFindRequests to `true` it will instead trigger the following request: - Some APIs require HTTP headers, e.g. to provide an API key. An array of - headers can be added to the adapter which are passed with every request: + ``` + GET /comments?ids[]=1&ids[]=2 + ``` - ```js - DS.RESTAdapter.reopen({ - headers: { - "API_KEY": "secret key", - "ANOTHER_HEADER": "Some header value" - } - }); - ``` + Setting coalesceFindRequests to `true` also works for `store.find` requests and `belongsTo` + relationships accessed within the same runloop. If you set `coalesceFindRequests: true` + + ```javascript + store.find('comment', 1); + store.find('comment', 2); + ``` + + will also send a request to: `GET /comments?ids[]=1&ids[]=2` + + Note: Requests coalescing rely on URL building strategy. So if you override `buildURL` in your app + `groupRecordsForFindMany` more likely should be overridden as well in order for coalescing to work. + + @property coalesceFindRequests + @type {boolean} + */ + coalesceFindRequests: false, - @class RESTAdapter - @constructor - @namespace DS - @extends DS.Adapter - */ - var RESTAdapter = Adapter.extend({ - defaultSerializer: '-rest', /** Endpoint paths can be prefixed with a `namespace` by setting the namespace property on the adapter: @@ -1090,11 +955,14 @@ define("ember-data/lib/adapters/rest_adapter", */ /** - Some APIs require HTTP headers, e.g. to provide an API key. An array of - headers can be added to the adapter which are passed with every request: + Some APIs require HTTP headers, e.g. to provide an API + key. Arbitrary headers can be set as key/value pairs on the + `RESTAdapter`'s `headers` object and Ember Data will send them + along with each ajax request. For dynamic headers see [headers + customization](/api/data/classes/DS.RESTAdapter.html#toc_headers-customization). ```javascript - DS.RESTAdapter.reopen({ + App.ApplicationAdapter = DS.RESTAdapter.extend({ headers: { "API_KEY": "secret key", "ANOTHER_HEADER": "Some header value" @@ -1119,10 +987,11 @@ define("ember-data/lib/adapters/rest_adapter", @param {DS.Store} store @param {subclass of DS.Model} type @param {String} id - @returns {Promise} promise + @param {DS.Model} record + @return {Promise} promise */ - find: function(store, type, id) { - return this.ajax(this.buildURL(type.typeKey, id), 'GET'); + find: function(store, type, id, record) { + return this.ajax(this.buildURL(type.typeKey, id, record), 'GET'); }, /** @@ -1137,7 +1006,7 @@ define("ember-data/lib/adapters/rest_adapter", @param {DS.Store} store @param {subclass of DS.Model} type @param {String} sinceToken - @returns {Promise} promise + @return {Promise} promise */ findAll: function(store, type, sinceToken) { var query; @@ -1164,16 +1033,14 @@ define("ember-data/lib/adapters/rest_adapter", @param {DS.Store} store @param {subclass of DS.Model} type @param {Object} query - @returns {Promise} promise + @return {Promise} promise */ findQuery: function(store, type, query) { return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query }); }, /** - Called by the store in order to fetch a JSON array for - the unloaded records in a has-many relationship that were originally - specified as IDs. + Called by the store in order to fetch several records together if `coalesceFindRequests` is true For example, if the original payload looks like: @@ -1202,10 +1069,11 @@ define("ember-data/lib/adapters/rest_adapter", @param {DS.Store} store @param {subclass of DS.Model} type @param {Array} ids - @returns {Promise} promise + @param {Array} records + @return {Promise} promise */ - findMany: function(store, type, ids) { - return this.ajax(this.buildURL(type.typeKey), 'GET', { data: { ids: ids } }); + findMany: function(store, type, ids, records) { + return this.ajax(this.buildURL(type.typeKey, ids, records), 'GET', { data: { ids: ids } }); }, /** @@ -1235,12 +1103,12 @@ define("ember-data/lib/adapters/rest_adapter", @param {DS.Store} store @param {DS.Model} record @param {String} url - @returns {Promise} promise + @return {Promise} promise */ - findHasMany: function(store, record, url) { - var host = get(this, 'host'), - id = get(record, 'id'), - type = record.constructor.typeKey; + findHasMany: function(store, record, url, relationship) { + var host = ember$data$lib$adapters$rest_adapter$$get(this, 'host'); + var id = ember$data$lib$adapters$rest_adapter$$get(record, 'id'); + var type = record.constructor.typeKey; if (host && url.charAt(0) === '/' && url.charAt(1) !== '/') { url = host + url; @@ -1274,11 +1142,11 @@ define("ember-data/lib/adapters/rest_adapter", @param {DS.Store} store @param {DS.Model} record @param {String} url - @returns {Promise} promise + @return {Promise} promise */ - findBelongsTo: function(store, record, url) { - var id = get(record, 'id'), - type = record.constructor.typeKey; + findBelongsTo: function(store, record, url, relationship) { + var id = ember$data$lib$adapters$rest_adapter$$get(record, 'id'); + var type = record.constructor.typeKey; return this.ajax(this.urlPrefix(url, this.buildURL(type, id)), 'GET'); }, @@ -1297,7 +1165,7 @@ define("ember-data/lib/adapters/rest_adapter", @param {DS.Store} store @param {subclass of DS.Model} type @param {DS.Model} record - @returns {Promise} promise + @return {Promise} promise */ createRecord: function(store, type, record) { var data = {}; @@ -1305,7 +1173,7 @@ define("ember-data/lib/adapters/rest_adapter", serializer.serializeIntoHash(data, type, record, { includeId: true }); - return this.ajax(this.buildURL(type.typeKey), "POST", { data: data }); + return this.ajax(this.buildURL(type.typeKey, null, record), "POST", { data: data }); }, /** @@ -1322,7 +1190,7 @@ define("ember-data/lib/adapters/rest_adapter", @param {DS.Store} store @param {subclass of DS.Model} type @param {DS.Model} record - @returns {Promise} promise + @return {Promise} promise */ updateRecord: function(store, type, record) { var data = {}; @@ -1330,9 +1198,9 @@ define("ember-data/lib/adapters/rest_adapter", serializer.serializeIntoHash(data, type, record); - var id = get(record, 'id'); + var id = ember$data$lib$adapters$rest_adapter$$get(record, 'id'); - return this.ajax(this.buildURL(type.typeKey, id), "PUT", { data: data }); + return this.ajax(this.buildURL(type.typeKey, id, record), "PUT", { data: data }); }, /** @@ -1344,12 +1212,12 @@ define("ember-data/lib/adapters/rest_adapter", @param {DS.Store} store @param {subclass of DS.Model} type @param {DS.Model} record - @returns {Promise} promise + @return {Promise} promise */ deleteRecord: function(store, type, record) { - var id = get(record, 'id'); + var id = ember$data$lib$adapters$rest_adapter$$get(record, 'id'); - return this.ajax(this.buildURL(type.typeKey, id), "DELETE"); + return this.ajax(this.buildURL(type.typeKey, id, record), "DELETE"); }, /** @@ -1365,15 +1233,20 @@ define("ember-data/lib/adapters/rest_adapter", @method buildURL @param {String} type @param {String} id - @returns {String} url + @param {DS.Model} record + @return {String} url */ - buildURL: function(type, id) { + buildURL: function(type, id, record) { var url = [], - host = get(this, 'host'), + host = ember$data$lib$adapters$rest_adapter$$get(this, 'host'), prefix = this.urlPrefix(); if (type) { url.push(this.pathForType(type)); } - if (id) { url.push(id); } + + //We might get passed in an array of ids from findMany + //in which case we don't want to modify the url, as the + //ids will be passed in through a query param + if (id && !Ember.isArray(id)) { url.push(encodeURIComponent(id)); } if (prefix) { url.unshift(prefix); } @@ -1391,9 +1264,9 @@ define("ember-data/lib/adapters/rest_adapter", @return {String} urlPrefix */ urlPrefix: function(path, parentURL) { - var host = get(this, 'host'), - namespace = get(this, 'namespace'), - url = []; + var host = ember$data$lib$adapters$rest_adapter$$get(this, 'host'); + var namespace = ember$data$lib$adapters$rest_adapter$$get(this, 'namespace'); + var url = []; if (path) { // Absolute path @@ -1418,6 +1291,95 @@ define("ember-data/lib/adapters/rest_adapter", return url.join('/'); }, + _stripIDFromURL: function(store, record) { + var type = record.constructor; + var url = this.buildURL(type.typeKey, record.get('id'), record); + + var expandedURL = url.split('/'); + //Case when the url is of the format ...something/:id + var lastSegment = expandedURL[ expandedURL.length - 1 ]; + var id = record.get('id'); + if (lastSegment === id) { + expandedURL[expandedURL.length - 1] = ""; + } else if(ember$data$lib$adapters$rest_adapter$$endsWith(lastSegment, '?id=' + id)) { + //Case when the url is of the format ...something?id=:id + expandedURL[expandedURL.length - 1] = lastSegment.substring(0, lastSegment.length - id.length - 1); + } + + return expandedURL.join('/'); + }, + + /** + http://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers + */ + maxUrlLength: 2048, + + /** + Organize records into groups, each of which is to be passed to separate + calls to `findMany`. + + This implementation groups together records that have the same base URL but + differing ids. For example `/comments/1` and `/comments/2` will be grouped together + because we know findMany can coalesce them together as `/comments?ids[]=1&ids[]=2` + + It also supports urls where ids are passed as a query param, such as `/comments?id=1` + but not those where there is more than 1 query param such as `/comments?id=2&name=David` + Currently only the query param of `id` is supported. If you need to support others, please + override this or the `_stripIDFromURL` method. + + It does not group records that have differing base urls, such as for example: `/posts/1/comments/2` + and `/posts/2/comments/3` + + @method groupRecordsForFindMany + @param {DS.Store} store + @param {Array} records + @return {Array} an array of arrays of records, each of which is to be + loaded separately by `findMany`. + */ + groupRecordsForFindMany: function (store, records) { + var groups = ember$data$lib$system$map$$MapWithDefault.create({defaultValue: function(){return [];}}); + var adapter = this; + var maxUrlLength = this.maxUrlLength; + + ember$data$lib$adapters$rest_adapter$$forEach.call(records, function(record){ + var baseUrl = adapter._stripIDFromURL(store, record); + groups.get(baseUrl).push(record); + }); + + function splitGroupToFitInUrl(group, maxUrlLength, paramNameLength) { + var baseUrl = adapter._stripIDFromURL(store, group[0]); + var idsSize = 0; + var splitGroups = [[]]; + + ember$data$lib$adapters$rest_adapter$$forEach.call(group, function(record) { + var additionalLength = encodeURIComponent(record.get('id')).length + paramNameLength; + if (baseUrl.length + idsSize + additionalLength >= maxUrlLength) { + idsSize = 0; + splitGroups.push([]); + } + + idsSize += additionalLength; + + var lastGroupIndex = splitGroups.length - 1; + splitGroups[lastGroupIndex].push(record); + }); + + return splitGroups; + } + + var groupsArray = []; + groups.forEach(function(group, key){ + var paramNameLength = '&ids%5B%5D='.length; + var splitGroups = splitGroupToFitInUrl(group, maxUrlLength, paramNameLength); + + ember$data$lib$adapters$rest_adapter$$forEach.call(splitGroups, function(splitGroup) { + groupsArray.push(splitGroup); + }); + }); + + return groupsArray; + }, + /** Determines the pathname for a given type. @@ -1430,17 +1392,17 @@ define("ember-data/lib/adapters/rest_adapter", endpoint of "/line_items/". ```js - DS.RESTAdapter.reopen({ + App.ApplicationAdapter = DS.RESTAdapter.extend({ pathForType: function(type) { var decamelized = Ember.String.decamelize(type); return Ember.String.pluralize(decamelized); - }; + } }); ``` @method pathForType @param {String} type - @returns {String} path + @return {String} path **/ pathForType: function(type) { var camelized = Ember.String.camelize(type); @@ -1448,19 +1410,25 @@ define("ember-data/lib/adapters/rest_adapter", }, /** - Takes an ajax response, and returns a relevant error. + Takes an ajax response, and returns an error payload. Returning a `DS.InvalidError` from this method will cause the record to transition into the `invalid` state and make the `errors` object available on the record. + This function should return the entire payload as received from the + server. Error object extraction and normalization of model errors + should be performed by `extractErrors` on the serializer. + + Example + ```javascript App.ApplicationAdapter = DS.RESTAdapter.extend({ ajaxError: function(jqXHR) { var error = this._super(jqXHR); if (jqXHR && jqXHR.status === 422) { - var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"]; + var jsonErrors = Ember.$.parseJSON(jqXHR.responseText); return new DS.InvalidError(jsonErrors); } else { @@ -1479,16 +1447,48 @@ define("ember-data/lib/adapters/rest_adapter", @method ajaxError @param {Object} jqXHR + @param {Object} responseText @return {Object} jqXHR */ - ajaxError: function(jqXHR) { - if (jqXHR) { + ajaxError: function(jqXHR, responseText, errorThrown) { + var isObject = jqXHR !== null && typeof jqXHR === 'object'; + + if (isObject) { jqXHR.then = null; + if (!jqXHR.errorThrown) { + jqXHR.errorThrown = errorThrown; + } } return jqXHR; }, + /** + Takes an ajax response, and returns the json payload. + + By default this hook just returns the jsonPayload passed to it. + You might want to override it in two cases: + + 1. Your API might return useful results in the request headers. + If you need to access these, you can override this hook to copy them + from jqXHR to the payload object so they can be processed in you serializer. + + + 2. Your API might return errors as successful responses with status code + 200 and an Errors text or object. You can return a DS.InvalidError from + this hook and it will automatically reject the promise and put your record + into the invalid state. + + @method ajaxSuccess + @param {Object} jqXHR + @param {Object} jsonPayload + @return {Object} jsonPayload + */ + + ajaxSuccess: function(jqXHR, jsonPayload) { + return jsonPayload; + }, + /** Takes a URL, an HTTP method and a hash of data, and makes an HTTP request. @@ -1513,22 +1513,27 @@ define("ember-data/lib/adapters/rest_adapter", @param {Object} hash @return {Promise} promise */ - ajax: function(url, type, hash) { + ajax: function(url, type, options) { var adapter = this; return new Ember.RSVP.Promise(function(resolve, reject) { - hash = adapter.ajaxOptions(url, type, hash); + var hash = adapter.ajaxOptions(url, type, options); - hash.success = function(json) { - Ember.run(null, resolve, json); + hash.success = function(json, textStatus, jqXHR) { + json = adapter.ajaxSuccess(jqXHR, json); + if (json instanceof ember$data$lib$system$adapter$$InvalidError) { + Ember.run(null, reject, json); + } else { + Ember.run(null, resolve, json); + } }; hash.error = function(jqXHR, textStatus, errorThrown) { - Ember.run(null, reject, adapter.ajaxError(jqXHR)); + Ember.run(null, reject, adapter.ajaxError(jqXHR, jqXHR.responseText, errorThrown)); }; Ember.$.ajax(hash); - }, "DS: RestAdapter#ajax " + type + " to " + url); + }, 'DS: RESTAdapter#ajax ' + type + ' to ' + url); }, /** @@ -1539,8 +1544,8 @@ define("ember-data/lib/adapters/rest_adapter", @param {Object} hash @return {Object} hash */ - ajaxOptions: function(url, type, hash) { - hash = hash || {}; + ajaxOptions: function(url, type, options) { + var hash = options || {}; hash.url = url; hash.type = type; hash.dataType = 'json'; @@ -1551,9582 +1556,10632 @@ define("ember-data/lib/adapters/rest_adapter", hash.data = JSON.stringify(hash.data); } - if (this.headers !== undefined) { - var headers = this.headers; + var headers = ember$data$lib$adapters$rest_adapter$$get(this, 'headers'); + if (headers !== undefined) { hash.beforeSend = function (xhr) { - forEach.call(Ember.keys(headers), function(key) { + ember$data$lib$adapters$rest_adapter$$forEach.call(Ember.keys(headers), function(key) { xhr.setRequestHeader(key, headers[key]); }); }; } - return hash; } - }); - __exports__["default"] = RESTAdapter; - }); -define("ember-data/lib/core", - ["exports"], - function(__exports__) { - "use strict"; - /** - @module ember-data - */ + //From http://stackoverflow.com/questions/280634/endswith-in-javascript + function ember$data$lib$adapters$rest_adapter$$endsWith(string, suffix){ + if (typeof String.prototype.endsWith !== 'function') { + return string.indexOf(suffix, string.length - suffix.length) !== -1; + } else { + return string.endsWith(suffix); + } + } - /** - All Ember Data methods and functions are defined inside of this namespace. + var ember$inflector$lib$system$inflector$$capitalize = Ember.String.capitalize; - @class DS - @static - */ - var DS; - if ('undefined' === typeof DS) { - /** - @property VERSION - @type String - @default '1.0.0-beta.7+canary.b45e23ba' - @static - */ - DS = Ember.Namespace.create({ - VERSION: '1.0.0-beta.7+canary.b45e23ba' - }); + var ember$inflector$lib$system$inflector$$BLANK_REGEX = /^\s*$/; + var ember$inflector$lib$system$inflector$$LAST_WORD_DASHED_REGEX = /(\w+[_-])([a-z\d]+$)/; + var ember$inflector$lib$system$inflector$$LAST_WORD_CAMELIZED_REGEX = /(\w+)([A-Z][a-z\d]*$)/; + var ember$inflector$lib$system$inflector$$CAMELIZED_REGEX = /[A-Z][a-z\d]*$/; - if ('undefined' !== typeof window) { - window.DS = DS; + function ember$inflector$lib$system$inflector$$loadUncountable(rules, uncountable) { + for (var i = 0, length = uncountable.length; i < length; i++) { + rules.uncountable[uncountable[i].toLowerCase()] = true; } + } + + function ember$inflector$lib$system$inflector$$loadIrregular(rules, irregularPairs) { + var pair; + + for (var i = 0, length = irregularPairs.length; i < length; i++) { + pair = irregularPairs[i]; + + //pluralizing + rules.irregular[pair[0].toLowerCase()] = pair[1]; + rules.irregular[pair[1].toLowerCase()] = pair[1]; - if (Ember.libraries) { - Ember.libraries.registerCoreLibrary('Ember Data', DS.VERSION); + //singularizing + rules.irregularInverse[pair[1].toLowerCase()] = pair[0]; + rules.irregularInverse[pair[0].toLowerCase()] = pair[0]; } } - __exports__["default"] = DS; - }); -define("ember-data/lib/ext/date", - [], - function() { - "use strict"; /** - @module ember-data - */ + Inflector.Ember provides a mechanism for supplying inflection rules for your + application. Ember includes a default set of inflection rules, and provides an + API for providing additional rules. - /** - Date.parse with progressive enhancement for ISO 8601 - - © 2011 Colin Snover - - Released under MIT license. - - @class Date - @namespace Ember - @static - */ - Ember.Date = Ember.Date || {}; - - var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ]; - - /** - @method parse - @param date - */ - Ember.Date.parse = function (date) { - var timestamp, struct, minutesOffset = 0; - - // ES5 §15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string - // before falling back to any implementation-specific date parsing, so that’s what we do, even if native - // implementations could be faster - // 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ± 10 tzHH 11 tzmm - if ((struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(date))) { - // avoid NaN timestamps caused by “undefined” values being passed to Date.UTC - for (var i = 0, k; (k = numericKeys[i]); ++i) { - struct[k] = +struct[k] || 0; - } - - // allow undefined days and months - struct[2] = (+struct[2] || 1) - 1; - struct[3] = +struct[3] || 1; - - if (struct[8] !== 'Z' && struct[9] !== undefined) { - minutesOffset = struct[10] * 60 + struct[11]; + Examples: - if (struct[9] === '+') { - minutesOffset = 0 - minutesOffset; - } - } + Creating an inflector with no rules. - timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]); - } - else { - timestamp = origParse ? origParse(date) : NaN; - } + ```js + var inflector = new Ember.Inflector(); + ``` - return timestamp; - }; + Creating an inflector with the default ember ruleset. - if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Date) { - Date.parse = Ember.Date.parse; - } - }); -define("ember-data/lib/initializers", - ["./system/store","./serializers","./adapters","./system/debug/debug_adapter","./system/container_proxy","./transforms"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { - "use strict"; - var Store = __dependency1__["default"]; - var JSONSerializer = __dependency2__.JSONSerializer; - var RESTSerializer = __dependency2__.RESTSerializer; - var RESTAdapter = __dependency3__.RESTAdapter; - var DebugAdapter = __dependency4__["default"]; - var ContainerProxy = __dependency5__["default"]; - var BooleanTransform = __dependency6__.BooleanTransform; - var DateTransform = __dependency6__.DateTransform; - var StringTransform = __dependency6__.StringTransform; - var NumberTransform = __dependency6__.NumberTransform; + ```js + var inflector = new Ember.Inflector(Ember.Inflector.defaultRules); - /** - @module ember-data - */ + inflector.pluralize('cow'); //=> 'kine' + inflector.singularize('kine'); //=> 'cow' + ``` - var set = Ember.set; + Creating an inflector and adding rules later. - /* - This code registers an injection for Ember.Application. + ```javascript + var inflector = Ember.Inflector.inflector; - If an Ember.js developer defines a subclass of DS.Store on their application, - this code will automatically instantiate it and make it available on the - router. + inflector.pluralize('advice'); // => 'advices' + inflector.uncountable('advice'); + inflector.pluralize('advice'); // => 'advice' - Additionally, after an application's controllers have been injected, they will - each have the store made available to them. + inflector.pluralize('formula'); // => 'formulas' + inflector.irregular('formula', 'formulae'); + inflector.pluralize('formula'); // => 'formulae' - For example, imagine an Ember.js application with the following classes: + // you would not need to add these as they are the default rules + inflector.plural(/$/, 's'); + inflector.singular(/s$/i, ''); + ``` - App.Store = DS.Store.extend({ - adapter: 'custom' - }); + Creating an inflector with a nondefault ruleset. - App.PostsController = Ember.ArrayController.extend({ - // ... - }); + ```javascript + var rules = { + plurals: [ /$/, 's' ], + singular: [ /\s$/, '' ], + irregularPairs: [ + [ 'cow', 'kine' ] + ], + uncountable: [ 'fish' ] + }; - When the application is initialized, `App.Store` will automatically be - instantiated, and the instance of `App.PostsController` will have its `store` - property set to that instance. + var inflector = new Ember.Inflector(rules); + ``` - Note that this code will only be run if the `ember-application` package is - loaded. If Ember Data is being used in an environment other than a - typical application (e.g., node.js where only `ember-runtime` is available), - this code will be ignored. + @class Inflector + @namespace Ember */ + function ember$inflector$lib$system$inflector$$Inflector(ruleSet) { + ruleSet = ruleSet || {}; + ruleSet.uncountable = ruleSet.uncountable || ember$inflector$lib$system$inflector$$makeDictionary(); + ruleSet.irregularPairs = ruleSet.irregularPairs || ember$inflector$lib$system$inflector$$makeDictionary(); - Ember.onLoad('Ember.Application', function(Application) { - Application.initializer({ - name: "store", - - initialize: function(container, application) { - application.register('store:main', application.Store || Store); - - // allow older names to be looked up - - var proxy = new ContainerProxy(container); - proxy.registerDeprecations([ - {deprecated: 'serializer:_default', valid: 'serializer:-default'}, - {deprecated: 'serializer:_rest', valid: 'serializer:-rest'}, - {deprecated: 'adapter:_rest', valid: 'adapter:-rest'} - ]); - - // new go forward paths - application.register('serializer:-default', JSONSerializer); - application.register('serializer:-rest', RESTSerializer); - application.register('adapter:-rest', RESTAdapter); + var rules = this.rules = { + plurals: ruleSet.plurals || [], + singular: ruleSet.singular || [], + irregular: ember$inflector$lib$system$inflector$$makeDictionary(), + irregularInverse: ember$inflector$lib$system$inflector$$makeDictionary(), + uncountable: ember$inflector$lib$system$inflector$$makeDictionary() + }; - // Eagerly generate the store so defaultStore is populated. - // TODO: Do this in a finisher hook - container.lookup('store:main'); - } - }); + ember$inflector$lib$system$inflector$$loadUncountable(rules, ruleSet.uncountable); + ember$inflector$lib$system$inflector$$loadIrregular(rules, ruleSet.irregularPairs); - Application.initializer({ - name: "transforms", - before: "store", + this.enableCache(); + } - initialize: function(container, application) { - application.register('transform:boolean', BooleanTransform); - application.register('transform:date', DateTransform); - application.register('transform:number', NumberTransform); - application.register('transform:string', StringTransform); - } - }); + if (!Object.create && !Object.create(null).hasOwnProperty) { + throw new Error("This browser does not support Object.create(null), please polyfil with es5-sham: http://git.io/yBU2rg"); + } - Application.initializer({ - name: "data-adapter", - before: "store", + function ember$inflector$lib$system$inflector$$makeDictionary() { + var cache = Object.create(null); + cache['_dict'] = null; + delete cache['_dict']; + return cache; + } - initialize: function(container, application) { - application.register('data-adapter:main', DebugAdapter); - } - }); + ember$inflector$lib$system$inflector$$Inflector.prototype = { + /** + @public - Application.initializer({ - name: "injectStore", - before: "store", + As inflections can be costly, and commonly the same subset of words are repeatedly + inflected an optional cache is provided. - initialize: function(container, application) { - application.inject('controller', 'store', 'store:main'); - application.inject('route', 'store', 'store:main'); - application.inject('serializer', 'store', 'store:main'); - application.inject('data-adapter', 'store', 'store:main'); - } - }); + @method enableCache + */ + enableCache: function() { + this.purgeCache(); - }); - }); -define("ember-data/lib/main", - ["./core","./ext/date","./system/store","./system/model","./system/changes","./system/adapter","./system/debug","./system/record_arrays","./system/record_array_manager","./adapters","./serializers/json_serializer","./serializers/rest_serializer","../../ember-inflector/lib/main","../../activemodel-adapter/lib/main","./transforms","./system/relationships","./initializers","./system/container_proxy","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __exports__) { - "use strict"; - /** - Ember Data + this.singularize = function(word) { + this._cacheUsed = true; + return this._sCache[word] || (this._sCache[word] = this._singularize(word)); + }; - @module ember-data - @main ember-data - */ + this.pluralize = function(word) { + this._cacheUsed = true; + return this._pCache[word] || (this._pCache[word] = this._pluralize(word)); + }; + }, - // support RSVP 2.x via resolve, but prefer RSVP 3.x's Promise.cast - Ember.RSVP.Promise.cast = Ember.RSVP.Promise.cast || Ember.RSVP.resolve; + /** + @public - var DS = __dependency1__["default"]; - - var Store = __dependency3__.Store; - var PromiseArray = __dependency3__.PromiseArray; - var PromiseObject = __dependency3__.PromiseObject; - var Model = __dependency4__.Model; - var Errors = __dependency4__.Errors; - var RootState = __dependency4__.RootState; - var attr = __dependency4__.attr; - var AttributeChange = __dependency5__.AttributeChange; - var RelationshipChange = __dependency5__.RelationshipChange; - var RelationshipChangeAdd = __dependency5__.RelationshipChangeAdd; - var RelationshipChangeRemove = __dependency5__.RelationshipChangeRemove; - var OneToManyChange = __dependency5__.OneToManyChange; - var ManyToNoneChange = __dependency5__.ManyToNoneChange; - var OneToOneChange = __dependency5__.OneToOneChange; - var ManyToManyChange = __dependency5__.ManyToManyChange; - var InvalidError = __dependency6__.InvalidError; - var Adapter = __dependency6__.Adapter; - var DebugAdapter = __dependency7__["default"]; - var RecordArray = __dependency8__.RecordArray; - var FilteredRecordArray = __dependency8__.FilteredRecordArray; - var AdapterPopulatedRecordArray = __dependency8__.AdapterPopulatedRecordArray; - var ManyArray = __dependency8__.ManyArray; - var RecordArrayManager = __dependency9__["default"]; - var RESTAdapter = __dependency10__.RESTAdapter; - var FixtureAdapter = __dependency10__.FixtureAdapter; - var JSONSerializer = __dependency11__["default"]; - var RESTSerializer = __dependency12__["default"]; - var ActiveModelAdapter = __dependency14__.ActiveModelAdapter; - var ActiveModelSerializer = __dependency14__.ActiveModelSerializer; - var EmbeddedRecordsMixin = __dependency14__.EmbeddedRecordsMixin; - - var Transform = __dependency15__.Transform; - var DateTransform = __dependency15__.DateTransform; - var NumberTransform = __dependency15__.NumberTransform; - var StringTransform = __dependency15__.StringTransform; - var BooleanTransform = __dependency15__.BooleanTransform; - - var hasMany = __dependency16__.hasMany; - var belongsTo = __dependency16__.belongsTo; - - var ContainerProxy = __dependency18__["default"]; - - DS.Store = Store; - DS.PromiseArray = PromiseArray; - DS.PromiseObject = PromiseObject; - - DS.Model = Model; - DS.RootState = RootState; - DS.attr = attr; - DS.Errors = Errors; - - DS.AttributeChange = AttributeChange; - DS.RelationshipChange = RelationshipChange; - DS.RelationshipChangeAdd = RelationshipChangeAdd; - DS.OneToManyChange = OneToManyChange; - DS.ManyToNoneChange = OneToManyChange; - DS.OneToOneChange = OneToOneChange; - DS.ManyToManyChange = ManyToManyChange; - - DS.Adapter = Adapter; - DS.InvalidError = InvalidError; - - DS.DebugAdapter = DebugAdapter; - - DS.RecordArray = RecordArray; - DS.FilteredRecordArray = FilteredRecordArray; - DS.AdapterPopulatedRecordArray = AdapterPopulatedRecordArray; - DS.ManyArray = ManyArray; - - DS.RecordArrayManager = RecordArrayManager; - - DS.RESTAdapter = RESTAdapter; - DS.FixtureAdapter = FixtureAdapter; - - DS.RESTSerializer = RESTSerializer; - DS.JSONSerializer = JSONSerializer; - - DS.Transform = Transform; - DS.DateTransform = DateTransform; - DS.StringTransform = StringTransform; - DS.NumberTransform = NumberTransform; - DS.BooleanTransform = BooleanTransform; - - DS.ActiveModelAdapter = ActiveModelAdapter; - DS.ActiveModelSerializer = ActiveModelSerializer; - DS.EmbeddedRecordsMixin = EmbeddedRecordsMixin; - - DS.belongsTo = belongsTo; - DS.hasMany = hasMany; - - DS.ContainerProxy = ContainerProxy; - - __exports__["default"] = DS; - }); -define("ember-data/lib/serializers", - ["./serializers/json_serializer","./serializers/rest_serializer","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var JSONSerializer = __dependency1__["default"]; - var RESTSerializer = __dependency2__["default"]; - - __exports__.JSONSerializer = JSONSerializer; - __exports__.RESTSerializer = RESTSerializer; - }); -define("ember-data/lib/serializers/json_serializer", - ["exports"], - function(__exports__) { - "use strict"; - var get = Ember.get, set = Ember.set, isNone = Ember.isNone; + @method purgedCache + */ + purgeCache: function() { + this._cacheUsed = false; + this._sCache = ember$inflector$lib$system$inflector$$makeDictionary(); + this._pCache = ember$inflector$lib$system$inflector$$makeDictionary(); + }, - /** - In Ember Data a Serializer is used to serialize and deserialize - records when they are transferred in and out of an external source. - This process involves normalizing property names, transforming - attribute values and serializing relationships. + /** + @public + disable caching - For maximum performance Ember Data recommends you use the - [RESTSerializer](DS.RESTSerializer.html) or one of its subclasses. + @method disableCache; + */ + disableCache: function() { + this._sCache = null; + this._pCache = null; + this.singularize = function(word) { + return this._singularize(word); + }; - `JSONSerializer` is useful for simpler or legacy backends that may - not support the http://jsonapi.org/ spec. + this.pluralize = function(word) { + return this._pluralize(word); + }; + }, - @class JSONSerializer - @namespace DS - */ - var JSONSerializer = Ember.Object.extend({ /** - The primaryKey is used when serializing and deserializing - data. Ember Data always uses the `id` property to store the id of - the record. The external source may not always follow this - convention. In these cases it is useful to override the - primaryKey property to match the primaryKey of your external - store. + @method plural + @param {RegExp} regex + @param {String} string + */ + plural: function(regex, string) { + if (this._cacheUsed) { this.purgeCache(); } + this.rules.plurals.push([regex, string.toLowerCase()]); + }, - Example + /** + @method singular + @param {RegExp} regex + @param {String} string + */ + singular: function(regex, string) { + if (this._cacheUsed) { this.purgeCache(); } + this.rules.singular.push([regex, string.toLowerCase()]); + }, - ```javascript - App.ApplicationSerializer = DS.JSONSerializer.extend({ - primaryKey: '_id' - }); - ``` + /** + @method uncountable + @param {String} regex + */ + uncountable: function(string) { + if (this._cacheUsed) { this.purgeCache(); } + ember$inflector$lib$system$inflector$$loadUncountable(this.rules, [string.toLowerCase()]); + }, - @property primaryKey - @type {String} - @default 'id' + /** + @method irregular + @param {String} singular + @param {String} plural */ - primaryKey: 'id', + irregular: function (singular, plural) { + if (this._cacheUsed) { this.purgeCache(); } + ember$inflector$lib$system$inflector$$loadIrregular(this.rules, [[singular, plural]]); + }, /** - Given a subclass of `DS.Model` and a JSON object this method will - iterate through each attribute of the `DS.Model` and invoke the - `DS.Transform#deserialize` method on the matching property of the - JSON object. This method is typically called after the - serializer's `normalize` method. + @method pluralize + @param {String} word + */ + pluralize: function(word) { + return this._pluralize(word); + }, - @method applyTransforms - @private - @param {subclass of DS.Model} type - @param {Object} data The data to transform - @return {Object} data The transformed data object + _pluralize: function(word) { + return this.inflect(word, this.rules.plurals, this.rules.irregular); + }, + /** + @method singularize + @param {String} word */ - applyTransforms: function(type, data) { - type.eachTransformedAttribute(function(key, type) { - var transform = this.transformFor(type); - data[key] = transform.deserialize(data[key]); - }, this); + singularize: function(word) { + return this._singularize(word); + }, - return data; + _singularize: function(word) { + return this.inflect(word, this.rules.singular, this.rules.irregularInverse); }, /** - Normalizes a part of the JSON payload returned by - the server. You should override this method, munge the hash - and call super if you have generic normalization to do. + @protected - It takes the type of the record that is being normalized - (as a DS.Model class), the property where the hash was - originally found, and the hash to normalize. + @method inflect + @param {String} word + @param {Object} typeRules + @param {Object} irregular + */ + inflect: function(word, typeRules, irregular) { + var inflection, substitution, result, lowercase, wordSplit, + firstPhrase, lastWord, isBlank, isCamelized, isUncountable, + isIrregular, isIrregularInverse, rule; - You can use this method, for example, to normalize underscored keys to camelized - or other general-purpose normalizations. + isBlank = ember$inflector$lib$system$inflector$$BLANK_REGEX.test(word); + isCamelized = ember$inflector$lib$system$inflector$$CAMELIZED_REGEX.test(word); + firstPhrase = ""; - Example + if (isBlank) { + return word; + } - ```javascript - App.ApplicationSerializer = DS.JSONSerializer.extend({ - normalize: function(type, hash) { - var fields = Ember.get(type, 'fields'); - fields.forEach(function(field) { - var payloadField = Ember.String.underscore(field); - if (field === payloadField) { return; } + lowercase = word.toLowerCase(); + wordSplit = ember$inflector$lib$system$inflector$$LAST_WORD_DASHED_REGEX.exec(word) || ember$inflector$lib$system$inflector$$LAST_WORD_CAMELIZED_REGEX.exec(word); + if (wordSplit){ + firstPhrase = wordSplit[1]; + lastWord = wordSplit[2].toLowerCase(); + } - hash[field] = hash[payloadField]; - delete hash[payloadField]; - }); - return this._super.apply(this, arguments); - } - }); - ``` + isUncountable = this.rules.uncountable[lowercase] || this.rules.uncountable[lastWord]; - @method normalize - @param {subclass of DS.Model} type - @param {Object} hash - @return {Object} - */ - normalize: function(type, hash) { - if (!hash) { return hash; } + if (isUncountable) { + return word; + } - this.applyTransforms(type, hash); - return hash; - }, + isIrregular = irregular && (irregular[lowercase] || irregular[lastWord]); - // SERIALIZE - /** - Called when a record is saved in order to convert the - record into JSON. + if (isIrregular) { + if (irregular[lowercase]){ + return isIrregular; + } + else { + isIrregular = (isCamelized) ? ember$inflector$lib$system$inflector$$capitalize(isIrregular) : isIrregular; + return firstPhrase + isIrregular; + } + } - By default, it creates a JSON object with a key for - each attribute and belongsTo relationship. + for (var i = typeRules.length, min = 0; i > min; i--) { + inflection = typeRules[i-1]; + rule = inflection[0]; - For example, consider this model: + if (rule.test(word)) { + break; + } + } - ```javascript - App.Comment = DS.Model.extend({ - title: DS.attr(), - body: DS.attr(), + inflection = inflection || []; - author: DS.belongsTo('user') - }); - ``` + rule = inflection[0]; + substitution = inflection[1]; - The default serialization would create a JSON object like: + result = word.replace(rule, substitution); - ```javascript - { - "title": "Rails is unagi", - "body": "Rails? Omakase? O_O", - "author": 12 - } - ``` + return result; + } + }; - By default, attributes are passed through as-is, unless - you specified an attribute type (`DS.attr('date')`). If - you specify a transform, the JavaScript value will be - serialized when inserted into the JSON hash. + var ember$inflector$lib$system$inflector$$default = ember$inflector$lib$system$inflector$$Inflector; - By default, belongs-to relationships are converted into - IDs when inserted into the JSON hash. + function ember$inflector$lib$system$string$$pluralize(word) { + return ember$inflector$lib$system$inflector$$default.inflector.pluralize(word); + } - ## IDs + function ember$inflector$lib$system$string$$singularize(word) { + return ember$inflector$lib$system$inflector$$default.inflector.singularize(word); + } - `serialize` takes an options hash with a single option: - `includeId`. If this option is `true`, `serialize` will, - by default include the ID in the JSON object it builds. + var ember$inflector$lib$system$inflections$$default = { + plurals: [ + [/$/, 's'], + [/s$/i, 's'], + [/^(ax|test)is$/i, '$1es'], + [/(octop|vir)us$/i, '$1i'], + [/(octop|vir)i$/i, '$1i'], + [/(alias|status)$/i, '$1es'], + [/(bu)s$/i, '$1ses'], + [/(buffal|tomat)o$/i, '$1oes'], + [/([ti])um$/i, '$1a'], + [/([ti])a$/i, '$1a'], + [/sis$/i, 'ses'], + [/(?:([^f])fe|([lr])f)$/i, '$1$2ves'], + [/(hive)$/i, '$1s'], + [/([^aeiouy]|qu)y$/i, '$1ies'], + [/(x|ch|ss|sh)$/i, '$1es'], + [/(matr|vert|ind)(?:ix|ex)$/i, '$1ices'], + [/^(m|l)ouse$/i, '$1ice'], + [/^(m|l)ice$/i, '$1ice'], + [/^(ox)$/i, '$1en'], + [/^(oxen)$/i, '$1'], + [/(quiz)$/i, '$1zes'] + ], - The adapter passes in `includeId: true` when serializing - a record for `createRecord`, but not for `updateRecord`. + singular: [ + [/s$/i, ''], + [/(ss)$/i, '$1'], + [/(n)ews$/i, '$1ews'], + [/([ti])a$/i, '$1um'], + [/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '$1sis'], + [/(^analy)(sis|ses)$/i, '$1sis'], + [/([^f])ves$/i, '$1fe'], + [/(hive)s$/i, '$1'], + [/(tive)s$/i, '$1'], + [/([lr])ves$/i, '$1f'], + [/([^aeiouy]|qu)ies$/i, '$1y'], + [/(s)eries$/i, '$1eries'], + [/(m)ovies$/i, '$1ovie'], + [/(x|ch|ss|sh)es$/i, '$1'], + [/^(m|l)ice$/i, '$1ouse'], + [/(bus)(es)?$/i, '$1'], + [/(o)es$/i, '$1'], + [/(shoe)s$/i, '$1'], + [/(cris|test)(is|es)$/i, '$1is'], + [/^(a)x[ie]s$/i, '$1xis'], + [/(octop|vir)(us|i)$/i, '$1us'], + [/(alias|status)(es)?$/i, '$1'], + [/^(ox)en/i, '$1'], + [/(vert|ind)ices$/i, '$1ex'], + [/(matr)ices$/i, '$1ix'], + [/(quiz)zes$/i, '$1'], + [/(database)s$/i, '$1'] + ], - ## Customization + irregularPairs: [ + ['person', 'people'], + ['man', 'men'], + ['child', 'children'], + ['sex', 'sexes'], + ['move', 'moves'], + ['cow', 'kine'], + ['zombie', 'zombies'] + ], - Your server may expect a different JSON format than the - built-in serialization format. + uncountable: [ + 'equipment', + 'information', + 'rice', + 'money', + 'species', + 'series', + 'fish', + 'sheep', + 'jeans', + 'police' + ] + }; - In that case, you can implement `serialize` yourself and - return a JSON hash of your choosing. + ember$inflector$lib$system$inflector$$default.inflector = new ember$inflector$lib$system$inflector$$default(ember$inflector$lib$system$inflections$$default); - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - serialize: function(post, options) { - var json = { - POST_TTL: post.get('title'), - POST_BDY: post.get('body'), - POST_CMS: post.get('comments').mapProperty('id') - } + /** + * + * If you have Ember Inflector (such as if Ember Data is present), + * singularize a word. For example, turn "oxen" into "ox". + * + * Example: + * + * {{singularize myProperty}} + * {{singularize "oxen"}} + * + * @for Ember.Handlebars.helpers + * @method singularize + * @param {String|Property} word word to singularize + */ + Ember.Handlebars.helper('singularize', ember$inflector$lib$system$string$$singularize); - if (options.includeId) { - json.POST_ID_ = post.get('id'); - } + /** + * + * If you have Ember Inflector (such as if Ember Data is present), + * pluralize a word. For example, turn "ox" into "oxen". + * + * Example: + * + * {{pluralize count myProperty}} + * {{pluralize 1 "oxen"}} + * {{pluralize myProperty}} + * {{pluralize "ox"}} + * + * @for Ember.Handlebars.helpers + * @method pluralize + * @param {Number|Property} [count] count of objects + * @param {String|Property} word word to pluralize + */ + Ember.Handlebars.helper('pluralize', function(count, word, options) { + if(arguments.length < 3) { + return ember$inflector$lib$system$string$$pluralize(count); + } else { + /* jshint eqeqeq: false */ + if(count != 1) { + /* jshint eqeqeq: true */ + word = ember$inflector$lib$system$string$$pluralize(word); + } + return count + " " + word; + } + }); - return json; - } - }); - ``` + if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { + /** + See {{#crossLink "Ember.String/pluralize"}}{{/crossLink}} - ## Customizing an App-Wide Serializer + @method pluralize + @for String + */ + String.prototype.pluralize = function() { + return ember$inflector$lib$system$string$$pluralize(this); + }; - If you want to define a serializer for your entire - application, you'll probably want to use `eachAttribute` - and `eachRelationship` on the record. + /** + See {{#crossLink "Ember.String/singularize"}}{{/crossLink}} - ```javascript - App.ApplicationSerializer = DS.JSONSerializer.extend({ - serialize: function(record, options) { - var json = {}; + @method singularize + @for String + */ + String.prototype.singularize = function() { + return ember$inflector$lib$system$string$$singularize(this); + }; + } - record.eachAttribute(function(name) { - json[serverAttributeName(name)] = record.get(name); - }) + ember$inflector$lib$system$inflector$$default.defaultRules = ember$inflector$lib$system$inflections$$default; + Ember.Inflector = ember$inflector$lib$system$inflector$$default; - record.eachRelationship(function(name, relationship) { - if (relationship.kind === 'hasMany') { - json[serverHasManyName(name)] = record.get(name).mapBy('id'); - } - }); + Ember.String.pluralize = ember$inflector$lib$system$string$$pluralize; + Ember.String.singularize = ember$inflector$lib$system$string$$singularize; - if (options.includeId) { - json.ID_ = record.get('id'); - } + var ember$inflector$lib$main$$default = ember$inflector$lib$system$inflector$$default; - return json; - } - }); + /** + @module ember-data + */ - function serverAttributeName(attribute) { - return attribute.underscore().toUpperCase(); - } + var activemodel$adapter$lib$system$active_model_adapter$$decamelize = Ember.String.decamelize, + activemodel$adapter$lib$system$active_model_adapter$$underscore = Ember.String.underscore; - function serverHasManyName(name) { - return serverAttributeName(name.singularize()) + "_IDS"; - } - ``` + /** + The ActiveModelAdapter is a subclass of the RESTAdapter designed to integrate + with a JSON API that uses an underscored naming convention instead of camelCasing. + It has been designed to work out of the box with the + [active\_model\_serializers](http://github.com/rails-api/active_model_serializers) + Ruby gem. This Adapter expects specific settings using ActiveModel::Serializers, + `embed :ids, embed_in_root: true` which sideloads the records. - This serializer will generate JSON that looks like this: + This adapter extends the DS.RESTAdapter by making consistent use of the camelization, + decamelization and pluralization methods to normalize the serialized JSON into a + format that is compatible with a conventional Rails backend and Ember Data. - ```javascript - { - "TITLE": "Rails is omakase", - "BODY": "Yep. Omakase.", - "COMMENT_IDS": [ 1, 2, 3 ] - } - ``` + ## JSON Structure - ## Tweaking the Default JSON + The ActiveModelAdapter expects the JSON returned from your server to follow + the REST adapter conventions substituting underscored keys for camelcased ones. - If you just want to do some small tweaks on the default JSON, - you can call super first and make the tweaks on the returned - JSON. + Unlike the DS.RESTAdapter, async relationship keys must be the singular form + of the relationship name, followed by "_id" for DS.belongsTo relationships, + or "_ids" for DS.hasMany relationships. - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - serialize: function(record, options) { - var json = this._super.apply(this, arguments); + ### Conventional Names - json.subject = json.title; - delete json.title; + Attribute names in your JSON payload should be the underscored versions of + the attributes in your Ember.js models. - return json; - } - }); - ``` + For example, if you have a `Person` model: - @method serialize - @param {subclass of DS.Model} record - @param {Object} options - @return {Object} json - */ - serialize: function(record, options) { - var json = {}; + ```js + App.FamousPerson = DS.Model.extend({ + firstName: DS.attr('string'), + lastName: DS.attr('string'), + occupation: DS.attr('string') + }); + ``` - if (options && options.includeId) { - var id = get(record, 'id'); + The JSON returned should look like this: - if (id) { - json[get(this, 'primaryKey')] = id; - } + ```js + { + "famous_person": { + "id": 1, + "first_name": "Barack", + "last_name": "Obama", + "occupation": "President" } + } + ``` - record.eachAttribute(function(key, attribute) { - this.serializeAttribute(record, json, key, attribute); - }, this); + Let's imagine that `Occupation` is just another model: - record.eachRelationship(function(key, relationship) { - if (relationship.kind === 'belongsTo') { - this.serializeBelongsTo(record, json, relationship); - } else if (relationship.kind === 'hasMany') { - this.serializeHasMany(record, json, relationship); - } - }, this); + ```js + App.Person = DS.Model.extend({ + firstName: DS.attr('string'), + lastName: DS.attr('string'), + occupation: DS.belongsTo('occupation') + }); - return json; - }, + App.Occupation = DS.Model.extend({ + name: DS.attr('string'), + salary: DS.attr('number'), + people: DS.hasMany('person') + }); + ``` - /** - `serializeAttribute` can be used to customize how `DS.attr` - properties are serialized + The JSON needed to avoid extra server calls, should look like this: - For example if you wanted to ensure all you attributes were always - serialized as properties on an `attributes` object you could - write: + ```js + { + "people": [{ + "id": 1, + "first_name": "Barack", + "last_name": "Obama", + "occupation_id": 1 + }], - ```javascript - App.ApplicationSerializer = DS.JSONSerializer.extend({ - serializeAttribute: function(record, json, key, attributes) { - json.attributes = json.attributes || {}; - this._super(record, json.attributes, key, attributes); - } - }); - ``` + "occupations": [{ + "id": 1, + "name": "President", + "salary": 100000, + "person_ids": [1] + }] + } + ``` - @method serializeAttribute - @param {DS.Model} record - @param {Object} json - @param {String} key - @param {Object} attribute - */ - serializeAttribute: function(record, json, key, attribute) { - var attrs = get(this, 'attrs'); - var value = get(record, key), type = attribute.type; + @class ActiveModelAdapter + @constructor + @namespace DS + @extends DS.RESTAdapter + **/ - if (type) { - var transform = this.transformFor(type); - value = transform.serialize(value); - } + var activemodel$adapter$lib$system$active_model_adapter$$ActiveModelAdapter = ember$data$lib$adapters$rest_adapter$$default.extend({ + defaultSerializer: '-active-model', + /** + The ActiveModelAdapter overrides the `pathForType` method to build + underscored URLs by decamelizing and pluralizing the object type name. - // if provided, use the mapping provided by `attrs` in - // the serializer - key = attrs && attrs[key] || (this.keyForAttribute ? this.keyForAttribute(key) : key); + ```js + this.pathForType("famousPerson"); + //=> "famous_people" + ``` - json[key] = value; + @method pathForType + @param {String} type + @return String + */ + pathForType: function(type) { + var decamelized = activemodel$adapter$lib$system$active_model_adapter$$decamelize(type); + var underscored = activemodel$adapter$lib$system$active_model_adapter$$underscore(decamelized); + return ember$inflector$lib$system$string$$pluralize(underscored); }, /** - `serializeBelongsTo` can be used to customize how `DS.belongsTo` - properties are serialized. - - Example - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - serializeBelongsTo: function(record, json, relationship) { - var key = relationship.key; - - var belongsTo = get(record, key); + The ActiveModelAdapter overrides the `ajaxError` method + to return a DS.InvalidError for all 422 Unprocessable Entity + responses. - key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key; + A 422 HTTP response from the server generally implies that the request + was well formed but the API was unable to process it because the + content was not semantically correct or meaningful per the API. - json[key] = Ember.isNone(belongsTo) ? belongsTo : belongsTo.toJSON(); - } - }); - ``` + For more information on 422 HTTP Error code see 11.2 WebDAV RFC 4918 + https://tools.ietf.org/html/rfc4918#section-11.2 - @method serializeBelongsTo - @param {DS.Model} record - @param {Object} json - @param {Object} relationship + @method ajaxError + @param {Object} jqXHR + @return error */ - serializeBelongsTo: function(record, json, relationship) { - var key = relationship.key; - - var belongsTo = get(record, key); - - key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key; + ajaxError: function(jqXHR) { + var error = this._super.apply(this, arguments); - if (isNone(belongsTo)) { - json[key] = belongsTo; + if (jqXHR && jqXHR.status === 422) { + return new ember$data$lib$system$adapter$$InvalidError(Ember.$.parseJSON(jqXHR.responseText)); } else { - json[key] = get(belongsTo, 'id'); + return error; } + } + }); - if (relationship.options.polymorphic) { - this.serializePolymorphicType(record, json, relationship); - } - }, + var activemodel$adapter$lib$system$active_model_adapter$$default = activemodel$adapter$lib$system$active_model_adapter$$ActiveModelAdapter; + var ember$data$lib$serializers$json_serializer$$get = Ember.get; + var ember$data$lib$serializers$json_serializer$$isNone = Ember.isNone; + var ember$data$lib$serializers$json_serializer$$map = Ember.ArrayPolyfills.map; + var ember$data$lib$serializers$json_serializer$$merge = Ember.merge; + var ember$data$lib$serializers$json_serializer$$default = Ember.Object.extend({ /** - `serializeHasMany` can be used to customize how `DS.hasMany` - properties are serialized. + The primaryKey is used when serializing and deserializing + data. Ember Data always uses the `id` property to store the id of + the record. The external source may not always follow this + convention. In these cases it is useful to override the + primaryKey property to match the primaryKey of your external + store. - Example + Example - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - serializeHasMany: function(record, json, relationship) { - var key = relationship.key; - if (key === 'comments') { - return; - } else { - this._super.apply(this, arguments); - } - } - }); - ``` + ```javascript + App.ApplicationSerializer = DS.JSONSerializer.extend({ + primaryKey: '_id' + }); + ``` - @method serializeHasMany - @param {DS.Model} record - @param {Object} json - @param {Object} relationship + @property primaryKey + @type {String} + @default 'id' */ - serializeHasMany: function(record, json, relationship) { - var key = relationship.key; - - var relationshipType = DS.RelationshipChange.determineRelationshipType(record.constructor, relationship); - - if (relationshipType === 'manyToNone' || relationshipType === 'manyToMany') { - json[key] = get(record, key).mapBy('id'); - // TODO support for polymorphic manyToNone and manyToMany relationships - } - }, + primaryKey: 'id', /** - You can use this method to customize how polymorphic objects are - serialized. Objects are considered to be polymorphic if - `{polymorphic: true}` is pass as the second argument to the - `DS.belongsTo` function. + The `attrs` object can be used to declare a simple mapping between + property names on `DS.Model` records and payload keys in the + serialized JSON object representing the record. An object with the + property `key` can also be used to designate the attribute's key on + the response payload. Example ```javascript - App.CommentSerializer = DS.JSONSerializer.extend({ - serializePolymorphicType: function(record, json, relationship) { - var key = relationship.key, - belongsTo = get(record, key); - key = this.keyForAttribute ? this.keyForAttribute(key) : key; - json[key + "_type"] = belongsTo.constructor.typeKey; - } + App.Person = DS.Model.extend({ + firstName: DS.attr('string'), + lastName: DS.attr('string'), + occupation: DS.attr('string'), + admin: DS.attr('boolean') }); - ``` - - @method serializePolymorphicType - @param {DS.Model} record - @param {Object} json - @param {Object} relationship - */ - serializePolymorphicType: Ember.K, - - // EXTRACT - /** - The `extract` method is used to deserialize payload data from the - server. By default the `JSONSerializer` does not push the records - into the store. However records that subclass `JSONSerializer` - such as the `RESTSerializer` may push records into the store as - part of the extract call. + App.PersonSerializer = DS.JSONSerializer.extend({ + attrs: { + admin: 'is_admin', + occupation: {key: 'career'} + } + }); + ``` - This method delegates to a more specific extract method based on - the `requestType`. + You can also remove attributes by setting the `serialize` key to + false in your mapping object. Example ```javascript - var get = Ember.get; - socket.on('message', function(message) { - var modelName = message.model; - var data = message.data; - var type = store.modelFor(modelName); - var serializer = store.serializerFor(type.typeKey); - var record = serializer.extract(store, type, data, get(data, 'id'), 'single'); - store.push(modelName, record); + App.PersonSerializer = DS.JSONSerializer.extend({ + attrs: { + admin: {serialize: false}, + occupation: {key: 'career'} + } }); ``` - @method extract - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @param {String or Number} id - @param {String} requestType - @return {Object} json The deserialized payload - */ - extract: function(store, type, payload, id, requestType) { - this.extractMeta(store, type, payload); + When serialized: - var specificExtract = "extract" + requestType.charAt(0).toUpperCase() + requestType.substr(1); - return this[specificExtract](store, type, payload, id, requestType); - }, + ```javascript + { + "career": "magician" + } + ``` - /** - `extractFindAll` is a hook into the extract method used when a - call is made to `DS.Store#findAll`. By default this method is an - alias for [extractArray](#method_extractArray). + Note that the `admin` is now not included in the payload. - @method extractFindAll - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Array} array An array of deserialized objects + @property attrs + @type {Object} */ - extractFindAll: function(store, type, payload){ - return this.extractArray(store, type, payload); - }, - /** - `extractFindQuery` is a hook into the extract method used when a - call is made to `DS.Store#findQuery`. By default this method is an - alias for [extractArray](#method_extractArray). - @method extractFindQuery - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Array} array An array of deserialized objects - */ - extractFindQuery: function(store, type, payload){ - return this.extractArray(store, type, payload); - }, /** - `extractFindMany` is a hook into the extract method used when a - call is made to `DS.Store#findMany`. By default this method is - alias for [extractArray](#method_extractArray). + Given a subclass of `DS.Model` and a JSON object this method will + iterate through each attribute of the `DS.Model` and invoke the + `DS.Transform#deserialize` method on the matching property of the + JSON object. This method is typically called after the + serializer's `normalize` method. - @method extractFindMany - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Array} array An array of deserialized objects + @method applyTransforms + @private + @param {subclass of DS.Model} type + @param {Object} data The data to transform + @return {Object} data The transformed data object */ - extractFindMany: function(store, type, payload){ - return this.extractArray(store, type, payload); - }, - /** - `extractFindHasMany` is a hook into the extract method used when a - call is made to `DS.Store#findHasMany`. By default this method is - alias for [extractArray](#method_extractArray). + applyTransforms: function(type, data) { + type.eachTransformedAttribute(function applyTransform(key, type) { + if (!data.hasOwnProperty(key)) { return; } - @method extractFindHasMany - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Array} array An array of deserialized objects - */ - extractFindHasMany: function(store, type, payload){ - return this.extractArray(store, type, payload); + var transform = this.transformFor(type); + data[key] = transform.deserialize(data[key]); + }, this); + + return data; }, /** - `extractCreateRecord` is a hook into the extract method used when a - call is made to `DS.Store#createRecord`. By default this method is - alias for [extractSave](#method_extractSave). + Normalizes a part of the JSON payload returned by + the server. You should override this method, munge the hash + and call super if you have generic normalization to do. - @method extractCreateRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Object} json The deserialized payload - */ - extractCreateRecord: function(store, type, payload) { - return this.extractSave(store, type, payload); - }, - /** - `extractUpdateRecord` is a hook into the extract method used when - a call is made to `DS.Store#update`. By default this method is alias - for [extractSave](#method_extractSave). + It takes the type of the record that is being normalized + (as a DS.Model class), the property where the hash was + originally found, and the hash to normalize. - @method extractUpdateRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Object} json The deserialized payload - */ - extractUpdateRecord: function(store, type, payload) { - return this.extractSave(store, type, payload); - }, - /** - `extractDeleteRecord` is a hook into the extract method used when - a call is made to `DS.Store#deleteRecord`. By default this method is - alias for [extractSave](#method_extractSave). + You can use this method, for example, to normalize underscored keys to camelized + or other general-purpose normalizations. - @method extractDeleteRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Object} json The deserialized payload - */ - extractDeleteRecord: function(store, type, payload) { - return this.extractSave(store, type, payload); - }, + Example - /** - `extractFind` is a hook into the extract method used when - a call is made to `DS.Store#find`. By default this method is - alias for [extractSingle](#method_extractSingle). + ```javascript + App.ApplicationSerializer = DS.JSONSerializer.extend({ + normalize: function(type, hash) { + var fields = Ember.get(type, 'fields'); + fields.forEach(function(field) { + var payloadField = Ember.String.underscore(field); + if (field === payloadField) { return; } - @method extractFind - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Object} json The deserialized payload - */ - extractFind: function(store, type, payload) { - return this.extractSingle(store, type, payload); - }, - /** - `extractFindBelongsTo` is a hook into the extract method used when - a call is made to `DS.Store#findBelongsTo`. By default this method is - alias for [extractSingle](#method_extractSingle). + hash[field] = hash[payloadField]; + delete hash[payloadField]; + }); + return this._super.apply(this, arguments); + } + }); + ``` - @method extractFindBelongsTo - @param {DS.Store} store + @method normalize @param {subclass of DS.Model} type - @param {Object} payload - @return {Object} json The deserialized payload + @param {Object} hash + @return {Object} */ - extractFindBelongsTo: function(store, type, payload) { - return this.extractSingle(store, type, payload); - }, - /** - `extractSave` is a hook into the extract method used when a call - is made to `DS.Model#save`. By default this method is alias - for [extractSingle](#method_extractSingle). + normalize: function(type, hash) { + if (!hash) { return hash; } - @method extractSave - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Object} json The deserialized payload - */ - extractSave: function(store, type, payload) { - return this.extractSingle(store, type, payload); + this.normalizeId(hash); + this.normalizeAttributes(type, hash); + this.normalizeRelationships(type, hash); + + this.normalizeUsingDeclaredMapping(type, hash); + this.applyTransforms(type, hash); + return hash; }, /** - `extractSingle` is used to deserialize a single record returned - from the adapter. - - Example + You can use this method to normalize all payloads, regardless of whether they + represent single records or an array. - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - extractSingle: function(store, type, payload) { - payload.comments = payload._embedded.comment; - delete payload._embedded; + For example, you might want to remove some extraneous data from the payload: - return this._super(store, type, payload); - }, + ```js + App.ApplicationSerializer = DS.JSONSerializer.extend({ + normalizePayload: function(payload) { + delete payload.version; + delete payload.status; + return payload; + } }); ``` - @method extractSingle - @param {DS.Store} store - @param {subclass of DS.Model} type + @method normalizePayload @param {Object} payload - @return {Object} json The deserialized payload + @return {Object} the normalized payload */ - extractSingle: function(store, type, payload) { - return this.normalize(type, payload); + normalizePayload: function(payload) { + return payload; }, /** - `extractArray` is used to deserialize an array of records - returned from the adapter. + @method normalizeAttributes + @private + */ + normalizeAttributes: function(type, hash) { + var payloadKey; - Example + if (this.keyForAttribute) { + type.eachAttribute(function(key) { + payloadKey = this.keyForAttribute(key); + if (key === payloadKey) { return; } + if (!hash.hasOwnProperty(payloadKey)) { return; } - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - extractArray: function(store, type, payload) { - return payload.map(function(json) { - return this.extractSingle(store, type, json); - }, this); - } - }); - ``` + hash[key] = hash[payloadKey]; + delete hash[payloadKey]; + }, this); + } + }, - @method extractArray - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Array} array An array of deserialized objects + /** + @method normalizeRelationships + @private */ - extractArray: function(store, type, payload) { - return this.normalize(type, payload); + normalizeRelationships: function(type, hash) { + var payloadKey; + + if (this.keyForRelationship) { + type.eachRelationship(function(key, relationship) { + payloadKey = this.keyForRelationship(key, relationship.kind); + if (key === payloadKey) { return; } + if (!hash.hasOwnProperty(payloadKey)) { return; } + + hash[key] = hash[payloadKey]; + delete hash[payloadKey]; + }, this); + } }, /** - `extractMeta` is used to deserialize any meta information in the - adapter payload. By default Ember Data expects meta information to - be located on the `meta` property of the payload object. + @method normalizeUsingDeclaredMapping + @private + */ + normalizeUsingDeclaredMapping: function(type, hash) { + var attrs = ember$data$lib$serializers$json_serializer$$get(this, 'attrs'), payloadKey, key; - Example + if (attrs) { + for (key in attrs) { + payloadKey = this._getMappedKey(key); + if (!hash.hasOwnProperty(payloadKey)) { continue; } - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - extractMeta: function(store, type, payload) { - if (payload && payload._pagination) { - store.metaForType(type, payload._pagination); - delete payload._pagination; + if (payloadKey !== key) { + hash[key] = hash[payloadKey]; + delete hash[payloadKey]; } } - }); - ``` - - @method extractMeta - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - */ - extractMeta: function(store, type, payload) { - if (payload && payload.meta) { - store.metaForType(type, payload.meta); - delete payload.meta; } }, /** - `keyForAttribute` can be used to define rules for how to convert an - attribute name in your model to a key in your JSON. + @method normalizeId + @private + */ + normalizeId: function(hash) { + var primaryKey = ember$data$lib$serializers$json_serializer$$get(this, 'primaryKey'); - Example + if (primaryKey === 'id') { return; } - ```javascript - App.ApplicationSerializer = DS.RESTSerializer.extend({ - keyForAttribute: function(attr) { - return Ember.String.underscore(attr).toUpperCase(); - } - }); - ``` + hash.id = hash[primaryKey]; + delete hash[primaryKey]; + }, - @method keyForAttribute - @param {String} key - @return {String} normalized key + /** + @method normalizeErrors + @private */ - + normalizeErrors: function(type, hash) { + this.normalizeId(hash); + this.normalizeAttributes(type, hash); + this.normalizeRelationships(type, hash); + }, /** - `keyForRelationship` can be used to define a custom key when - serializing relationship properties. By default `JSONSerializer` - does not provide an implementation of this method. + Looks up the property key that was set by the custom `attr` mapping + passed to the serializer. - Example - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - keyForRelationship: function(key, relationship) { - return 'rel_' + Ember.String.underscore(key); + @method _getMappedKey + @private + @param {String} key + @return {String} key + */ + _getMappedKey: function(key) { + var attrs = ember$data$lib$serializers$json_serializer$$get(this, 'attrs'); + var mappedKey; + if (attrs && attrs[key]) { + mappedKey = attrs[key]; + //We need to account for both the {title: 'post_title'} and + //{title: {key: 'post_title'}} forms + if (mappedKey.key){ + mappedKey = mappedKey.key; } - }); - ``` - - @method keyForRelationship - @param {String} key - @param {String} relationship type - @return {String} normalized key - */ + if (typeof mappedKey === 'string'){ + key = mappedKey; + } + } - // HELPERS + return key; + }, /** - @method transformFor - @private - @param {String} attributeType - @param {Boolean} skipAssertion - @return {DS.Transform} transform + Check attrs.key.serialize property to inform if the `key` + can be serialized + + @method _canSerialize + @private + @param {String} key + @return {boolean} true if the key can be serialized */ - transformFor: function(attributeType, skipAssertion) { - var transform = this.container.lookup('transform:' + attributeType); - Ember.assert("Unable to find transform for '" + attributeType + "'", skipAssertion || !!transform); - return transform; - } - }); + _canSerialize: function(key) { + var attrs = ember$data$lib$serializers$json_serializer$$get(this, 'attrs'); - __exports__["default"] = JSONSerializer; - }); -define("ember-data/lib/serializers/rest_serializer", - ["./json_serializer","exports"], - function(__dependency1__, __exports__) { - "use strict"; - /** - @module ember-data - */ + return !attrs || !attrs[key] || attrs[key].serialize !== false; + }, - var JSONSerializer = __dependency1__["default"]; - var get = Ember.get, set = Ember.set; - var forEach = Ember.ArrayPolyfills.forEach; - var map = Ember.ArrayPolyfills.map; + // SERIALIZE + /** + Called when a record is saved in order to convert the + record into JSON. - function coerceId(id) { - return id == null ? null : id+''; - } + By default, it creates a JSON object with a key for + each attribute and belongsTo relationship. - /** - Normally, applications will use the `RESTSerializer` by implementing - the `normalize` method and individual normalizations under - `normalizeHash`. + For example, consider this model: - This allows you to do whatever kind of munging you need, and is - especially useful if your server is inconsistent and you need to - do munging differently for many different kinds of responses. + ```javascript + App.Comment = DS.Model.extend({ + title: DS.attr(), + body: DS.attr(), - See the `normalize` documentation for more information. + author: DS.belongsTo('user') + }); + ``` - ## Across the Board Normalization + The default serialization would create a JSON object like: - There are also a number of hooks that you might find useful to defined - across-the-board rules for your payload. These rules will be useful - if your server is consistent, or if you're building an adapter for - an infrastructure service, like Parse, and want to encode service - conventions. + ```javascript + { + "title": "Rails is unagi", + "body": "Rails? Omakase? O_O", + "author": 12 + } + ``` - For example, if all of your keys are underscored and all-caps, but - otherwise consistent with the names you use in your models, you - can implement across-the-board rules for how to convert an attribute - name in your model to a key in your JSON. + By default, attributes are passed through as-is, unless + you specified an attribute type (`DS.attr('date')`). If + you specify a transform, the JavaScript value will be + serialized when inserted into the JSON hash. - ```js - App.ApplicationSerializer = DS.RESTSerializer.extend({ - keyForAttribute: function(attr) { - return Ember.String.underscore(attr).toUpperCase(); - } - }); - ``` + By default, belongs-to relationships are converted into + IDs when inserted into the JSON hash. - You can also implement `keyForRelationship`, which takes the name - of the relationship as the first parameter, and the kind of - relationship (`hasMany` or `belongsTo`) as the second parameter. + ## IDs - @class RESTSerializer - @namespace DS - @extends DS.JSONSerializer - */ - var RESTSerializer = JSONSerializer.extend({ - /** - If you want to do normalizations specific to some part of the payload, you - can specify those under `normalizeHash`. + `serialize` takes an options hash with a single option: + `includeId`. If this option is `true`, `serialize` will, + by default include the ID in the JSON object it builds. - For example, given the following json where the the `IDs` under - `"comments"` are provided as `_id` instead of `id`. + The adapter passes in `includeId: true` when serializing + a record for `createRecord`, but not for `updateRecord`. - ```javascript - { - "post": { - "id": 1, - "title": "Rails is omakase", - "comments": [ 1, 2 ] - }, - "comments": [{ - "_id": 1, - "body": "FIRST" - }, { - "_id": 2, - "body": "Rails is unagi" - }] - } - ``` + ## Customization - You use `normalizeHash` to normalize just the comments: + Your server may expect a different JSON format than the + built-in serialization format. + + In that case, you can implement `serialize` yourself and + return a JSON hash of your choosing. ```javascript - App.PostSerializer = DS.RESTSerializer.extend({ - normalizeHash: { - comments: function(hash) { - hash.id = hash._id; - delete hash._id; - return hash; + App.PostSerializer = DS.JSONSerializer.extend({ + serialize: function(post, options) { + var json = { + POST_TTL: post.get('title'), + POST_BDY: post.get('body'), + POST_CMS: post.get('comments').mapBy('id') + } + + if (options.includeId) { + json.POST_ID_ = post.get('id'); } + + return json; } }); ``` - The key under `normalizeHash` is usually just the original key - that was in the original payload. However, key names will be - impacted by any modifications done in the `normalizePayload` - method. The `DS.RESTSerializer`'s default implementation makes no - changes to the payload keys. + ## Customizing an App-Wide Serializer - @property normalizeHash - @type {Object} - @default undefined - */ + If you want to define a serializer for your entire + application, you'll probably want to use `eachAttribute` + and `eachRelationship` on the record. - /** - Normalizes a part of the JSON payload returned by - the server. You should override this method, munge the hash - and call super if you have generic normalization to do. + ```javascript + App.ApplicationSerializer = DS.JSONSerializer.extend({ + serialize: function(record, options) { + var json = {}; - It takes the type of the record that is being normalized - (as a DS.Model class), the property where the hash was - originally found, and the hash to normalize. + record.eachAttribute(function(name) { + json[serverAttributeName(name)] = record.get(name); + }) - For example, if you have a payload that looks like this: + record.eachRelationship(function(name, relationship) { + if (relationship.kind === 'hasMany') { + json[serverHasManyName(name)] = record.get(name).mapBy('id'); + } + }); - ```js - { - "post": { - "id": 1, - "title": "Rails is omakase", - "comments": [ 1, 2 ] - }, - "comments": [{ - "id": 1, - "body": "FIRST" - }, { - "id": 2, - "body": "Rails is unagi" - }] + if (options.includeId) { + json.ID_ = record.get('id'); + } + + return json; + } + }); + + function serverAttributeName(attribute) { + return attribute.underscore().toUpperCase(); + } + + function serverHasManyName(name) { + return serverAttributeName(name.singularize()) + "_IDS"; } ``` - The `normalize` method will be called three times: + This serializer will generate JSON that looks like this: - * With `App.Post`, `"posts"` and `{ id: 1, title: "Rails is omakase", ... }` - * With `App.Comment`, `"comments"` and `{ id: 1, body: "FIRST" }` - * With `App.Comment`, `"comments"` and `{ id: 2, body: "Rails is unagi" }` + ```javascript + { + "TITLE": "Rails is omakase", + "BODY": "Yep. Omakase.", + "COMMENT_IDS": [ 1, 2, 3 ] + } + ``` - You can use this method, for example, to normalize underscored keys to camelized - or other general-purpose normalizations. + ## Tweaking the Default JSON - If you want to do normalizations specific to some part of the payload, you - can specify those under `normalizeHash`. + If you just want to do some small tweaks on the default JSON, + you can call super first and make the tweaks on the returned + JSON. - For example, if the `IDs` under `"comments"` are provided as `_id` instead of - `id`, you can specify how to normalize just the comments: + ```javascript + App.PostSerializer = DS.JSONSerializer.extend({ + serialize: function(record, options) { + var json = this._super.apply(this, arguments); - ```js - App.PostSerializer = DS.RESTSerializer.extend({ - normalizeHash: { - comments: function(hash) { - hash.id = hash._id; - delete hash._id; - return hash; - } + json.subject = json.title; + delete json.title; + + return json; } }); ``` - The key under `normalizeHash` is just the original key that was in the original - payload. - - @method normalize - @param {subclass of DS.Model} type - @param {Object} hash - @param {String} prop - @returns {Object} + @method serialize + @param {subclass of DS.Model} record + @param {Object} options + @return {Object} json */ - normalize: function(type, hash, prop) { - this.normalizeId(hash); - this.normalizeAttributes(type, hash); - this.normalizeRelationships(type, hash); + serialize: function(record, options) { + var json = {}; - this.normalizeUsingDeclaredMapping(type, hash); + if (options && options.includeId) { + var id = ember$data$lib$serializers$json_serializer$$get(record, 'id'); - if (this.normalizeHash && this.normalizeHash[prop]) { - this.normalizeHash[prop](hash); + if (id) { + json[ember$data$lib$serializers$json_serializer$$get(this, 'primaryKey')] = id; + } } - return this._super(type, hash, prop); + record.eachAttribute(function(key, attribute) { + this.serializeAttribute(record, json, key, attribute); + }, this); + + record.eachRelationship(function(key, relationship) { + if (relationship.kind === 'belongsTo') { + this.serializeBelongsTo(record, json, relationship); + } else if (relationship.kind === 'hasMany') { + this.serializeHasMany(record, json, relationship); + } + }, this); + + return json; }, /** - You can use this method to normalize all payloads, regardless of whether they - represent single records or an array. + You can use this method to customize how a serialized record is added to the complete + JSON hash to be sent to the server. By default the JSON Serializer does not namespace + the payload and just sends the raw serialized JSON object. + If your server expects namespaced keys, you should consider using the RESTSerializer. + Otherwise you can override this method to customize how the record is added to the hash. - For example, you might want to remove some extraneous data from the payload: + For example, your server may expect underscored root objects. ```js App.ApplicationSerializer = DS.RESTSerializer.extend({ - normalizePayload: function(type, payload) { - delete payload.version; - delete payload.status; - return payload; + serializeIntoHash: function(data, type, record, options) { + var root = Ember.String.decamelize(type.typeKey); + data[root] = this.serialize(record, options); } }); ``` - @method normalizePayload - @param {subclass of DS.Model} type + @method serializeIntoHash @param {Object} hash - @returns {Object} the normalized payload + @param {subclass of DS.Model} type + @param {DS.Model} record + @param {Object} options */ - normalizePayload: function(type, payload) { - return payload; + serializeIntoHash: function(hash, type, record, options) { + ember$data$lib$serializers$json_serializer$$merge(hash, this.serialize(record, options)); }, /** - @method normalizeId - @private - */ - normalizeId: function(hash) { - var primaryKey = get(this, 'primaryKey'); + `serializeAttribute` can be used to customize how `DS.attr` + properties are serialized - if (primaryKey === 'id') { return; } + For example if you wanted to ensure all your attributes were always + serialized as properties on an `attributes` object you could + write: - hash.id = hash[primaryKey]; - delete hash[primaryKey]; - }, + ```javascript + App.ApplicationSerializer = DS.JSONSerializer.extend({ + serializeAttribute: function(record, json, key, attributes) { + json.attributes = json.attributes || {}; + this._super(record, json.attributes, key, attributes); + } + }); + ``` - /** - @method normalizeUsingDeclaredMapping - @private + @method serializeAttribute + @param {DS.Model} record + @param {Object} json + @param {String} key + @param {Object} attribute */ - normalizeUsingDeclaredMapping: function(type, hash) { - var attrs = get(this, 'attrs'), payloadKey, key; + serializeAttribute: function(record, json, key, attribute) { + var type = attribute.type; - if (attrs) { - for (key in attrs) { - payloadKey = attrs[key]; - if (payloadKey && payloadKey.key) { - payloadKey = payloadKey.key; - } - if (typeof payloadKey === 'string') { - hash[key] = hash[payloadKey]; - delete hash[payloadKey]; - } + if (this._canSerialize(key)) { + var value = ember$data$lib$serializers$json_serializer$$get(record, key); + if (type) { + var transform = this.transformFor(type); + value = transform.serialize(value); } - } - }, - /** - @method normalizeAttributes - @private - */ - normalizeAttributes: function(type, hash) { - var payloadKey, key; + // if provided, use the mapping provided by `attrs` in + // the serializer + var payloadKey = this._getMappedKey(key); - if (this.keyForAttribute) { - type.eachAttribute(function(key) { + if (payloadKey === key && this.keyForAttribute) { payloadKey = this.keyForAttribute(key); - if (key === payloadKey) { return; } + } - hash[key] = hash[payloadKey]; - delete hash[payloadKey]; - }, this); + json[payloadKey] = value; } }, /** - @method normalizeRelationships - @private - */ - normalizeRelationships: function(type, hash) { - var payloadKey, key; + `serializeBelongsTo` can be used to customize how `DS.belongsTo` + properties are serialized. - if (this.keyForRelationship) { - type.eachRelationship(function(key, relationship) { - payloadKey = this.keyForRelationship(key, relationship.kind); - if (key === payloadKey) { return; } + Example - hash[key] = hash[payloadKey]; - delete hash[payloadKey]; - }, this); - } - }, + ```javascript + App.PostSerializer = DS.JSONSerializer.extend({ + serializeBelongsTo: function(record, json, relationship) { + var key = relationship.key; - /** - Called when the server has returned a payload representing - a single record, such as in response to a `find` or `save`. + var belongsTo = get(record, key); - It is your opportunity to clean up the server's response into the normalized - form expected by Ember Data. + key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key; - If you want, you can just restructure the top-level of your payload, and - do more fine-grained normalization in the `normalize` method. + json[key] = Ember.isNone(belongsTo) ? belongsTo : belongsTo.toJSON(); + } + }); + ``` - For example, if you have a payload like this in response to a request for - post 1: + @method serializeBelongsTo + @param {DS.Model} record + @param {Object} json + @param {Object} relationship + */ + serializeBelongsTo: function(record, json, relationship) { + var key = relationship.key; - ```js - { - "id": 1, - "title": "Rails is omakase", + if (this._canSerialize(key)) { + var belongsTo = ember$data$lib$serializers$json_serializer$$get(record, key); - "_embedded": { - "comment": [{ - "_id": 1, - "comment_title": "FIRST" - }, { - "_id": 2, - "comment_title": "Rails is unagi" - }] + // if provided, use the mapping provided by `attrs` in + // the serializer + var payloadKey = this._getMappedKey(key); + if (payloadKey === key && this.keyForRelationship) { + payloadKey = this.keyForRelationship(key, "belongsTo"); } - } - ``` - - You could implement a serializer that looks like this to get your payload - into shape: - - ```js - App.PostSerializer = DS.RESTSerializer.extend({ - // First, restructure the top-level so it's organized by type - extractSingle: function(store, type, payload, id, requestType) { - var comments = payload._embedded.comment; - delete payload._embedded; - payload = { comments: comments, post: payload }; - return this._super(store, type, payload, id, requestType); - }, + //Need to check whether the id is there for new&async records + if (ember$data$lib$serializers$json_serializer$$isNone(belongsTo) || ember$data$lib$serializers$json_serializer$$isNone(ember$data$lib$serializers$json_serializer$$get(belongsTo, 'id'))) { + json[payloadKey] = null; + } else { + json[payloadKey] = ember$data$lib$serializers$json_serializer$$get(belongsTo, 'id'); + } - normalizeHash: { - // Next, normalize individual comments, which (after `extract`) - // are now located under `comments` - comments: function(hash) { - hash.id = hash._id; - hash.title = hash.comment_title; - delete hash._id; - delete hash.comment_title; - return hash; - } + if (relationship.options.polymorphic) { + this.serializePolymorphicType(record, json, relationship); } - }) - ``` + } + }, - When you call super from your own implementation of `extractSingle`, the - built-in implementation will find the primary record in your normalized - payload and push the remaining records into the store. + /** + `serializeHasMany` can be used to customize how `DS.hasMany` + properties are serialized. - The primary record is the single hash found under `post` or the first - element of the `posts` array. + Example - The primary record has special meaning when the record is being created - for the first time or updated (`createRecord` or `updateRecord`). In - particular, it will update the properties of the record that was saved. + ```javascript + App.PostSerializer = DS.JSONSerializer.extend({ + serializeHasMany: function(record, json, relationship) { + var key = relationship.key; + if (key === 'comments') { + return; + } else { + this._super.apply(this, arguments); + } + } + }); + ``` - @method extractSingle - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @param {String} id - @param {'find'|'createRecord'|'updateRecord'|'deleteRecord'} requestType - @returns {Object} the primary response to the original request + @method serializeHasMany + @param {DS.Model} record + @param {Object} json + @param {Object} relationship */ - extractSingle: function(store, primaryType, payload, recordId, requestType) { - payload = this.normalizePayload(primaryType, payload); - - var primaryTypeName = primaryType.typeKey, - primaryRecord; + serializeHasMany: function(record, json, relationship) { + var key = relationship.key; - for (var prop in payload) { - var typeName = this.typeForRoot(prop), - type = store.modelFor(typeName), - isPrimary = type.typeKey === primaryTypeName; + if (this._canSerialize(key)) { + var payloadKey; - // legacy support for singular resources - if (isPrimary && Ember.typeOf(payload[prop]) !== "array" ) { - primaryRecord = this.normalize(primaryType, payload[prop], prop); - continue; + // if provided, use the mapping provided by `attrs` in + // the serializer + payloadKey = this._getMappedKey(key); + if (payloadKey === key && this.keyForRelationship) { + payloadKey = this.keyForRelationship(key, "hasMany"); } - /*jshint loopfunc:true*/ - forEach.call(payload[prop], function(hash) { - var typeName = this.typeForRoot(prop), - type = store.modelFor(typeName), - typeSerializer = store.serializerFor(type); - - hash = typeSerializer.normalize(type, hash, prop); - - var isFirstCreatedRecord = isPrimary && !recordId && !primaryRecord, - isUpdatedRecord = isPrimary && coerceId(hash.id) === recordId; + var relationshipType = record.constructor.determineRelationshipType(relationship); - // find the primary record. - // - // It's either: - // * the record with the same ID as the original request - // * in the case of a newly created record that didn't have an ID, the first - // record in the Array - if (isFirstCreatedRecord || isUpdatedRecord) { - primaryRecord = hash; - } else { - store.push(typeName, hash); - } - }, this); + if (relationshipType === 'manyToNone' || relationshipType === 'manyToMany') { + json[payloadKey] = ember$data$lib$serializers$json_serializer$$get(record, key).mapBy('id'); + // TODO support for polymorphic manyToNone and manyToMany relationships + } } - - return primaryRecord; }, /** - Called when the server has returned a payload representing - multiple records, such as in response to a `findAll` or `findQuery`. - - It is your opportunity to clean up the server's response into the normalized - form expected by Ember Data. - - If you want, you can just restructure the top-level of your payload, and - do more fine-grained normalization in the `normalize` method. + You can use this method to customize how polymorphic objects are + serialized. Objects are considered to be polymorphic if + `{polymorphic: true}` is pass as the second argument to the + `DS.belongsTo` function. - For example, if you have a payload like this in response to a request for - all posts: + Example - ```js - { - "_embedded": { - "post": [{ - "id": 1, - "title": "Rails is omakase" - }, { - "id": 2, - "title": "The Parley Letter" - }], - "comment": [{ - "_id": 1, - "comment_title": "Rails is unagi" - "post_id": 1 - }, { - "_id": 2, - "comment_title": "Don't tread on me", - "post_id": 2 - }] - } - } - ``` - - You could implement a serializer that looks like this to get your payload - into shape: - - ```js - App.PostSerializer = DS.RESTSerializer.extend({ - // First, restructure the top-level so it's organized by type - // and the comments are listed under a post's `comments` key. - extractArray: function(store, type, payload, id, requestType) { - var posts = payload._embedded.post; - var comments = []; - var postCache = {}; - - posts.forEach(function(post) { - post.comments = []; - postCache[post.id] = post; - }); + ```javascript + App.CommentSerializer = DS.JSONSerializer.extend({ + serializePolymorphicType: function(record, json, relationship) { + var key = relationship.key, + belongsTo = get(record, key); + key = this.keyForAttribute ? this.keyForAttribute(key) : key; - payload._embedded.comment.forEach(function(comment) { - comments.push(comment); - postCache[comment.post_id].comments.push(comment); - delete comment.post_id; + if (Ember.isNone(belongsTo)) { + json[key + "_type"] = null; + } else { + json[key + "_type"] = belongsTo.constructor.typeKey; } + } + }); + ``` - payload = { comments: comments, posts: payload }; - - return this._super(store, type, payload, id, requestType); - }, + @method serializePolymorphicType + @param {DS.Model} record + @param {Object} json + @param {Object} relationship + */ + serializePolymorphicType: Ember.K, - normalizeHash: { - // Next, normalize individual comments, which (after `extract`) - // are now located under `comments` - comments: function(hash) { - hash.id = hash._id; - hash.title = hash.comment_title; - delete hash._id; - delete hash.comment_title; - return hash; - } - } - }) - ``` + // EXTRACT - When you call super from your own implementation of `extractArray`, the - built-in implementation will find the primary array in your normalized - payload and push the remaining records into the store. + /** + The `extract` method is used to deserialize payload data from the + server. By default the `JSONSerializer` does not push the records + into the store. However records that subclass `JSONSerializer` + such as the `RESTSerializer` may push records into the store as + part of the extract call. - The primary array is the array found under `posts`. + This method delegates to a more specific extract method based on + the `requestType`. - The primary record has special meaning when responding to `findQuery` - or `findHasMany`. In particular, the primary array will become the - list of records in the record array that kicked off the request. + Example - If your primary array contains secondary (embedded) records of the same type, - you cannot place these into the primary array `posts`. Instead, place the - secondary items into an underscore prefixed property `_posts`, which will - push these items into the store and will not affect the resulting query. + ```javascript + var get = Ember.get; + socket.on('message', function(message) { + var modelName = message.model; + var data = message.data; + var type = store.modelFor(modelName); + var serializer = store.serializerFor(type.typeKey); + var record = serializer.extract(store, type, data, get(data, 'id'), 'single'); + store.push(modelName, record); + }); + ``` - @method extractArray + @method extract @param {DS.Store} store @param {subclass of DS.Model} type @param {Object} payload - @param {'findAll'|'findMany'|'findHasMany'|'findQuery'} requestType - @returns {Array} The primary array that was returned in response - to the original query. + @param {String or Number} id + @param {String} requestType + @return {Object} json The deserialized payload */ - extractArray: function(store, primaryType, payload) { - payload = this.normalizePayload(primaryType, payload); - - var primaryTypeName = primaryType.typeKey, - primaryArray; - - for (var prop in payload) { - var typeKey = prop, - forcedSecondary = false; - - if (prop.charAt(0) === '_') { - forcedSecondary = true; - typeKey = prop.substr(1); - } - - var typeName = this.typeForRoot(typeKey), - type = store.modelFor(typeName), - typeSerializer = store.serializerFor(type), - isPrimary = (!forcedSecondary && (type.typeKey === primaryTypeName)); - - /*jshint loopfunc:true*/ - var normalizedArray = map.call(payload[prop], function(hash) { - return typeSerializer.normalize(type, hash, prop); - }, this); - - if (isPrimary) { - primaryArray = normalizedArray; - } else { - store.pushMany(typeName, normalizedArray); - } - } + extract: function(store, type, payload, id, requestType) { + this.extractMeta(store, type, payload); - return primaryArray; + var specificExtract = "extract" + requestType.charAt(0).toUpperCase() + requestType.substr(1); + return this[specificExtract](store, type, payload, id, requestType); }, /** - This method allows you to push a payload containing top-level - collections of records organized per type. - - ```js - { - "posts": [{ - "id": "1", - "title": "Rails is omakase", - "author", "1", - "comments": [ "1" ] - }], - "comments": [{ - "id": "1", - "body": "FIRST" - }], - "users": [{ - "id": "1", - "name": "@d2h" - }] - } - ``` - - It will first normalize the payload, so you can use this to push - in data streaming in from your server structured the same way - that fetches and saves are structured. + `extractFindAll` is a hook into the extract method used when a + call is made to `DS.Store#findAll`. By default this method is an + alias for [extractArray](#method_extractArray). - @method pushPayload + @method extractFindAll @param {DS.Store} store + @param {subclass of DS.Model} type @param {Object} payload + @param {String or Number} id + @param {String} requestType + @return {Array} array An array of deserialized objects */ - pushPayload: function(store, payload) { - payload = this.normalizePayload(null, payload); + extractFindAll: function(store, type, payload, id, requestType){ + return this.extractArray(store, type, payload, id, requestType); + }, + /** + `extractFindQuery` is a hook into the extract method used when a + call is made to `DS.Store#findQuery`. By default this method is an + alias for [extractArray](#method_extractArray). - for (var prop in payload) { - var typeName = this.typeForRoot(prop), - type = store.modelFor(typeName); + @method extractFindQuery + @param {DS.Store} store + @param {subclass of DS.Model} type + @param {Object} payload + @param {String or Number} id + @param {String} requestType + @return {Array} array An array of deserialized objects + */ + extractFindQuery: function(store, type, payload, id, requestType){ + return this.extractArray(store, type, payload, id, requestType); + }, + /** + `extractFindMany` is a hook into the extract method used when a + call is made to `DS.Store#findMany`. By default this method is + alias for [extractArray](#method_extractArray). - /*jshint loopfunc:true*/ - var normalizedArray = map.call(Ember.makeArray(payload[prop]), function(hash) { - return this.normalize(type, hash, prop); - }, this); + @method extractFindMany + @param {DS.Store} store + @param {subclass of DS.Model} type + @param {Object} payload + @param {String or Number} id + @param {String} requestType + @return {Array} array An array of deserialized objects + */ + extractFindMany: function(store, type, payload, id, requestType){ + return this.extractArray(store, type, payload, id, requestType); + }, + /** + `extractFindHasMany` is a hook into the extract method used when a + call is made to `DS.Store#findHasMany`. By default this method is + alias for [extractArray](#method_extractArray). - store.pushMany(typeName, normalizedArray); - } + @method extractFindHasMany + @param {DS.Store} store + @param {subclass of DS.Model} type + @param {Object} payload + @param {String or Number} id + @param {String} requestType + @return {Array} array An array of deserialized objects + */ + extractFindHasMany: function(store, type, payload, id, requestType){ + return this.extractArray(store, type, payload, id, requestType); }, /** - You can use this method to normalize the JSON root keys returned - into the model type expected by your store. - - For example, your server may return underscored root keys rather than - the expected camelcased versions. + `extractCreateRecord` is a hook into the extract method used when a + call is made to `DS.Model#save` and the record is new. By default + this method is alias for [extractSave](#method_extractSave). - ```js - App.ApplicationSerializer = DS.RESTSerializer.extend({ - typeForRoot: function(root) { - var camelized = Ember.String.camelize(root); - return Ember.String.singularize(camelized); - } - }); - ``` + @method extractCreateRecord + @param {DS.Store} store + @param {subclass of DS.Model} type + @param {Object} payload + @param {String or Number} id + @param {String} requestType + @return {Object} json The deserialized payload + */ + extractCreateRecord: function(store, type, payload, id, requestType) { + return this.extractSave(store, type, payload, id, requestType); + }, + /** + `extractUpdateRecord` is a hook into the extract method used when + a call is made to `DS.Model#save` and the record has been updated. + By default this method is alias for [extractSave](#method_extractSave). - @method typeForRoot - @param {String} root - @returns {String} the model's typeKey + @method extractUpdateRecord + @param {DS.Store} store + @param {subclass of DS.Model} type + @param {Object} payload + @param {String or Number} id + @param {String} requestType + @return {Object} json The deserialized payload */ - typeForRoot: function(root) { - return Ember.String.singularize(root); + extractUpdateRecord: function(store, type, payload, id, requestType) { + return this.extractSave(store, type, payload, id, requestType); }, + /** + `extractDeleteRecord` is a hook into the extract method used when + a call is made to `DS.Model#save` and the record has been deleted. + By default this method is alias for [extractSave](#method_extractSave). - // SERIALIZE + @method extractDeleteRecord + @param {DS.Store} store + @param {subclass of DS.Model} type + @param {Object} payload + @param {String or Number} id + @param {String} requestType + @return {Object} json The deserialized payload + */ + extractDeleteRecord: function(store, type, payload, id, requestType) { + return this.extractSave(store, type, payload, id, requestType); + }, /** - Called when a record is saved in order to convert the - record into JSON. - - By default, it creates a JSON object with a key for - each attribute and belongsTo relationship. - - For example, consider this model: - - ```js - App.Comment = DS.Model.extend({ - title: DS.attr(), - body: DS.attr(), - - author: DS.belongsTo('user') - }); - ``` - - The default serialization would create a JSON object like: - - ```js - { - "title": "Rails is unagi", - "body": "Rails? Omakase? O_O", - "author": 12 - } - ``` + `extractFind` is a hook into the extract method used when + a call is made to `DS.Store#find`. By default this method is + alias for [extractSingle](#method_extractSingle). - By default, attributes are passed through as-is, unless - you specified an attribute type (`DS.attr('date')`). If - you specify a transform, the JavaScript value will be - serialized when inserted into the JSON hash. + @method extractFind + @param {DS.Store} store + @param {subclass of DS.Model} type + @param {Object} payload + @param {String or Number} id + @param {String} requestType + @return {Object} json The deserialized payload + */ + extractFind: function(store, type, payload, id, requestType) { + return this.extractSingle(store, type, payload, id, requestType); + }, + /** + `extractFindBelongsTo` is a hook into the extract method used when + a call is made to `DS.Store#findBelongsTo`. By default this method is + alias for [extractSingle](#method_extractSingle). - By default, belongs-to relationships are converted into - IDs when inserted into the JSON hash. + @method extractFindBelongsTo + @param {DS.Store} store + @param {subclass of DS.Model} type + @param {Object} payload + @param {String or Number} id + @param {String} requestType + @return {Object} json The deserialized payload + */ + extractFindBelongsTo: function(store, type, payload, id, requestType) { + return this.extractSingle(store, type, payload, id, requestType); + }, + /** + `extractSave` is a hook into the extract method used when a call + is made to `DS.Model#save`. By default this method is alias + for [extractSingle](#method_extractSingle). - ## IDs + @method extractSave + @param {DS.Store} store + @param {subclass of DS.Model} type + @param {Object} payload + @param {String or Number} id + @param {String} requestType + @return {Object} json The deserialized payload + */ + extractSave: function(store, type, payload, id, requestType) { + return this.extractSingle(store, type, payload, id, requestType); + }, - `serialize` takes an options hash with a single option: - `includeId`. If this option is `true`, `serialize` will, - by default include the ID in the JSON object it builds. + /** + `extractSingle` is used to deserialize a single record returned + from the adapter. - The adapter passes in `includeId: true` when serializing - a record for `createRecord`, but not for `updateRecord`. + Example - ## Customization + ```javascript + App.PostSerializer = DS.JSONSerializer.extend({ + extractSingle: function(store, type, payload) { + payload.comments = payload._embedded.comment; + delete payload._embedded; - Your server may expect a different JSON format than the - built-in serialization format. + return this._super(store, type, payload); + }, + }); + ``` - In that case, you can implement `serialize` yourself and - return a JSON hash of your choosing. + @method extractSingle + @param {DS.Store} store + @param {subclass of DS.Model} type + @param {Object} payload + @param {String or Number} id + @param {String} requestType + @return {Object} json The deserialized payload + */ + extractSingle: function(store, type, payload, id, requestType) { + payload = this.normalizePayload(payload); + return this.normalize(type, payload); + }, - ```js - App.PostSerializer = DS.RESTSerializer.extend({ - serialize: function(post, options) { - var json = { - POST_TTL: post.get('title'), - POST_BDY: post.get('body'), - POST_CMS: post.get('comments').mapProperty('id') - } + /** + `extractArray` is used to deserialize an array of records + returned from the adapter. - if (options.includeId) { - json.POST_ID_ = post.get('id'); - } + Example - return json; + ```javascript + App.PostSerializer = DS.JSONSerializer.extend({ + extractArray: function(store, type, payload) { + return payload.map(function(json) { + return this.extractSingle(store, type, json); + }, this); } }); ``` - ## Customizing an App-Wide Serializer - - If you want to define a serializer for your entire - application, you'll probably want to use `eachAttribute` - and `eachRelationship` on the record. + @method extractArray + @param {DS.Store} store + @param {subclass of DS.Model} type + @param {Object} payload + @param {String or Number} id + @param {String} requestType + @return {Array} array An array of deserialized objects + */ + extractArray: function(store, type, arrayPayload, id, requestType) { + var normalizedPayload = this.normalizePayload(arrayPayload); + var serializer = this; - ```js - App.ApplicationSerializer = DS.RESTSerializer.extend({ - serialize: function(record, options) { - var json = {}; + return ember$data$lib$serializers$json_serializer$$map.call(normalizedPayload, function(singlePayload) { + return serializer.normalize(type, singlePayload); + }); + }, - record.eachAttribute(function(name) { - json[serverAttributeName(name)] = record.get(name); - }) + /** + `extractMeta` is used to deserialize any meta information in the + adapter payload. By default Ember Data expects meta information to + be located on the `meta` property of the payload object. - record.eachRelationship(function(name, relationship) { - if (relationship.kind === 'hasMany') { - json[serverHasManyName(name)] = record.get(name).mapBy('id'); - } - }); + Example - if (options.includeId) { - json.ID_ = record.get('id'); + ```javascript + App.PostSerializer = DS.JSONSerializer.extend({ + extractMeta: function(store, type, payload) { + if (payload && payload._pagination) { + store.setMetadataFor(type, payload._pagination); + delete payload._pagination; } - - return json; } }); - - function serverAttributeName(attribute) { - return attribute.underscore().toUpperCase(); - } - - function serverHasManyName(name) { - return serverAttributeName(name.singularize()) + "_IDS"; - } ``` - This serializer will generate JSON that looks like this: - - ```js - { - "TITLE": "Rails is omakase", - "BODY": "Yep. Omakase.", - "COMMENT_IDS": [ 1, 2, 3 ] + @method extractMeta + @param {DS.Store} store + @param {subclass of DS.Model} type + @param {Object} payload + */ + extractMeta: function(store, type, payload) { + if (payload && payload.meta) { + store.setMetadataFor(type, payload.meta); + delete payload.meta; } - ``` - - ## Tweaking the Default JSON - - If you just want to do some small tweaks on the default JSON, - you can call super first and make the tweaks on the returned - JSON. + }, - ```js - App.PostSerializer = DS.RESTSerializer.extend({ - serialize: function(record, options) { - var json = this._super(record, options); + /** + `extractErrors` is used to extract model errors when a call is made + to `DS.Model#save` which fails with an InvalidError`. By default + Ember Data expects error information to be located on the `errors` + property of the payload object. - json.subject = json.title; - delete json.title; + Example - return json; + ```javascript + App.PostSerializer = DS.JSONSerializer.extend({ + extractErrors: function(store, type, payload, id) { + if (payload && typeof payload === 'object' && payload._problems) { + payload = payload._problems; + this.normalizeErrors(type, payload); + } + return payload; } }); ``` - @method serialize - @param record - @param options + @method extractErrors + @param {DS.Store} store + @param {subclass of DS.Model} type + @param {Object} payload + @param {String or Number} id + @return {Object} json The deserialized errors */ - serialize: function(record, options) { - return this._super.apply(this, arguments); + extractErrors: function(store, type, payload, id) { + if (payload && typeof payload === 'object' && payload.errors) { + payload = payload.errors; + this.normalizeErrors(type, payload); + } + return payload; }, /** - You can use this method to customize the root keys serialized into the JSON. - By default the REST Serializer sends camelized root keys. - For example, your server may expect underscored root objects. + `keyForAttribute` can be used to define rules for how to convert an + attribute name in your model to a key in your JSON. - ```js - App.ApplicationSerializer = DS.RESTSerializer.extend({ - serializeIntoHash: function(data, type, record, options) { - var root = Ember.String.decamelize(type.typeKey); - data[root] = this.serialize(record, options); - } - }); - ``` + Example - @method serializeIntoHash - @param {Object} hash - @param {subclass of DS.Model} type - @param {DS.Model} record - @param {Object} options + ```javascript + App.ApplicationSerializer = DS.RESTSerializer.extend({ + keyForAttribute: function(attr) { + return Ember.String.underscore(attr).toUpperCase(); + } + }); + ``` + + @method keyForAttribute + @param {String} key + @return {String} normalized key */ - serializeIntoHash: function(hash, type, record, options) { - var root = Ember.String.camelize(type.typeKey); - hash[root] = this.serialize(record, options); + keyForAttribute: function(key){ + return key; }, /** - You can use this method to customize how polymorphic objects are serialized. - By default the JSON Serializer creates the key by appending `Type` to - the attribute and value from the model's camelcased model name. + `keyForRelationship` can be used to define a custom key when + serializing relationship properties. By default `JSONSerializer` + does not provide an implementation of this method. - @method serializePolymorphicType - @param {DS.Model} record - @param {Object} json - @param {Object} relationship + Example + + ```javascript + App.PostSerializer = DS.JSONSerializer.extend({ + keyForRelationship: function(key, relationship) { + return 'rel_' + Ember.String.underscore(key); + } + }); + ``` + + @method keyForRelationship + @param {String} key + @param {String} relationship type + @return {String} normalized key */ - serializePolymorphicType: function(record, json, relationship) { - var key = relationship.key, - belongsTo = get(record, key); - key = this.keyForAttribute ? this.keyForAttribute(key) : key; - json[key + "Type"] = Ember.String.camelize(belongsTo.constructor.typeKey); + + keyForRelationship: function(key, type){ + return key; + }, + + // HELPERS + + /** + @method transformFor + @private + @param {String} attributeType + @param {Boolean} skipAssertion + @return {DS.Transform} transform + */ + transformFor: function(attributeType, skipAssertion) { + var transform = this.container.lookup('transform:' + attributeType); + Ember.assert("Unable to find transform for '" + attributeType + "'", skipAssertion || !!transform); + return transform; } }); - __exports__["default"] = RESTSerializer; - }); -define("ember-data/lib/system/adapter", - ["exports"], - function(__exports__) { - "use strict"; + var ember$data$lib$serializers$rest_serializer$$get = Ember.get; + var ember$data$lib$serializers$rest_serializer$$forEach = Ember.ArrayPolyfills.forEach; + var ember$data$lib$serializers$rest_serializer$$map = Ember.ArrayPolyfills.map; + var ember$data$lib$serializers$rest_serializer$$camelize = Ember.String.camelize; + + function ember$data$lib$serializers$rest_serializer$$coerceId(id) { + return id == null ? null : id + ''; + } + /** - @module ember-data - */ + Normally, applications will use the `RESTSerializer` by implementing + the `normalize` method and individual normalizations under + `normalizeHash`. - var get = Ember.get, set = Ember.set; - var map = Ember.ArrayPolyfills.map; + This allows you to do whatever kind of munging you need, and is + especially useful if your server is inconsistent and you need to + do munging differently for many different kinds of responses. - var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; + See the `normalize` documentation for more information. - /** - A `DS.InvalidError` is used by an adapter to signal the external API - was unable to process a request because the content was not - semantically correct or meaningful per the API. Usually this means a - record failed some form of server side validation. When a promise - from an adapter is rejected with a `DS.InvalidError` the record will - transition to the `invalid` state and the errors will be set to the - `errors` property on the record. + ## Across the Board Normalization - Example + There are also a number of hooks that you might find useful to define + across-the-board rules for your payload. These rules will be useful + if your server is consistent, or if you're building an adapter for + an infrastructure service, like Parse, and want to encode service + conventions. - ```javascript - App.ApplicationAdapter = DS.RESTAdapter.extend({ - ajaxError: function(jqXHR) { - var error = this._super(jqXHR); + For example, if all of your keys are underscored and all-caps, but + otherwise consistent with the names you use in your models, you + can implement across-the-board rules for how to convert an attribute + name in your model to a key in your JSON. - if (jqXHR && jqXHR.status === 422) { - var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"]; - return new DS.InvalidError(jsonErrors); - } else { - return error; - } + ```js + App.ApplicationSerializer = DS.RESTSerializer.extend({ + keyForAttribute: function(attr) { + return Ember.String.underscore(attr).toUpperCase(); } }); ``` - - The `DS.InvalidError` must be constructed with a single object whose - keys are the invalid model properties, and whose values are the - corresponding error messages. For example: - - ```javascript - return new DS.InvalidError({ - length: 'Must be less than 15', - name: 'Must not be blank - }); - ``` - @class InvalidError + You can also implement `keyForRelationship`, which takes the name + of the relationship as the first parameter, and the kind of + relationship (`hasMany` or `belongsTo`) as the second parameter. + + @class RESTSerializer @namespace DS + @extends DS.JSONSerializer */ - var InvalidError = function(errors) { - var tmp = Error.prototype.constructor.call(this, "The backend rejected the commit because it was invalid: " + Ember.inspect(errors)); - this.errors = errors; - - for (var i=0, l=errorProps.length; i 0; i--) { - var proxyPair = proxyPairs[i - 1], - deprecated = proxyPair['deprecated'], - valid = proxyPair['valid']; + ```js + { + "people": [{ + "id": 1, + "first_name": "Barack", + "last_name": "Obama", + "occupation_id": 1 + }], - this.registerDeprecation(deprecated, valid); + "occupations": [{ + "id": 1, + "name": "President", + "salary": 100000, + "person_ids": [1] + }] } - }; - - __exports__["default"] = ContainerProxy; - }); -define("ember-data/lib/system/debug", - ["./debug/debug_info","./debug/debug_adapter","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var DebugAdapter = __dependency2__["default"]; + ``` - __exports__["default"] = DebugAdapter; - }); -define("ember-data/lib/system/debug/debug_adapter", - ["../model","exports"], - function(__dependency1__, __exports__) { - "use strict"; - /** - @module ember-data + @class ActiveModelSerializer + @namespace DS + @extends DS.RESTSerializer */ - var Model = __dependency1__.Model; - var get = Ember.get, capitalize = Ember.String.capitalize, underscore = Ember.String.underscore; + var activemodel$adapter$lib$system$active_model_serializer$$ActiveModelSerializer = ember$data$lib$serializers$rest_serializer$$default.extend({ + // SERIALIZE - /** - Extend `Ember.DataAdapter` with ED specific code. + /** + Converts camelCased attributes to underscored when serializing. - @class DebugAdapter - @namespace DS - @extends Ember.DataAdapter - @private - */ - var DebugAdapter = Ember.DataAdapter.extend({ - getFilters: function() { - return [ - { name: 'isNew', desc: 'New' }, - { name: 'isModified', desc: 'Modified' }, - { name: 'isClean', desc: 'Clean' } - ]; + @method keyForAttribute + @param {String} attribute + @return String + */ + keyForAttribute: function(attr) { + return activemodel$adapter$lib$system$active_model_serializer$$decamelize(attr); }, - detect: function(klass) { - return klass !== Model && Model.detect(klass); - }, + /** + Underscores relationship names and appends "_id" or "_ids" when serializing + relationship keys. - columnsForType: function(type) { - var columns = [{ name: 'id', desc: 'Id' }], count = 0, self = this; - get(type, 'attributes').forEach(function(name, meta) { - if (count++ > self.attributeLimit) { return false; } - var desc = capitalize(underscore(name).replace('_', ' ')); - columns.push({ name: name, desc: desc }); - }); - return columns; + @method keyForRelationship + @param {String} key + @param {String} kind + @return String + */ + keyForRelationship: function(rawKey, kind) { + var key = activemodel$adapter$lib$system$active_model_serializer$$decamelize(rawKey); + if (kind === "belongsTo") { + return key + "_id"; + } else if (kind === "hasMany") { + return ember$inflector$lib$system$string$$singularize(key) + "_ids"; + } else { + return key; + } }, - getRecords: function(type) { - return this.get('store').all(type); - }, + /* + Does not serialize hasMany relationships by default. + */ + serializeHasMany: Ember.K, - getRecordColumnValues: function(record) { - var self = this, count = 0, - columnValues = { id: get(record, 'id') }; + /** + Underscores the JSON root keys when serializing. - record.eachAttribute(function(key) { - if (count++ > self.attributeLimit) { - return false; - } - var value = get(record, key); - columnValues[key] = value; - }); - return columnValues; + @method serializeIntoHash + @param {Object} hash + @param {subclass of DS.Model} type + @param {DS.Model} record + @param {Object} options + */ + serializeIntoHash: function(data, type, record, options) { + var root = activemodel$adapter$lib$system$active_model_serializer$$underscore(activemodel$adapter$lib$system$active_model_serializer$$decamelize(type.typeKey)); + data[root] = this.serialize(record, options); }, - getRecordKeywords: function(record) { - var keywords = [], keys = Ember.A(['id']); - record.eachAttribute(function(key) { - keys.push(key); - }); - keys.forEach(function(key) { - keywords.push(get(record, key)); - }); - return keywords; - }, + /** + Serializes a polymorphic type as a fully capitalized model name. - getRecordFilterValues: function(record) { - return { - isNew: record.get('isNew'), - isModified: record.get('isDirty') && !record.get('isNew'), - isClean: !record.get('isDirty') - }; - }, + @method serializePolymorphicType + @param {DS.Model} record + @param {Object} json + @param {Object} relationship + */ + serializePolymorphicType: function(record, json, relationship) { + var key = relationship.key; + var belongsTo = activemodel$adapter$lib$system$active_model_serializer$$get(record, key); + var jsonKey = activemodel$adapter$lib$system$active_model_serializer$$underscore(key + "_type"); - getRecordColor: function(record) { - var color = 'black'; - if (record.get('isNew')) { - color = 'green'; - } else if (record.get('isDirty')) { - color = 'blue'; + if (Ember.isNone(belongsTo)) { + json[jsonKey] = null; + } else { + json[jsonKey] = activemodel$adapter$lib$system$active_model_serializer$$capitalize(activemodel$adapter$lib$system$active_model_serializer$$camelize(belongsTo.constructor.typeKey)); } - return color; }, - observeRecord: function(record, recordUpdated) { - var releaseMethods = Ember.A(), self = this, - keysToObserve = Ember.A(['id', 'isNew', 'isDirty']); + // EXTRACT - record.eachAttribute(function(key) { - keysToObserve.push(key); - }); + /** + Add extra step to `DS.RESTSerializer.normalize` so links are normalized. - keysToObserve.forEach(function(key) { - var handler = function() { - recordUpdated(self.wrapRecord(record)); - }; - Ember.addObserver(record, key, handler); - releaseMethods.push(function() { - Ember.removeObserver(record, key, handler); - }); - }); + If your payload looks like: - var release = function() { - releaseMethods.forEach(function(fn) { fn(); } ); - }; + ```js + { + "post": { + "id": 1, + "title": "Rails is omakase", + "links": { "flagged_comments": "api/comments/flagged" } + } + } + ``` - return release; - } + The normalized version would look like this - }); + ```js + { + "post": { + "id": 1, + "title": "Rails is omakase", + "links": { "flaggedComments": "api/comments/flagged" } + } + } + ``` - __exports__["default"] = DebugAdapter; - }); -define("ember-data/lib/system/debug/debug_info", - ["../model","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Model = __dependency1__.Model; + @method normalize + @param {subclass of DS.Model} type + @param {Object} hash + @param {String} prop + @return Object + */ + + normalize: function(type, hash, prop) { + this.normalizeLinks(hash); - Model.reopen({ + return this._super(type, hash, prop); + }, /** - Provides info about the model for debugging purposes - by grouping the properties into more semantic groups. + Convert `snake_cased` links to `camelCase` - Meant to be used by debugging tools such as the Chrome Ember Extension. + @method normalizeLinks + @param {Object} data + */ - - Groups all attributes in "Attributes" group. - - Groups all belongsTo relationships in "Belongs To" group. - - Groups all hasMany relationships in "Has Many" group. - - Groups all flags in "Flags" group. - - Flags relationship CPs as expensive properties. + normalizeLinks: function(data){ + if (data.links) { + var links = data.links; - @method _debugInfo - @for DS.Model - @private - */ - _debugInfo: function() { - var attributes = ['id'], - relationships = { belongsTo: [], hasMany: [] }, - expensiveProperties = []; + for (var link in links) { + var camelizedLink = activemodel$adapter$lib$system$active_model_serializer$$camelize(link); - this.eachAttribute(function(name, meta) { - attributes.push(name); - }, this); + if (camelizedLink !== link) { + links[camelizedLink] = links[link]; + delete links[link]; + } + } + } + }, - this.eachRelationship(function(name, relationship) { - relationships[relationship.kind].push(name); - expensiveProperties.push(name); - }); + /** + Normalize the polymorphic type from the JSON. - var groups = [ - { - name: 'Attributes', - properties: attributes, - expand: true - }, - { - name: 'Belongs To', - properties: relationships.belongsTo, - expand: true - }, - { - name: 'Has Many', - properties: relationships.hasMany, - expand: true - }, + Normalize: + ```js { - name: 'Flags', - properties: ['isLoaded', 'isDirty', 'isSaving', 'isDeleted', 'isError', 'isNew', 'isValid'] + id: "1" + minion: { type: "evil_minion", id: "12"} } - ]; + ``` - return { - propertyInfo: { - // include all other mixins / properties (not just the grouped ones) - includeOtherProperties: true, - groups: groups, - // don't pre-calculate unless cached - expensiveProperties: expensiveProperties + To: + ```js + { + id: "1" + minion: { type: "evilMinion", id: "12"} } - }; + ``` + + @method normalizeRelationships + @private + */ + normalizeRelationships: function(type, hash) { + + if (this.keyForRelationship) { + type.eachRelationship(function(key, relationship) { + var payloadKey, payload; + if (relationship.options.polymorphic) { + payloadKey = this.keyForAttribute(key); + payload = hash[payloadKey]; + if (payload && payload.type) { + payload.type = this.typeForRoot(payload.type); + } else if (payload && relationship.kind === "hasMany") { + var self = this; + activemodel$adapter$lib$system$active_model_serializer$$forEach(payload, function(single) { + single.type = self.typeForRoot(single.type); + }); + } + } else { + payloadKey = this.keyForRelationship(key, relationship.kind); + if (!hash.hasOwnProperty(payloadKey)) { return; } + payload = hash[payloadKey]; + } + + hash[key] = payload; + + if (key !== payloadKey) { + delete hash[payloadKey]; + } + }, this); + } } }); - __exports__["default"] = Model; - }); -define("ember-data/lib/system/model", - ["./model/model","./model/attributes","./model/states","./model/errors","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; + var activemodel$adapter$lib$system$active_model_serializer$$default = activemodel$adapter$lib$system$active_model_serializer$$ActiveModelSerializer; /** - @module ember-data + This is used internally to enable deprecation of container paths and provide + a decent message to the user indicating how to fix the issue. + + @class ContainerProxy + @namespace DS + @private */ + function ember$data$lib$system$container_proxy$$ContainerProxy(container){ + this.container = container; + } - var Model = __dependency1__["default"]; - var attr = __dependency2__["default"]; - var RootState = __dependency3__["default"]; - var Errors = __dependency4__["default"]; - - __exports__.Model = Model; - __exports__.RootState = RootState; - __exports__.attr = attr; - __exports__.Errors = Errors; - }); -define("ember-data/lib/system/model/attributes", - ["./model","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Model = __dependency1__["default"]; + ember$data$lib$system$container_proxy$$ContainerProxy.prototype.aliasedFactory = function(path, preLookup) { + var _this = this; + + return {create: function(){ + if (preLookup) { preLookup(); } + + return _this.container.lookup(path); + }}; + }; + + ember$data$lib$system$container_proxy$$ContainerProxy.prototype.registerAlias = function(source, dest, preLookup) { + var factory = this.aliasedFactory(dest, preLookup); + + return this.container.register(source, factory); + }; + + ember$data$lib$system$container_proxy$$ContainerProxy.prototype.registerDeprecation = function(deprecated, valid) { + var preLookupCallback = function(){ + Ember.deprecate("You tried to look up '" + deprecated + "', " + + "but this has been deprecated in favor of '" + valid + "'.", false); + }; + + return this.registerAlias(deprecated, valid, preLookupCallback); + }; + + ember$data$lib$system$container_proxy$$ContainerProxy.prototype.registerDeprecations = function(proxyPairs) { + var i, proxyPair, deprecated, valid; + + for (i = proxyPairs.length; i > 0; i--) { + proxyPair = proxyPairs[i - 1]; + deprecated = proxyPair['deprecated']; + valid = proxyPair['valid']; + this.registerDeprecation(deprecated, valid); + } + }; + + var ember$data$lib$system$container_proxy$$default = ember$data$lib$system$container_proxy$$ContainerProxy; + function activemodel$adapter$lib$setup$container$$setupActiveModelAdapter(container, application){ + var proxy = new ember$data$lib$system$container_proxy$$default(container); + proxy.registerDeprecations([ + { deprecated: 'serializer:_ams', valid: 'serializer:-active-model' }, + { deprecated: 'adapter:_ams', valid: 'adapter:-active-model' } + ]); + + container.register('serializer:-active-model', activemodel$adapter$lib$system$active_model_serializer$$default); + container.register('adapter:-active-model', activemodel$adapter$lib$system$active_model_adapter$$default); + } + var activemodel$adapter$lib$setup$container$$default = activemodel$adapter$lib$setup$container$$setupActiveModelAdapter; /** @module ember-data */ - var get = Ember.get; + /** + All Ember Data methods and functions are defined inside of this namespace. + + @class DS + @static + */ /** - @class Model - @namespace DS + @property VERSION + @type String + @default '1.0.0-beta.14.1' + @static */ - Model.reopenClass({ - /** - A map whose keys are the attributes of the model (properties - described by DS.attr) and whose values are the meta object for the - property. + /*jshint -W079 */ + var ember$data$lib$core$$DS = Ember.Namespace.create({ + VERSION: '1.0.0-beta.14.1' + }); - Example + if (Ember.libraries) { + Ember.libraries.registerCoreLibrary('Ember Data', ember$data$lib$core$$DS.VERSION); + } - ```javascript + var ember$data$lib$core$$default = ember$data$lib$core$$DS; + var ember$data$lib$system$promise_proxies$$Promise = Ember.RSVP.Promise; + var ember$data$lib$system$promise_proxies$$get = Ember.get; - App.Person = DS.Model.extend({ - firstName: attr('string'), - lastName: attr('string'), - birthday: attr('date') - }); + /** + A `PromiseArray` is an object that acts like both an `Ember.Array` + and a promise. When the promise is resolved the resulting value + will be set to the `PromiseArray`'s `content` property. This makes + it easy to create data bindings with the `PromiseArray` that will be + updated when the promise resolves. - var attributes = Ember.get(App.Person, 'attributes') + For more information see the [Ember.PromiseProxyMixin + documentation](/api/classes/Ember.PromiseProxyMixin.html). - attributes.forEach(function(name, meta) { - console.log(name, meta); - }); + Example - // prints: - // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"} - // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"} - // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"} - ``` + ```javascript + var promiseArray = DS.PromiseArray.create({ + promise: $.getJSON('/some/remote/data.json') + }); - @property attributes - @static - @type {Ember.Map} - @readOnly - */ - attributes: Ember.computed(function() { - var map = Ember.Map.create(); + promiseArray.get('length'); // 0 - this.eachComputedProperty(function(name, meta) { - if (meta.isAttribute) { - Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('')` from " + this.toString(), name !== 'id'); + promiseArray.then(function() { + promiseArray.get('length'); // 100 + }); + ``` - meta.name = name; - map.set(name, meta); - } - }); + @class PromiseArray + @namespace DS + @extends Ember.ArrayProxy + @uses Ember.PromiseProxyMixin + */ + var ember$data$lib$system$promise_proxies$$PromiseArray = Ember.ArrayProxy.extend(Ember.PromiseProxyMixin); - return map; - }), + /** + A `PromiseObject` is an object that acts like both an `Ember.Object` + and a promise. When the promise is resolved, then the resulting value + will be set to the `PromiseObject`'s `content` property. This makes + it easy to create data bindings with the `PromiseObject` that will + be updated when the promise resolves. - /** - A map whose keys are the attributes of the model (properties - described by DS.attr) and whose values are type of transformation - applied to each attribute. This map does not include any - attributes that do not have an transformation type. + For more information see the [Ember.PromiseProxyMixin + documentation](/api/classes/Ember.PromiseProxyMixin.html). - Example + Example - ```javascript - App.Person = DS.Model.extend({ - firstName: attr(), - lastName: attr('string'), - birthday: attr('date') - }); + ```javascript + var promiseObject = DS.PromiseObject.create({ + promise: $.getJSON('/some/remote/data.json') + }); - var transformedAttributes = Ember.get(App.Person, 'transformedAttributes') + promiseObject.get('name'); // null - transformedAttributes.forEach(function(field, type) { - console.log(field, type); - }); + promiseObject.then(function() { + promiseObject.get('name'); // 'Tomster' + }); + ``` - // prints: - // lastName string - // birthday date - ``` + @class PromiseObject + @namespace DS + @extends Ember.ObjectProxy + @uses Ember.PromiseProxyMixin + */ + var ember$data$lib$system$promise_proxies$$PromiseObject = Ember.ObjectProxy.extend(Ember.PromiseProxyMixin); - @property transformedAttributes - @static - @type {Ember.Map} - @readOnly - */ - transformedAttributes: Ember.computed(function() { - var map = Ember.Map.create(); + var ember$data$lib$system$promise_proxies$$promiseObject = function(promise, label) { + return ember$data$lib$system$promise_proxies$$PromiseObject.create({ + promise: ember$data$lib$system$promise_proxies$$Promise.resolve(promise, label) + }); + }; - this.eachAttribute(function(key, meta) { - if (meta.type) { - map.set(key, meta.type); - } - }); + var ember$data$lib$system$promise_proxies$$promiseArray = function(promise, label) { + return ember$data$lib$system$promise_proxies$$PromiseArray.create({ + promise: ember$data$lib$system$promise_proxies$$Promise.resolve(promise, label) + }); + }; - return map; - }), + /** + A PromiseManyArray is a PromiseArray that also proxies certain method calls + to the underlying manyArray. + Right now we proxy: - /** - Iterates through the attributes of the model, calling the passed function on each - attribute. + * `reload()` + * `createRecord()` + * `on()` + * `one()` + * `trigger()` + * `off()` + * `has()` - The callback method you provide should have the following signature (all - parameters are optional): + @class PromiseManyArray + @namespace DS + @extends Ember.ArrayProxy + */ - ```javascript - function(name, meta); - ``` + function ember$data$lib$system$promise_proxies$$proxyToContent(method) { + return function() { + var content = ember$data$lib$system$promise_proxies$$get(this, 'content'); + return content[method].apply(content, arguments); + }; + } - - `name` the name of the current property in the iteration - - `meta` the meta object for the attribute property in the iteration + var ember$data$lib$system$promise_proxies$$PromiseManyArray = ember$data$lib$system$promise_proxies$$PromiseArray.extend({ + reload: function() { + //I don't think this should ever happen right now, but worth guarding if we refactor the async relationships + Ember.assert('You are trying to reload an async manyArray before it has been created', ember$data$lib$system$promise_proxies$$get(this, 'content')); + return ember$data$lib$system$promise_proxies$$PromiseManyArray.create({ + promise: ember$data$lib$system$promise_proxies$$get(this, 'content').reload() + }); + }, - Note that in addition to a callback, you can also pass an optional target - object that will be set as `this` on the context. + createRecord: ember$data$lib$system$promise_proxies$$proxyToContent('createRecord'), - Example + on: ember$data$lib$system$promise_proxies$$proxyToContent('on'), - ```javascript - App.Person = DS.Model.extend({ - firstName: attr('string'), - lastName: attr('string'), - birthday: attr('date') - }); + one: ember$data$lib$system$promise_proxies$$proxyToContent('one'), - App.Person.eachAttribute(function(name, meta) { - console.log(name, meta); - }); + trigger: ember$data$lib$system$promise_proxies$$proxyToContent('trigger'), - // prints: - // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"} - // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"} - // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"} - ``` + off: ember$data$lib$system$promise_proxies$$proxyToContent('off'), - @method eachAttribute - @param {Function} callback The callback to execute - @param {Object} [target] The target object to use - @static - */ - eachAttribute: function(callback, binding) { - get(this, 'attributes').forEach(function(name, meta) { - callback.call(binding, name, meta); - }, binding); - }, + has: ember$data$lib$system$promise_proxies$$proxyToContent('has') + }); - /** - Iterates through the transformedAttributes of the model, calling - the passed function on each attribute. Note the callback will not be - called for any attributes that do not have an transformation type. + var ember$data$lib$system$promise_proxies$$promiseManyArray = function(promise, label) { + return ember$data$lib$system$promise_proxies$$PromiseManyArray.create({ + promise: ember$data$lib$system$promise_proxies$$Promise.resolve(promise, label) + }); + }; - The callback method you provide should have the following signature (all - parameters are optional): - ```javascript - function(name, type); - ``` + var ember$data$lib$system$record_arrays$record_array$$get = Ember.get; + var ember$data$lib$system$record_arrays$record_array$$set = Ember.set; - - `name` the name of the current property in the iteration - - `type` a string containing the name of the type of transformed - applied to the attribute + var ember$data$lib$system$record_arrays$record_array$$default = Ember.ArrayProxy.extend(Ember.Evented, { + /** + The model type contained by this record array. - Note that in addition to a callback, you can also pass an optional target - object that will be set as `this` on the context. + @property type + @type DS.Model + */ + type: null, - Example + /** + The array of client ids backing the record array. When a + record is requested from the record array, the record + for the client id at the same index is materialized, if + necessary, by the store. - ```javascript - App.Person = DS.Model.extend({ - firstName: attr(), - lastName: attr('string'), - birthday: attr('date') - }); + @property content + @private + @type Ember.Array + */ + content: null, - App.Person.eachTransformedAttribute(function(name, type) { - console.log(name, type); - }); + /** + The flag to signal a `RecordArray` is currently loading data. - // prints: - // lastName string - // birthday date - ``` + Example - @method eachTransformedAttribute - @param {Function} callback The callback to execute - @param {Object} [target] The target object to use - @static - */ - eachTransformedAttribute: function(callback, binding) { - get(this, 'transformedAttributes').forEach(function(name, type) { - callback.call(binding, name, type); - }); - } - }); + ```javascript + var people = store.all('person'); + people.get('isLoaded'); // true + ``` + @property isLoaded + @type Boolean + */ + isLoaded: false, + /** + The flag to signal a `RecordArray` is currently loading data. - Model.reopen({ - eachAttribute: function(callback, binding) { - this.constructor.eachAttribute(callback, binding); - } - }); + Example - function getDefaultValue(record, options, key) { - if (typeof options.defaultValue === "function") { - return options.defaultValue.apply(null, arguments); - } else { - return options.defaultValue; - } - } + ```javascript + var people = store.all('person'); + people.get('isUpdating'); // false + people.update(); + people.get('isUpdating'); // true + ``` - function hasValue(record, key) { - return record._attributes.hasOwnProperty(key) || - record._inFlightAttributes.hasOwnProperty(key) || - record._data.hasOwnProperty(key); - } + @property isUpdating + @type Boolean + */ + isUpdating: false, - function getValue(record, key) { - if (record._attributes.hasOwnProperty(key)) { - return record._attributes[key]; - } else if (record._inFlightAttributes.hasOwnProperty(key)) { - return record._inFlightAttributes[key]; - } else { - return record._data[key]; - } - } + /** + The store that created this record array. - /** - `DS.attr` defines an attribute on a [DS.Model](/api/data/classes/DS.Model.html). - By default, attributes are passed through as-is, however you can specify an - optional type to have the value automatically transformed. - Ember Data ships with four basic transform types: `string`, `number`, - `boolean` and `date`. You can define your own transforms by subclassing - [DS.Transform](/api/data/classes/DS.Transform.html). - - `DS.attr` takes an optional hash as a second parameter, currently - supported options are: + @property store + @private + @type DS.Store + */ + store: null, - - `defaultValue`: Pass a string or a function to be called to set the attribute - to a default value if none is supplied. + /** + Retrieves an object from the content by index. - Example + @method objectAtContent + @private + @param {Number} index + @return {DS.Model} record + */ + objectAtContent: function(index) { + var content = ember$data$lib$system$record_arrays$record_array$$get(this, 'content'); - ```javascript - var attr = DS.attr; + return content.objectAt(index); + }, - App.User = DS.Model.extend({ - username: attr('string'), - email: attr('string'), - verified: attr('boolean', {defaultValue: false}) - }); - ``` + /** + Used to get the latest version of all of the records in this array + from the adapter. - @namespace - @method attr - @for DS - @param {String} type the attribute type - @param {Object} options a hash of options - @return {Attribute} - */ + Example - function attr(type, options) { - options = options || {}; + ```javascript + var people = store.all('person'); + people.get('isUpdating'); // false + people.update(); + people.get('isUpdating'); // true + ``` - var meta = { - type: type, - isAttribute: true, - options: options - }; + @method update + */ + update: function() { + if (ember$data$lib$system$record_arrays$record_array$$get(this, 'isUpdating')) { return; } - return Ember.computed('data', function(key, value) { - if (arguments.length > 1) { - Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('')` from " + this.constructor.toString(), key !== 'id'); - var oldValue = getValue(this, key); + var store = ember$data$lib$system$record_arrays$record_array$$get(this, 'store'); + var type = ember$data$lib$system$record_arrays$record_array$$get(this, 'type'); - if (value !== oldValue) { - // Add the new value to the changed attributes hash; it will get deleted by - // the 'didSetProperty' handler if it is no different from the original value - this._attributes[key] = value; + return store.fetchAll(type, this); + }, - this.send('didSetProperty', { - name: key, - oldValue: oldValue, - originalValue: this._data[key], - value: value - }); - } + /** + Adds a record to the `RecordArray` without duplicates - return value; - } else if (hasValue(this, key)) { - return getValue(this, key); + @method addRecord + @private + @param {DS.Model} record + @param {DS.Model} an optional index to insert at + */ + addRecord: function(record, idx) { + var content = ember$data$lib$system$record_arrays$record_array$$get(this, 'content'); + if (idx === undefined) { + content.addObject(record); } else { - return getDefaultValue(this, options, key); + if (!content.contains(record)) { + content.insertAt(idx, record); + } } + }, - // `data` is never set directly. However, it may be - // invalidated from the state manager's setData - // event. - }).meta(meta); - } - - __exports__["default"] = attr; - }); -define("ember-data/lib/system/model/errors", - ["exports"], - function(__exports__) { - "use strict"; - var get = Ember.get, isEmpty = Ember.isEmpty; - - /** - @module ember-data - */ - - /** - Holds validation errors for a given record organized by attribute names. - - @class Errors - @namespace DS - @extends Ember.Object - @uses Ember.Enumerable - @uses Ember.Evented - */ - var Errors = Ember.Object.extend(Ember.Enumerable, Ember.Evented, { /** - Register with target handler + Adds a record to the `RecordArray`, but allows duplicates - @method registerHandlers - @param {Object} target - @param {Function} becameInvalid - @param {Function} becameValid + @method pushRecord + @private + @param {DS.Model} record */ - registerHandlers: function(target, becameInvalid, becameValid) { - this.on('becameInvalid', target, becameInvalid); - this.on('becameValid', target, becameValid); + pushRecord: function(record) { + ember$data$lib$system$record_arrays$record_array$$get(this, 'content').pushObject(record); }, - /** - @property errorsByAttributeName - @type {Ember.MapWithDefault} + Removes a record to the `RecordArray`. + + @method removeRecord @private + @param {DS.Model} record */ - errorsByAttributeName: Ember.reduceComputed("content", { - initialValue: function() { - return Ember.MapWithDefault.create({ - defaultValue: function() { - return Ember.A(); - } - }); - }, - - addedItem: function(errors, error) { - errors.get(error.attribute).pushObject(error); - - return errors; - }, + removeRecord: function(record) { + ember$data$lib$system$record_arrays$record_array$$get(this, 'content').removeObject(record); + }, - removedItem: function(errors, error) { - errors.get(error.attribute).removeObject(error); + /** + Saves all of the records in the `RecordArray`. - return errors; - } - }), + Example - /** - Returns errors for a given attribute + ```javascript + var messages = store.all('message'); + messages.forEach(function(message) { + message.set('hasBeenSeen', true); + }); + messages.save(); + ``` - @method errorsFor - @param {String} attribute - @returns {Array} + @method save + @return {DS.PromiseArray} promise */ - errorsFor: function(attribute) { - return get(this, 'errorsByAttributeName').get(attribute); + save: function() { + var promiseLabel = "DS: RecordArray#save " + ember$data$lib$system$record_arrays$record_array$$get(this, 'type'); + var promise = Ember.RSVP.all(this.invoke("save"), promiseLabel).then(function(array) { + return Ember.A(array); + }, null, "DS: RecordArray#save apply Ember.NativeArray"); + + return ember$data$lib$system$promise_proxies$$PromiseArray.create({ promise: promise }); }, - /** - */ - messages: Ember.computed.mapBy('content', 'message'), + _dissociateFromOwnRecords: function() { + var array = this; - /** - @property content - @type {Array} - @private - */ - content: Ember.computed(function() { - return Ember.A(); - }), + this.forEach(function(record){ + var recordArrays = record._recordArrays; - /** - @method unknownProperty - @private - */ - unknownProperty: function(attribute) { - var errors = this.errorsFor(attribute); - if (isEmpty(errors)) { return null; } - return errors; + if (recordArrays) { + recordArrays["delete"](array); + } + }); }, /** - @method nextObject + @method _unregisterFromManager @private */ - nextObject: function(index, previousObject, context) { - return get(this, 'content').objectAt(index); + _unregisterFromManager: function(){ + var manager = ember$data$lib$system$record_arrays$record_array$$get(this, 'manager'); + //We will stop needing this stupid if statement soon, once manyArray are refactored to not be RecordArrays + if (manager) { + manager.unregisterFilteredRecordArray(this); + } }, - /** - Total number of errors. + willDestroy: function(){ + this._unregisterFromManager(); + this._dissociateFromOwnRecords(); + ember$data$lib$system$record_arrays$record_array$$set(this, 'content', undefined); + this._super(); + } + }); - @property length - @type {Number} - @readOnly - */ - length: Ember.computed.oneWay('content.length').readOnly(), + /** + @module ember-data + */ - /** - @property isEmpty - @type {Boolean} - @readOnly - */ - isEmpty: Ember.computed.not('length').readOnly(), + var ember$data$lib$system$record_arrays$filtered_record_array$$get = Ember.get; + var ember$data$lib$system$record_arrays$filtered_record_array$$default = ember$data$lib$system$record_arrays$record_array$$default.extend({ /** - Adds error messages to a given attribute and sends - `becameInvalid` event to the record. + The filterFunction is a function used to test records from the store to + determine if they should be part of the record array. - @method add - @param {String} attribute - @param {Array|String} messages - */ - add: function(attribute, messages) { - var wasEmpty = get(this, 'isEmpty'); + Example - messages = this._findOrCreateMessages(attribute, messages); - get(this, 'content').addObjects(messages); + ```javascript + var allPeople = store.all('person'); + allPeople.mapBy('name'); // ["Tom Dale", "Yehuda Katz", "Trek Glowacki"] - this.notifyPropertyChange(attribute); - this.enumerableContentDidChange(); + var people = store.filter('person', function(person) { + if (person.get('name').match(/Katz$/)) { return true; } + }); + people.mapBy('name'); // ["Yehuda Katz"] - if (wasEmpty && !get(this, 'isEmpty')) { - this.trigger('becameInvalid'); - } - }, + var notKatzFilter = function(person) { + return !person.get('name').match(/Katz$/); + }; + people.set('filterFunction', notKatzFilter); + people.mapBy('name'); // ["Tom Dale", "Trek Glowacki"] + ``` - /** - @method _findOrCreateMessages - @private + @method filterFunction + @param {DS.Model} record + @return {Boolean} `true` if the record should be in the array */ - _findOrCreateMessages: function(attribute, messages) { - var errors = this.errorsFor(attribute); + filterFunction: null, + isLoaded: true, - return Ember.makeArray(messages).map(function(message) { - return errors.findBy('message', message) || { - attribute: attribute, - message: message - }; - }); + replace: function() { + var type = ember$data$lib$system$record_arrays$filtered_record_array$$get(this, 'type').toString(); + throw new Error("The result of a client-side filter (on " + type + ") is immutable."); }, /** - Removes all error messages from the given attribute and sends - `becameValid` event to the record if there no more errors left. - - @method remove - @param {String} attribute + @method updateFilter + @private */ - remove: function(attribute) { - if (get(this, 'isEmpty')) { return; } + _updateFilter: function() { + var manager = ember$data$lib$system$record_arrays$filtered_record_array$$get(this, 'manager'); + manager.updateFilter(this, ember$data$lib$system$record_arrays$filtered_record_array$$get(this, 'type'), ember$data$lib$system$record_arrays$filtered_record_array$$get(this, 'filterFunction')); + }, - var content = get(this, 'content').rejectBy('attribute', attribute); - get(this, 'content').setObjects(content); + updateFilter: Ember.observer(function() { + Ember.run.once(this, this._updateFilter); + }, 'filterFunction'), - this.notifyPropertyChange(attribute); - this.enumerableContentDidChange(); + }); - if (get(this, 'isEmpty')) { - this.trigger('becameValid'); - } - }, + /** + @module ember-data + */ - /** - Removes all error messages and sends `becameValid` event - to the record. + var ember$data$lib$system$record_arrays$adapter_populated_record_array$$get = Ember.get; - @method clear - */ - clear: function() { - if (get(this, 'isEmpty')) { return; } + function ember$data$lib$system$record_arrays$adapter_populated_record_array$$cloneNull(source) { + var clone = Ember.create(null); + for (var key in source) { + clone[key] = source[key]; + } + return clone; + } - get(this, 'content').clear(); - this.enumerableContentDidChange(); + var ember$data$lib$system$record_arrays$adapter_populated_record_array$$default = ember$data$lib$system$record_arrays$record_array$$default.extend({ + query: null, - this.trigger('becameValid'); + replace: function() { + var type = ember$data$lib$system$record_arrays$adapter_populated_record_array$$get(this, 'type').toString(); + throw new Error("The result of a server query (on " + type + ") is immutable."); }, /** - Checks if there is error messages for the given attribute. - - @method has - @param {String} attribute - @returns {Boolean} true if there some errors on given attribute + @method load + @private + @param {Array} data */ - has: function(attribute) { - return !isEmpty(this.errorsFor(attribute)); + load: function(data) { + var store = ember$data$lib$system$record_arrays$adapter_populated_record_array$$get(this, 'store'); + var type = ember$data$lib$system$record_arrays$adapter_populated_record_array$$get(this, 'type'); + var records = store.pushMany(type, data); + var meta = store.metadataFor(type); + + this.setProperties({ + content: Ember.A(records), + isLoaded: true, + meta: ember$data$lib$system$record_arrays$adapter_populated_record_array$$cloneNull(meta) + }); + + records.forEach(function(record) { + this.manager.recordArraysForRecord(record).add(this); + }, this); + + // TODO: should triggering didLoad event be the last action of the runLoop? + Ember.run.once(this, 'trigger', 'didLoad'); } }); - __exports__["default"] = Errors; - }); -define("ember-data/lib/system/model/model", - ["./states","./errors","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var RootState = __dependency1__["default"]; - var Errors = __dependency2__["default"]; /** @module ember-data */ - var get = Ember.get, set = Ember.set, - merge = Ember.merge, - Promise = Ember.RSVP.Promise; + var ember$data$lib$system$record_arrays$many_array$$get = Ember.get, ember$data$lib$system$record_arrays$many_array$$set = Ember.set; - var retrieveFromCurrentState = Ember.computed('currentState', function(key, value) { - return get(get(this, 'currentState'), key); - }).readOnly(); + var ember$data$lib$system$record_arrays$many_array$$default = Ember.Object.extend(Ember.MutableArray, Ember.Evented, { + init: function() { + this.currentState = Ember.A([]); + this.diff = []; + }, - /** + record: null, - The model class that all Ember Data records descend from. + canonicalState: null, + currentState: null, - @class Model - @namespace DS - @extends Ember.Object - @uses Ember.Evented - */ - var Model = Ember.Object.extend(Ember.Evented, { - _recordArrays: undefined, - _relationships: undefined, - _loadingRecordArrays: undefined, + diff: null, + + length: 0, + + objectAt: function(index) { + if (this.currentState[index]) { + return this.currentState[index]; + } else { + return this.canonicalState[index]; + } + }, + + flushCanonical: function() { + //TODO make this smarter, currently its plenty stupid + var toSet = this.canonicalState.slice(0); + //a hack for not removing new records + //TODO remove once we have proper diffing + var newRecords = this.currentState.filter(function(record) { + return record.get('isNew'); + }); + toSet = toSet.concat(newRecords); + this.arrayContentWillChange(0, this.length, this.length); + this.set('length', toSet.length); + this.currentState = toSet; + this.arrayContentDidChange(0, this.length, this.length); + //TODO Figure out to notify only on additions and maybe only if unloaded + this.relationship.notifyHasManyChanged(); + this.record.updateRecordArrays(); + }, /** - If this property is `true` the record is in the `empty` - state. Empty is the first state all records enter after they have - been created. Most records created by the store will quickly - transition to the `loading` state if data needs to be fetched from - the server or the `created` state if the record is created on the - client. A record can also enter the empty state if the adapter is - unable to locate the record. + `true` if the relationship is polymorphic, `false` otherwise. - @property isEmpty - @type {Boolean} - @readOnly + @property {Boolean} isPolymorphic + @private */ - isEmpty: retrieveFromCurrentState, + isPolymorphic: false, + /** - If this property is `true` the record is in the `loading` state. A - record enters this state when the store asks the adapter for its - data. It remains in this state until the adapter provides the - requested data. + The loading state of this array - @property isLoading - @type {Boolean} - @readOnly + @property {Boolean} isLoaded */ - isLoading: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `loaded` state. A - record enters this state when its data is populated. Most of a - record's lifecycle is spent inside substates of the `loaded` - state. + isLoaded: false, - Example + /** + The relationship which manages this array. - ```javascript - var record = store.createRecord(App.Model); - record.get('isLoaded'); // true + @property {ManyRelationship} relationship + @private + */ + relationship: null, - store.find('model', 1).then(function(model) { - model.get('isLoaded'); // true - }); - ``` + internalReplace: function(idx, amt, objects) { + if (!objects) { + objects = []; + } + this.arrayContentWillChange(idx, amt, objects.length); + this.currentState.splice.apply(this.currentState, [idx, amt].concat(objects)); + this.set('length', this.currentState.length); + this.arrayContentDidChange(idx, amt, objects.length); + if (objects){ + //TODO(Igor) probably needed only for unloaded records + this.relationship.notifyHasManyChanged(); + } + this.record.updateRecordArrays(); + }, - @property isLoaded - @type {Boolean} - @readOnly - */ - isLoaded: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `dirty` state. The - record has local changes that have not yet been saved by the - adapter. This includes records that have been created (but not yet - saved) or deleted. + //TODO(Igor) optimize + internalRemoveRecords: function(records) { + var index; + for(var i=0; i < records.length; i++) { + index = this.currentState.indexOf(records[i]); + this.internalReplace(index, 1); + } + }, - Example + //TODO(Igor) optimize + internalAddRecords: function(records, idx) { + if (idx === undefined) { + idx = this.currentState.length; + } + this.internalReplace(idx, 0, records); + }, - ```javascript - var record = store.createRecord(App.Model); - record.get('isDirty'); // true + replace: function(idx, amt, objects) { + var records; + if (amt > 0){ + records = this.currentState.slice(idx, idx+amt); + this.get('relationship').removeRecords(records); + } + if (objects){ + this.get('relationship').addRecords(objects, idx); + } + }, + /** + Used for async `hasMany` arrays + to keep track of when they will resolve. - store.find('model', 1).then(function(model) { - model.get('isDirty'); // false - model.set('foo', 'some value'); - model.set('isDirty'); // true - }); - ``` + @property {Ember.RSVP.Promise} promise + @private + */ + promise: null, - @property isDirty - @type {Boolean} - @readOnly + /** + @method loadingRecordsCount + @param {Number} count + @private */ - isDirty: retrieveFromCurrentState, + loadingRecordsCount: function(count) { + this.loadingRecordsCount = count; + }, + /** - If this property is `true` the record is in the `saving` state. A - record enters the saving state when `save` is called, but the - adapter has not yet acknowledged that the changes have been - persisted to the backend. + @method loadedRecord + @private + */ + loadedRecord: function() { + this.loadingRecordsCount--; + if (this.loadingRecordsCount === 0) { + ember$data$lib$system$record_arrays$many_array$$set(this, 'isLoaded', true); + this.trigger('didLoad'); + } + }, - Example + /** + @method reload + @public + */ + reload: function() { + return this.relationship.reload(); + }, - ```javascript - var record = store.createRecord(App.Model); - record.get('isSaving'); // false - var promise = record.save(); - record.get('isSaving'); // true - promise.then(function() { - record.get('isSaving'); // false - }); - ``` + /** + Create a child record within the owner - @property isSaving - @type {Boolean} - @readOnly + @method createRecord + @private + @param {Object} hash + @return {DS.Model} record */ - isSaving: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `deleted` state - and has been marked for deletion. When `isDeleted` is true and - `isDirty` is true, the record is deleted locally but the deletion - was not yet persisted. When `isSaving` is true, the change is - in-flight. When both `isDirty` and `isSaving` are false, the - change has persisted. - - Example + createRecord: function(hash) { + var store = ember$data$lib$system$record_arrays$many_array$$get(this, 'store'); + var type = ember$data$lib$system$record_arrays$many_array$$get(this, 'type'); + var record; - ```javascript - var record = store.createRecord(App.Model); - record.get('isDeleted'); // false - record.deleteRecord(); - record.get('isDeleted'); // true - ``` + Ember.assert("You cannot add '" + type.typeKey + "' records to this polymorphic relationship.", !ember$data$lib$system$record_arrays$many_array$$get(this, 'isPolymorphic')); - @property isDeleted - @type {Boolean} - @readOnly - */ - isDeleted: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `new` state. A - record will be in the `new` state when it has been created on the - client and the adapter has not yet report that it was successfully - saved. + record = store.createRecord(type, hash); + this.pushObject(record); - Example + return record; + } + }); - ```javascript - var record = store.createRecord(App.Model); - record.get('isNew'); // true + var ember$data$lib$system$record_array_manager$$get = Ember.get; + var ember$data$lib$system$record_array_manager$$forEach = Ember.EnumerableUtils.forEach; + var ember$data$lib$system$record_array_manager$$indexOf = Ember.EnumerableUtils.indexOf; - record.save().then(function(model) { - model.get('isNew'); // false + var ember$data$lib$system$record_array_manager$$default = Ember.Object.extend({ + init: function() { + this.filteredRecordArrays = ember$data$lib$system$map$$MapWithDefault.create({ + defaultValue: function() { return []; } }); - ``` - @property isNew - @type {Boolean} - @readOnly - */ - isNew: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `valid` state. A - record will be in the `valid` state when no client-side - validations have failed and the adapter did not report any - server-side validation failures. + this.changedRecords = []; + this._adapterPopulatedRecordArrays = []; + }, - @property isValid - @type {Boolean} - @readOnly - */ - isValid: retrieveFromCurrentState, - /** - If the record is in the dirty state this property will report what - kind of change has caused it to move into the dirty - state. Possible values are: + recordDidChange: function(record) { + if (this.changedRecords.push(record) !== 1) { return; } - - `created` The record has been created by the client and not yet saved to the adapter. - - `updated` The record has been updated by the client and not yet saved to the adapter. - - `deleted` The record has been deleted by the client and not yet saved to the adapter. + Ember.run.schedule('actions', this, this.updateRecordArrays); + }, - Example + recordArraysForRecord: function(record) { + record._recordArrays = record._recordArrays || ember$data$lib$system$map$$OrderedSet.create(); + return record._recordArrays; + }, - ```javascript - var record = store.createRecord(App.Model); - record.get('dirtyType'); // 'created' - ``` + /** + This method is invoked whenever data is loaded into the store by the + adapter or updated by the adapter, or when a record has changed. - @property dirtyType - @type {String} - @readOnly + It updates all record arrays that a record belongs to. + + To avoid thrashing, it only runs at most once per run loop. + + @method updateRecordArrays + @param {Class} type + @param {Number|String} clientId */ - dirtyType: retrieveFromCurrentState, + updateRecordArrays: function() { + ember$data$lib$system$record_array_manager$$forEach(this.changedRecords, function(record) { + if (ember$data$lib$system$record_array_manager$$get(record, 'isDeleted')) { + this._recordWasDeleted(record); + } else { + this._recordWasChanged(record); + } + }, this); - /** - If `true` the adapter reported that it was unable to save local - changes to the backend. This may also result in the record having - its `isValid` property become false if the adapter reported that - server-side validations failed. + this.changedRecords.length = 0; + }, - Example + _recordWasDeleted: function (record) { + var recordArrays = record._recordArrays; - ```javascript - record.get('isError'); // false - record.set('foo', 'invalid value'); - record.save().then(null, function() { - record.get('isError'); // true + if (!recordArrays) { return; } + + recordArrays.forEach(function(array){ + array.removeRecord(record); }); - ``` - @property isError - @type {Boolean} - @readOnly - */ - isError: false, - /** - If `true` the store is attempting to reload the record form the adapter. + record._recordArrays = null; + }, - Example + _recordWasChanged: function (record) { + var type = record.constructor; + var recordArrays = this.filteredRecordArrays.get(type); + var filter; - ```javascript - record.get('isReloading'); // false - record.reload(); - record.get('isReloading'); // true - ``` + ember$data$lib$system$record_array_manager$$forEach(recordArrays, function(array) { + filter = ember$data$lib$system$record_array_manager$$get(array, 'filterFunction'); + this.updateRecordArray(array, filter, type, record); + }, this); - @property isReloading - @type {Boolean} - @readOnly - */ - isReloading: false, + // loop through all manyArrays containing an unloaded copy of this + // clientId and notify them that the record was loaded. + var manyArrays = record._loadingRecordArrays; - /** - The `clientId` property is a transient numerical identifier - generated at runtime by the data store. It is important - primarily because newly created objects may not yet have an - externally generated id. + if (manyArrays) { + for (var i=0, l=manyArrays.length; i "root.created.uncommitted" + ``` - var setups = [], enters = [], i, l; + The hierarchy of valid states that ship with ember data looks like + this: - for (i=0, l=path.length; i "Received myEvent with foo" + ``` + Note that an optional parameter can be sent to a record's `send()` method, + which will be passed as the second parameter to the event handler. + + Events should transition to a different state if appropriate. This can be + done by calling the record's `transitionTo()` method with a path to the + desired state. The state manager will attempt to resolve the state path + relative to the current state. If no state is found at that path, it will + attempt to resolve it relative to the current state's parent, and then its + parent, and so on until the root is reached. For example, imagine a hierarchy + like this: + + * created + * uncommitted <-- currentState + * inFlight + * updated + * inFlight + + If we are currently in the `uncommitted` state, calling + `transitionTo('inFlight')` would transition to the `created.inFlight` state, + while calling `transitionTo('updated.inFlight')` would transition to + the `updated.inFlight` state. + + Remember that *only events* should ever cause a state transition. You should + never call `transitionTo()` from outside a state's event handler. If you are + tempted to do so, create a new event and send that to the state manager. + + #### Flags + + Flags are Boolean values that can be used to introspect a record's current + state in a more user-friendly way than examining its state path. For example, + instead of doing this: + + ```javascript + var statePath = record.get('stateManager.currentPath'); + if (statePath === 'created.inFlight') { + doSomething(); + } + ``` + + You can say: + + ```javascript + if (record.get('isNew') && record.get('isSaving')) { + doSomething(); + } + ``` + + If your state does not set a value for a given flag, the value will + be inherited from its parent (or the first place in the state hierarchy + where it is defined). + + The current set of flags are defined below. If you want to add a new flag, + in addition to the area below, you will also need to declare it in the + `DS.Model` class. + + + * [isEmpty](DS.Model.html#property_isEmpty) + * [isLoading](DS.Model.html#property_isLoading) + * [isLoaded](DS.Model.html#property_isLoaded) + * [isDirty](DS.Model.html#property_isDirty) + * [isSaving](DS.Model.html#property_isSaving) + * [isDeleted](DS.Model.html#property_isDeleted) + * [isNew](DS.Model.html#property_isNew) + * [isValid](DS.Model.html#property_isValid) + + @namespace DS + @class RootState + */ + + function ember$data$lib$system$model$states$$didSetProperty(record, context) { + if (context.value === context.originalValue) { + delete record._attributes[context.name]; + record.send('propertyWasReset', context.name); + } else if (context.value !== context.oldValue) { + record.send('becomeDirty'); + } + + record.updateRecordArraysLater(); + } + + // Implementation notes: + // + // Each state has a boolean value for all of the following flags: + // + // * isLoaded: The record has a populated `data` property. When a + // record is loaded via `store.find`, `isLoaded` is false + // until the adapter sets it. When a record is created locally, + // its `isLoaded` property is always true. + // * isDirty: The record has local changes that have not yet been + // saved by the adapter. This includes records that have been + // created (but not yet saved) or deleted. + // * isSaving: The record has been committed, but + // the adapter has not yet acknowledged that the changes have + // been persisted to the backend. + // * isDeleted: The record was marked for deletion. When `isDeleted` + // is true and `isDirty` is true, the record is deleted locally + // but the deletion was not yet persisted. When `isSaving` is + // true, the change is in-flight. When both `isDirty` and + // `isSaving` are false, the change has persisted. + // * isError: The adapter reported that it was unable to save + // local changes to the backend. This may also result in the + // record having its `isValid` property become false if the + // adapter reported that server-side validations failed. + // * isNew: The record was created on the client and the adapter + // did not yet report that it was successfully saved. + // * isValid: The adapter did not report any server-side validation + // failures. + + // The dirty state is a abstract state whose functionality is + // shared between the `created` and `updated` states. + // + // The deleted state shares the `isDirty` flag with the + // subclasses of `DirtyState`, but with a very different + // implementation. + // + // Dirty states have three child states: + // + // `uncommitted`: the store has not yet handed off the record + // to be saved. + // `inFlight`: the store has handed off the record to be saved, + // but the adapter has not yet acknowledged success. + // `invalid`: the record has invalid information and cannot be + // send to the adapter yet. + var ember$data$lib$system$model$states$$DirtyState = { + initialState: 'uncommitted', + + // FLAGS + isDirty: true, + + // SUBSTATES + + // When a record first becomes dirty, it is `uncommitted`. + // This means that there are local pending changes, but they + // have not yet begun to be saved, and are not invalid. + uncommitted: { + // EVENTS + didSetProperty: ember$data$lib$system$model$states$$didSetProperty, + + //TODO(Igor) reloading now triggers a + //loadingData event, though it seems fine? + loadingData: Ember.K, + + propertyWasReset: function(record, name) { + var length = Ember.keys(record._attributes).length; + var stillDirty = length > 0; + + if (!stillDirty) { record.send('rolledBack'); } + }, + + pushedData: Ember.K, + + becomeDirty: Ember.K, + + willCommit: function(record) { + record.transitionTo('inFlight'); + }, + + reloadRecord: function(record, resolve) { + resolve(ember$data$lib$system$model$states$$get(record, 'store').reloadRecord(record)); + }, + + rolledBack: function(record) { + record.transitionTo('loaded.saved'); + }, + + becameInvalid: function(record) { + record.transitionTo('invalid'); + }, + + rollback: function(record) { + record.rollback(); + } + }, + + // Once a record has been handed off to the adapter to be + // saved, it is in the 'in flight' state. Changes to the + // record cannot be made during this window. + inFlight: { + // FLAGS + isSaving: true, + + // EVENTS + didSetProperty: ember$data$lib$system$model$states$$didSetProperty, + becomeDirty: Ember.K, + pushedData: Ember.K, + + unloadRecord: function(record) { + Ember.assert("You can only unload a record which is not inFlight. `" + Ember.inspect(record) + " `", false); + }, + + // TODO: More robust semantics around save-while-in-flight + willCommit: Ember.K, + + didCommit: function(record) { + var dirtyType = ember$data$lib$system$model$states$$get(this, 'dirtyType'); + + record.transitionTo('saved'); + record.send('invokeLifecycleCallbacks', dirtyType); + }, + + becameInvalid: function(record) { + record.transitionTo('invalid'); + record.send('invokeLifecycleCallbacks'); + }, + + becameError: function(record) { + record.transitionTo('uncommitted'); + record.triggerLater('becameError', record); + } + }, + + // A record is in the `invalid` if the adapter has indicated + // the the record failed server-side invalidations. + invalid: { + // FLAGS + isValid: false, + + // EVENTS + deleteRecord: function(record) { + record.transitionTo('deleted.uncommitted'); + record.disconnectRelationships(); + }, + + didSetProperty: function(record, context) { + ember$data$lib$system$model$states$$get(record, 'errors').remove(context.name); + + ember$data$lib$system$model$states$$didSetProperty(record, context); + }, + + becomeDirty: Ember.K, + + willCommit: function(record) { + ember$data$lib$system$model$states$$get(record, 'errors').clear(); + record.transitionTo('inFlight'); + }, + + rolledBack: function(record) { + ember$data$lib$system$model$states$$get(record, 'errors').clear(); + }, + + becameValid: function(record) { + record.transitionTo('uncommitted'); + }, + + invokeLifecycleCallbacks: function(record) { + record.triggerLater('becameInvalid', record); + }, + + exit: function(record) { + record._inFlightAttributes = {}; + } + } + }; + + // The created and updated states are created outside the state + // chart so we can reopen their substates and add mixins as + // necessary. + + function ember$data$lib$system$model$states$$deepClone(object) { + var clone = {}, value; + + for (var prop in object) { + value = object[prop]; + if (value && typeof value === 'object') { + clone[prop] = ember$data$lib$system$model$states$$deepClone(value); + } else { + clone[prop] = value; + } + } + + return clone; + } + + function ember$data$lib$system$model$states$$mixin(original, hash) { + for (var prop in hash) { + original[prop] = hash[prop]; + } + + return original; + } + + function ember$data$lib$system$model$states$$dirtyState(options) { + var newState = ember$data$lib$system$model$states$$deepClone(ember$data$lib$system$model$states$$DirtyState); + return ember$data$lib$system$model$states$$mixin(newState, options); + } + + var ember$data$lib$system$model$states$$createdState = ember$data$lib$system$model$states$$dirtyState({ + dirtyType: 'created', + // FLAGS + isNew: true + }); + + ember$data$lib$system$model$states$$createdState.uncommitted.rolledBack = function(record) { + record.transitionTo('deleted.saved'); + }; + + var ember$data$lib$system$model$states$$updatedState = ember$data$lib$system$model$states$$dirtyState({ + dirtyType: 'updated' + }); + + ember$data$lib$system$model$states$$createdState.uncommitted.deleteRecord = function(record) { + record.disconnectRelationships(); + record.transitionTo('deleted.saved'); + }; + + ember$data$lib$system$model$states$$createdState.uncommitted.rollback = function(record) { + ember$data$lib$system$model$states$$DirtyState.uncommitted.rollback.apply(this, arguments); + record.transitionTo('deleted.saved'); + }; + + ember$data$lib$system$model$states$$createdState.uncommitted.propertyWasReset = Ember.K; + + function ember$data$lib$system$model$states$$assertAgainstUnloadRecord(record) { + Ember.assert("You can only unload a record which is not inFlight. `" + Ember.inspect(record) + "`", false); + } + + ember$data$lib$system$model$states$$updatedState.inFlight.unloadRecord = ember$data$lib$system$model$states$$assertAgainstUnloadRecord; + + ember$data$lib$system$model$states$$updatedState.uncommitted.deleteRecord = function(record) { + record.transitionTo('deleted.uncommitted'); + record.disconnectRelationships(); + }; + + var ember$data$lib$system$model$states$$RootState = { + // FLAGS + isEmpty: false, + isLoading: false, + isLoaded: false, + isDirty: false, + isSaving: false, + isDeleted: false, + isNew: false, + isValid: true, + + // DEFAULT EVENTS + + // Trying to roll back if you're not in the dirty state + // doesn't change your state. For example, if you're in the + // in-flight state, rolling back the record doesn't move + // you out of the in-flight state. + rolledBack: Ember.K, + unloadRecord: function(record) { + // clear relationships before moving to deleted state + // otherwise it fails + record.clearRelationships(); + record.transitionTo('deleted.saved'); + }, + + + propertyWasReset: Ember.K, + + // SUBSTATES + + // A record begins its lifecycle in the `empty` state. + // If its data will come from the adapter, it will + // transition into the `loading` state. Otherwise, if + // the record is being created on the client, it will + // transition into the `created` state. + empty: { + isEmpty: true, + + // EVENTS + loadingData: function(record, promise) { + record._loadingPromise = promise; + record.transitionTo('loading'); + }, + + loadedData: function(record) { + record.transitionTo('loaded.created.uncommitted'); + }, + + pushedData: function(record) { + record.transitionTo('loaded.saved'); + record.triggerLater('didLoad'); + } + }, + + // A record enters this state when the store asks + // the adapter for its data. It remains in this state + // until the adapter provides the requested data. + // + // Usually, this process is asynchronous, using an + // XHR to retrieve the data. + loading: { + // FLAGS + isLoading: true, + + exit: function(record) { + record._loadingPromise = null; + }, + + // EVENTS + pushedData: function(record) { + record.transitionTo('loaded.saved'); + record.triggerLater('didLoad'); + ember$data$lib$system$model$states$$set(record, 'isError', false); + }, + + becameError: function(record) { + record.triggerLater('becameError', record); + }, + + notFound: function(record) { + record.transitionTo('empty'); + } + }, + + // A record enters this state when its data is populated. + // Most of a record's lifecycle is spent inside substates + // of the `loaded` state. + loaded: { + initialState: 'saved', + + // FLAGS + isLoaded: true, + + //TODO(Igor) Reloading now triggers a loadingData event, + //but it should be ok? + loadingData: Ember.K, + + // SUBSTATES + + // If there are no local changes to a record, it remains + // in the `saved` state. + saved: { + setup: function(record) { + var attrs = record._attributes; + var isDirty = Ember.keys(attrs).length > 0; + + if (isDirty) { + record.adapterDidDirty(); + } + }, + + // EVENTS + didSetProperty: ember$data$lib$system$model$states$$didSetProperty, + + pushedData: Ember.K, + + becomeDirty: function(record) { + record.transitionTo('updated.uncommitted'); + }, + + willCommit: function(record) { + record.transitionTo('updated.inFlight'); + }, + + reloadRecord: function(record, resolve) { + resolve(ember$data$lib$system$model$states$$get(record, 'store').reloadRecord(record)); + }, + + deleteRecord: function(record) { + record.transitionTo('deleted.uncommitted'); + record.disconnectRelationships(); + }, + + unloadRecord: function(record) { + // clear relationships before moving to deleted state + // otherwise it fails + record.clearRelationships(); + record.transitionTo('deleted.saved'); + }, + + didCommit: function(record) { + record.send('invokeLifecycleCallbacks', ember$data$lib$system$model$states$$get(record, 'lastDirtyType')); + }, + + // loaded.saved.notFound would be triggered by a failed + // `reload()` on an unchanged record + notFound: Ember.K + + }, + + // A record is in this state after it has been locally + // created but before the adapter has indicated that + // it has been saved. + created: ember$data$lib$system$model$states$$createdState, + + // A record is in this state if it has already been + // saved to the server, but there are new local changes + // that have not yet been saved. + updated: ember$data$lib$system$model$states$$updatedState + }, + + // A record is in this state if it was deleted from the store. + deleted: { + initialState: 'uncommitted', + dirtyType: 'deleted', + + // FLAGS + isDeleted: true, + isLoaded: true, + isDirty: true, + + // TRANSITIONS + setup: function(record) { + record.updateRecordArrays(); + }, + + // SUBSTATES + + // When a record is deleted, it enters the `start` + // state. It will exit this state when the record + // starts to commit. + uncommitted: { + + // EVENTS + + willCommit: function(record) { + record.transitionTo('inFlight'); + }, + + rollback: function(record) { + record.rollback(); + }, + + becomeDirty: Ember.K, + deleteRecord: Ember.K, + + rolledBack: function(record) { + record.transitionTo('loaded.saved'); + } + }, + + // After a record starts committing, but + // before the adapter indicates that the deletion + // has saved to the server, a record is in the + // `inFlight` substate of `deleted`. + inFlight: { + // FLAGS + isSaving: true, + + // EVENTS + + unloadRecord: ember$data$lib$system$model$states$$assertAgainstUnloadRecord, + + // TODO: More robust semantics around save-while-in-flight + willCommit: Ember.K, + didCommit: function(record) { + record.transitionTo('saved'); + + record.send('invokeLifecycleCallbacks'); + }, + + becameError: function(record) { + record.transitionTo('uncommitted'); + record.triggerLater('becameError', record); + } + }, + + // Once the adapter indicates that the deletion has + // been saved, the record enters the `saved` substate + // of `deleted`. + saved: { + // FLAGS + isDirty: false, + + setup: function(record) { + var store = ember$data$lib$system$model$states$$get(record, 'store'); + store.dematerializeRecord(record); + }, + + invokeLifecycleCallbacks: function(record) { + record.triggerLater('didDelete', record); + record.triggerLater('didCommit', record); + }, + + willCommit: Ember.K, + + didCommit: Ember.K + } + }, + + invokeLifecycleCallbacks: function(record, dirtyType) { + if (dirtyType === 'created') { + record.triggerLater('didCreate', record); + } else { + record.triggerLater('didUpdate', record); + } + + record.triggerLater('didCommit', record); + } + }; + + function ember$data$lib$system$model$states$$wireState(object, parent, name) { + /*jshint proto:true*/ + // TODO: Use Object.create and copy instead + object = ember$data$lib$system$model$states$$mixin(parent ? Ember.create(parent) : {}, object); + object.parentState = parent; + object.stateName = name; + + for (var prop in object) { + if (!object.hasOwnProperty(prop) || prop === 'parentState' || prop === 'stateName') { continue; } + if (typeof object[prop] === 'object') { + object[prop] = ember$data$lib$system$model$states$$wireState(object[prop], object, name + "." + prop); + } + } + + return object; + } + + ember$data$lib$system$model$states$$RootState = ember$data$lib$system$model$states$$wireState(ember$data$lib$system$model$states$$RootState, null, "root"); + + var ember$data$lib$system$model$states$$default = ember$data$lib$system$model$states$$RootState; + var ember$data$lib$system$model$errors$$get = Ember.get; + var ember$data$lib$system$model$errors$$isEmpty = Ember.isEmpty; + var ember$data$lib$system$model$errors$$map = Ember.EnumerableUtils.map; + + var ember$data$lib$system$model$errors$$default = Ember.Object.extend(Ember.Enumerable, Ember.Evented, { /** - @method loadingData - @private - @param {Promise} promise + Register with target handler + + @method registerHandlers + @param {Object} target + @param {Function} becameInvalid + @param {Function} becameValid */ - loadingData: function(promise) { - this.send('loadingData', promise); + registerHandlers: function(target, becameInvalid, becameValid) { + this.on('becameInvalid', target, becameInvalid); + this.on('becameValid', target, becameValid); }, /** - @method loadedData + @property errorsByAttributeName + @type {Ember.MapWithDefault} @private */ - loadedData: function() { - this.send('loadedData'); + errorsByAttributeName: Ember.reduceComputed("content", { + initialValue: function() { + return ember$data$lib$system$map$$MapWithDefault.create({ + defaultValue: function() { + return Ember.A(); + } + }); + }, + + addedItem: function(errors, error) { + errors.get(error.attribute).pushObject(error); + + return errors; + }, + + removedItem: function(errors, error) { + errors.get(error.attribute).removeObject(error); + + return errors; + } + }), + + /** + Returns errors for a given attribute + + ```javascript + var user = store.createRecord('user', { + username: 'tomster', + email: 'invalidEmail' + }); + user.save().catch(function(){ + user.get('errors').errorsFor('email'); // returns: + // [{attribute: "email", message: "Doesn't look like a valid email."}] + }); + ``` + + @method errorsFor + @param {String} attribute + @return {Array} + */ + errorsFor: function(attribute) { + return ember$data$lib$system$model$errors$$get(this, 'errorsByAttributeName').get(attribute); }, /** - @method notFound + An array containing all of the error messages for this + record. This is useful for displaying all errors to the user. + + ```handlebars + {{#each message in errors.messages}} +
    + {{message}} +
    + {{/each}} + ``` + + @property messages + @type {Array} + */ + messages: Ember.computed.mapBy('content', 'message'), + + /** + @property content + @type {Array} @private */ - notFound: function() { - this.send('notFound'); - }, + content: Ember.computed(function() { + return Ember.A(); + }), /** - @method pushedData + @method unknownProperty @private */ - pushedData: function() { - this.send('pushedData'); + unknownProperty: function(attribute) { + var errors = this.errorsFor(attribute); + if (ember$data$lib$system$model$errors$$isEmpty(errors)) { return null; } + return errors; }, /** - Marks the record as deleted but does not save it. You must call - `save` afterwards if you want to persist it. You might use this - method if you want to allow the user to still `rollback()` a - delete after it was made. + @method nextObject + @private + */ + nextObject: function(index, previousObject, context) { + return ember$data$lib$system$model$errors$$get(this, 'content').objectAt(index); + }, - Example + /** + Total number of errors. - ```javascript - App.ModelDeleteRoute = Ember.Route.extend({ - actions: { - softDelete: function() { - this.get('model').deleteRecord(); - }, - confirm: function() { - this.get('model').save(); - }, - undo: function() { - this.get('model').rollback(); - } - } - }); - ``` + @property length + @type {Number} + @readOnly + */ + length: Ember.computed.oneWay('content.length').readOnly(), - @method deleteRecord + /** + @property isEmpty + @type {Boolean} + @readOnly */ - deleteRecord: function() { - this.send('deleteRecord'); - }, + isEmpty: Ember.computed.not('length').readOnly(), /** - Same as `deleteRecord`, but saves the record immediately. + Adds error messages to a given attribute and sends + `becameInvalid` event to the record. - Example + Example: ```javascript - App.ModelDeleteRoute = Ember.Route.extend({ - actions: { - delete: function() { - var controller = this.controller; - this.get('model').destroyRecord().then(function() { - controller.transitionToRoute('model.index'); - }); - } - } - }); + if (!user.get('username') { + user.get('errors').add('username', 'This field is required'); + } ``` - @method destroyRecord - @return {Promise} a promise that will be resolved when the adapter returns - successfully or rejected if the adapter returns with an error. + @method add + @param {String} attribute + @param {Array|String} messages */ - destroyRecord: function() { - this.deleteRecord(); - return this.save(); + add: function(attribute, messages) { + var wasEmpty = ember$data$lib$system$model$errors$$get(this, 'isEmpty'); + + messages = this._findOrCreateMessages(attribute, messages); + ember$data$lib$system$model$errors$$get(this, 'content').addObjects(messages); + + this.notifyPropertyChange(attribute); + this.enumerableContentDidChange(); + + if (wasEmpty && !ember$data$lib$system$model$errors$$get(this, 'isEmpty')) { + this.trigger('becameInvalid'); + } }, /** - @method unloadRecord + @method _findOrCreateMessages @private */ - unloadRecord: function() { - if (this.isDestroyed) { return; } + _findOrCreateMessages: function(attribute, messages) { + var errors = this.errorsFor(attribute); - this.send('unloadRecord'); + return ember$data$lib$system$model$errors$$map(Ember.makeArray(messages), function(message) { + return errors.findBy('message', message) || { + attribute: attribute, + message: message + }; + }); }, /** - @method clearRelationships - @private - */ - clearRelationships: function() { - this.eachRelationship(function(name, relationship) { - if (relationship.kind === 'belongsTo') { - set(this, name, null); - } else if (relationship.kind === 'hasMany') { - var hasMany = this._relationships[name]; - if (hasMany) { // relationships are created lazily - hasMany.destroy(); - } + Removes all error messages from the given attribute and sends + `becameValid` event to the record if there no more errors left. + + Example: + + ```javascript + App.User = DS.Model.extend({ + email: DS.attr('string'), + twoFactorAuth: DS.attr('boolean'), + phone: DS.attr('string') + }); + + App.UserEditRoute = Ember.Route.extend({ + actions: { + save: function(user) { + if (!user.get('twoFactorAuth')) { + user.get('errors').remove('phone'); + } + user.save(); + } } - }, this); - }, + }); + ``` - /** - @method updateRecordArrays - @private + @method remove + @param {String} attribute */ - updateRecordArrays: function() { - this._updatingRecordArraysLater = false; - get(this, 'store').dataWasUpdated(this.constructor, this); + remove: function(attribute) { + if (ember$data$lib$system$model$errors$$get(this, 'isEmpty')) { return; } + + var content = ember$data$lib$system$model$errors$$get(this, 'content').rejectBy('attribute', attribute); + ember$data$lib$system$model$errors$$get(this, 'content').setObjects(content); + + this.notifyPropertyChange(attribute); + this.enumerableContentDidChange(); + + if (ember$data$lib$system$model$errors$$get(this, 'isEmpty')) { + this.trigger('becameValid'); + } }, /** - Returns an object, whose keys are changed properties, and value is - an [oldProp, newProp] array. + Removes all error messages and sends `becameValid` event + to the record. - Example + Example: ```javascript - App.Mascot = DS.Model.extend({ - name: attr('string') + App.UserEditRoute = Ember.Route.extend({ + actions: { + retrySave: function(user) { + user.get('errors').clear(); + user.save(); + } + } }); - - var person = store.createRecord('person'); - person.changedAttributes(); // {} - person.set('name', 'Tomster'); - person.changedAttributes(); // {name: [undefined, 'Tomster']} ``` - @method changedAttributes - @return {Object} an object, whose keys are changed properties, - and value is an [oldProp, newProp] array. + @method clear */ - changedAttributes: function() { - var oldData = get(this, '_data'), - newData = get(this, '_attributes'), - diffData = {}, - prop; + clear: function() { + if (ember$data$lib$system$model$errors$$get(this, 'isEmpty')) { return; } - for (prop in newData) { - diffData[prop] = [oldData[prop], newData[prop]]; - } + ember$data$lib$system$model$errors$$get(this, 'content').clear(); + this.enumerableContentDidChange(); - return diffData; + this.trigger('becameValid'); }, /** - @method adapterWillCommit - @private - */ - adapterWillCommit: function() { - this.send('willCommit'); - }, + Checks if there is error messages for the given attribute. - /** - If the adapter did not return a hash in response to a commit, - merge the changed attributes and relationships into the existing - saved data. + ```javascript + App.UserEditRoute = Ember.Route.extend({ + actions: { + save: function(user) { + if (user.get('errors').has('email')) { + return alert('Please update your email before attempting to save.'); + } + user.save(); + } + } + }); + ``` - @method adapterDidCommit + @method has + @param {String} attribute + @return {Boolean} true if there some errors on given attribute */ - adapterDidCommit: function(data) { - set(this, 'isError', false); + has: function(attribute) { + return !ember$data$lib$system$model$errors$$isEmpty(this.errorsFor(attribute)); + } + }); - if (data) { - this._data = data; - } else { - Ember.mixin(this._data, this._inFlightAttributes); - } + function ember$data$lib$system$merge$$merge(original, updates) { + if (!updates || typeof updates !== 'object') { + return original; + } - this._inFlightAttributes = {}; + var props = Ember.keys(updates); + var prop; + var length = props.length; - this.send('didCommit'); - this.updateRecordArraysLater(); + for (var i = 0; i < length; i++) { + prop = props[i]; + original[prop] = updates[prop]; + } - if (!data) { return; } + return original; + } - this.suspendRelationshipObservers(function() { - this.notifyPropertyChange('data'); - }); - }, + var ember$data$lib$system$merge$$default = ember$data$lib$system$merge$$merge; + + var ember$data$lib$system$relationships$state$relationship$$forEach = Ember.EnumerableUtils.forEach; + + var ember$data$lib$system$relationships$state$relationship$$Relationship = function(store, record, inverseKey, relationshipMeta) { + this.members = new ember$data$lib$system$map$$OrderedSet(); + this.canonicalMembers = new ember$data$lib$system$map$$OrderedSet(); + this.store = store; + this.key = relationshipMeta.key; + this.inverseKey = inverseKey; + this.record = record; + this.isAsync = relationshipMeta.options.async; + this.relationshipMeta = relationshipMeta; + //This probably breaks for polymorphic relationship in complex scenarios, due to + //multiple possible typeKeys + this.inverseKeyForImplicit = this.store.modelFor(this.record.constructor).typeKey + this.key; + this.linkPromise = null; + }; - /** - @method adapterDidDirty - @private - */ - adapterDidDirty: function() { - this.send('becomeDirty'); - this.updateRecordArraysLater(); + ember$data$lib$system$relationships$state$relationship$$Relationship.prototype = { + constructor: ember$data$lib$system$relationships$state$relationship$$Relationship, + + destroy: Ember.K, + + clear: function() { + var members = this.members.list; + var member; + + while (members.length > 0){ + member = members[0]; + this.removeRecord(member); + } }, - dataDidChange: Ember.observer(function() { - this.reloadHasManys(); - }, 'data'), + disconnect: function(){ + this.members.forEach(function(member) { + this.removeRecordFromInverse(member); + }, this); + }, - reloadHasManys: function() { - var relationships = get(this.constructor, 'relationshipsByName'); - this.updateRecordArraysLater(); - relationships.forEach(function(name, relationship) { - if (this._data.links && this._data.links[name]) { return; } - if (relationship.kind === 'hasMany') { - this.hasManyDidChange(relationship.key); - } + reconnect: function(){ + this.members.forEach(function(member) { + this.addRecordToInverse(member); }, this); }, - hasManyDidChange: function(key) { - var hasMany = this._relationships[key]; + removeRecords: function(records){ + var self = this; + ember$data$lib$system$relationships$state$relationship$$forEach(records, function(record){ + self.removeRecord(record); + }); + }, - if (hasMany) { - var records = this._data[key] || []; + addRecords: function(records, idx){ + var self = this; + ember$data$lib$system$relationships$state$relationship$$forEach(records, function(record){ + self.addRecord(record, idx); + if (idx !== undefined) { + idx++; + } + }); + }, - set(hasMany, 'content', Ember.A(records)); - set(hasMany, 'isLoaded', true); - hasMany.trigger('didLoad'); + addCanonicalRecords: function(records, idx) { + for (var i=0; i -1) { + this.canonicalState.splice(i, 1); + } + this._super$removeCanonicalRecordFromOwn(record, idx); + }; - /** - Save the record and persist any changes to the record to an - extenal source via the adapter. + ember$data$lib$system$relationships$state$has_many$$ManyRelationship.prototype._super$flushCanonical = ember$data$lib$system$relationships$state$relationship$$default.prototype.flushCanonical; + ember$data$lib$system$relationships$state$has_many$$ManyRelationship.prototype.flushCanonical = function() { + this.manyArray.flushCanonical(); + this._super$flushCanonical(); + }; - Example + ember$data$lib$system$relationships$state$has_many$$ManyRelationship.prototype._super$removeRecordFromOwn = ember$data$lib$system$relationships$state$relationship$$default.prototype.removeRecordFromOwn; + ember$data$lib$system$relationships$state$has_many$$ManyRelationship.prototype.removeRecordFromOwn = function(record, idx) { + if (!this.members.has(record)) { + return; + } + this._super$removeRecordFromOwn(record, idx); + if (idx !== undefined) { + //TODO(Igor) not used currently, fix + this.manyArray.currentState.removeAt(idx); + } else { + this.manyArray.internalRemoveRecords([record]); + } + }; - ```javascript - record.set('name', 'Tomster'); - record.save().then(function(){ - // Success callback - }, function() { - // Error callback - }); - ``` - @method save - @return {Promise} a promise that will be resolved when the adapter returns - successfully or rejected if the adapter returns with an error. - */ - save: function() { - var promiseLabel = "DS: Model#save " + this; - var resolver = Ember.RSVP.defer(promiseLabel); + ember$data$lib$system$relationships$state$has_many$$ManyRelationship.prototype.notifyRecordRelationshipAdded = function(record, idx) { + var type = this.relationshipMeta.type; + Ember.assert("You cannot add '" + record.constructor.typeKey + "' records to the " + this.record.constructor.typeKey + "." + this.key + " relationship (only '" + this.belongsToType.typeKey + "' allowed)", (function () { + if (record instanceof type) { + return true; + } else if (Ember.MODEL_FACTORY_INJECTIONS) { + return record instanceof type.superclass; + } - this.get('store').scheduleSave(this, resolver); - this._inFlightAttributes = this._attributes; - this._attributes = {}; + return false; + })()); - return DS.PromiseObject.create({ promise: resolver.promise }); - }, + this.record.notifyHasManyAdded(this.key, record, idx); + }; - /** - Reload the record from the adapter. + ember$data$lib$system$relationships$state$has_many$$ManyRelationship.prototype.reload = function() { + var self = this; + if (this.link) { + return this.fetchLink(); + } else { + return this.store.scheduleFetchMany(this.manyArray.toArray()).then(function() { + //Goes away after the manyArray refactor + self.manyArray.set('isLoaded', true); + return self.manyArray; + }); + } + }; - This will only work if the record has already finished loading - and has not yet been modified (`isLoaded` but not `isDirty`, - or `isSaving`). + ember$data$lib$system$relationships$state$has_many$$ManyRelationship.prototype.computeChanges = function(records) { + var members = this.canonicalMembers; + var recordsToRemove = []; + var length; + var record; + var i; - Example + records = ember$data$lib$system$relationships$state$has_many$$setForArray(records); - ```javascript - App.ModelViewRoute = Ember.Route.extend({ - actions: { - reload: function() { - this.get('model').reload(); - } - } - }); - ``` + members.forEach(function(member) { + if (records.has(member)) return; - @method reload - @return {Promise} a promise that will be resolved with the record when the - adapter returns successfully or rejected if the adapter returns - with an error. - */ - reload: function() { - set(this, 'isReloading', true); + recordsToRemove.push(member); + }); - var record = this; + this.removeCanonicalRecords(recordsToRemove); - var promiseLabel = "DS: Model#reload of " + this; - var promise = new Promise(function(resolve){ - record.send('reloadRecord', resolve); - }, promiseLabel).then(function() { - record.set('isReloading', false); - record.set('isError', false); - return record; - }, function(reason) { - record.set('isError', true); - throw reason; - }, "DS: Model#reload complete, update flags"); + var hasManyArray = this.manyArray; - return DS.PromiseObject.create({ promise: promise }); - }, + // Using records.toArray() since currently using + // removeRecord can modify length, messing stuff up + // forEach since it directly looks at "length" each + // iteration + records = records.toArray(); + length = records.length; + for (i = 0; i < length; i++){ + record = records[i]; + //Need to preserve the order of incoming records + if (hasManyArray.objectAt(i) === record ) { + continue; + } + this.removeCanonicalRecord(record); + this.addCanonicalRecord(record, i); + } + }; - // FOR USE DURING COMMIT PROCESS + ember$data$lib$system$relationships$state$has_many$$ManyRelationship.prototype.fetchLink = function() { + var self = this; + return this.store.findHasMany(this.record, this.link, this.relationshipMeta).then(function(records){ + self.updateRecordsFromAdapter(records); + return self.manyArray; + }); + }; - adapterDidUpdateAttribute: function(attributeName, value) { + ember$data$lib$system$relationships$state$has_many$$ManyRelationship.prototype.findRecords = function() { + var manyArray = this.manyArray; + return this.store.findMany(manyArray.toArray()).then(function(){ + //Goes away after the manyArray refactor + manyArray.set('isLoaded', true); + return manyArray; + }); + }; + ember$data$lib$system$relationships$state$has_many$$ManyRelationship.prototype.notifyHasManyChanged = function() { + this.record.notifyHasManyAdded(this.key); + }; - // If a value is passed in, update the internal attributes and clear - // the attribute cache so it picks up the new value. Otherwise, - // collapse the current value into the internal attributes because - // the adapter has acknowledged it. - if (value !== undefined) { - this._data[attributeName] = value; - this.notifyPropertyChange(attributeName); + ember$data$lib$system$relationships$state$has_many$$ManyRelationship.prototype.getRecords = function() { + //TODO(Igor) sync server here, once our syncing is not stupid + if (this.isAsync) { + var self = this; + var promise; + if (this.link) { + promise = this.findLink().then(function() { + return self.findRecords(); + }); } else { - this._data[attributeName] = this._inFlightAttributes[attributeName]; + promise = this.findRecords(); } + return ember$data$lib$system$promise_proxies$$PromiseManyArray.create({ + content: this.manyArray, + promise: promise + }); + } else { + Ember.assert("You looked up the '" + this.key + "' relationship on a '" + this.record.constructor.typeKey + "' with id " + this.record.get('id') + " but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.hasMany({ async: true })`)", this.manyArray.isEvery('isEmpty', false)); - this.updateRecordArraysLater(); - }, + //TODO(Igor) WTF DO I DO HERE? + if (!this.manyArray.get('isDestroyed')) { + this.manyArray.set('isLoaded', true); + } + return this.manyArray; + } + }; - /** - @method adapterDidInvalidate - @private - */ - adapterDidInvalidate: function(errors) { - var recordErrors = get(this, 'errors'); - function addError(name) { - if (errors[name]) { - recordErrors.add(name, errors[name]); - } + function ember$data$lib$system$relationships$state$has_many$$setForArray(array) { + var set = new ember$data$lib$system$map$$OrderedSet(); + + if (array) { + for (var i=0, l=array.length; i "root.created.uncommitted" - ``` + if (inverse) { + inverseKey = inverse.name; + } - The hierarchy of valid states that ship with ember data looks like - this: + if (relationshipMeta.kind === 'hasMany'){ + return new ember$data$lib$system$relationships$state$has_many$$default(store, record, inverseKey, relationshipMeta); + } + else { + return new ember$data$lib$system$relationships$state$belongs_to$$default(store, record, inverseKey, relationshipMeta); + } + }; - ```text - * root - * deleted - * saved - * uncommitted - * inFlight - * empty - * loaded - * created - * uncommitted - * inFlight - * saved - * updated - * uncommitted - * inFlight - * loading - ``` + var ember$data$lib$system$relationships$state$create$$default = ember$data$lib$system$relationships$state$create$$createRelationshipFor; - The `DS.Model` states are themselves stateless. What we mean is - that, the hierarchical states that each of *those* points to is a - shared data structure. For performance reasons, instead of each - record getting its own copy of the hierarchy of states, each record - points to this global, immutable shared instance. How does a state - know which record it should be acting on? We pass the record - instance into the state's event handlers as the first argument. + /** + @module ember-data + */ - The record passed as the first parameter is where you should stash - state about the record if needed; you should never store data on the state - object itself. + var ember$data$lib$system$model$model$$get = Ember.get; + var ember$data$lib$system$model$model$$set = Ember.set; + var ember$data$lib$system$model$model$$Promise = Ember.RSVP.Promise; + var ember$data$lib$system$model$model$$forEach = Ember.ArrayPolyfills.forEach; + var ember$data$lib$system$model$model$$map = Ember.ArrayPolyfills.map; - ### Events and Flags + var ember$data$lib$system$model$model$$retrieveFromCurrentState = Ember.computed('currentState', function(key, value) { + return ember$data$lib$system$model$model$$get(ember$data$lib$system$model$model$$get(this, 'currentState'), key); + }).readOnly(); - A state may implement zero or more events and flags. + var ember$data$lib$system$model$model$$_extractPivotNameCache = Ember.create(null); + var ember$data$lib$system$model$model$$_splitOnDotCache = Ember.create(null); - #### Events + function ember$data$lib$system$model$model$$splitOnDot(name) { + return ember$data$lib$system$model$model$$_splitOnDotCache[name] || ( + (ember$data$lib$system$model$model$$_splitOnDotCache[name] = name.split('.')) + ); + } - Events are named functions that are invoked when sent to a record. The - record will first look for a method with the given name on the - current state. If no method is found, it will search the current - state's parent, and then its grandparent, and so on until reaching - the top of the hierarchy. If the root is reached without an event - handler being found, an exception will be raised. This can be very - helpful when debugging new features. + function ember$data$lib$system$model$model$$extractPivotName(name) { + return ember$data$lib$system$model$model$$_extractPivotNameCache[name] || ( + (ember$data$lib$system$model$model$$_extractPivotNameCache[name] = ember$data$lib$system$model$model$$splitOnDot(name)[0]) + ); + } + + /** + + The model class that all Ember Data records descend from. + + @class Model + @namespace DS + @extends Ember.Object + @uses Ember.Evented + */ + var ember$data$lib$system$model$model$$Model = Ember.Object.extend(Ember.Evented, { + _recordArrays: undefined, + _relationships: undefined, + _loadingRecordArrays: undefined, + /** + If this property is `true` the record is in the `empty` + state. Empty is the first state all records enter after they have + been created. Most records created by the store will quickly + transition to the `loading` state if data needs to be fetched from + the server or the `created` state if the record is created on the + client. A record can also enter the empty state if the adapter is + unable to locate the record. + + @property isEmpty + @type {Boolean} + @readOnly + */ + isEmpty: ember$data$lib$system$model$model$$retrieveFromCurrentState, + /** + If this property is `true` the record is in the `loading` state. A + record enters this state when the store asks the adapter for its + data. It remains in this state until the adapter provides the + requested data. - Here's an example implementation of a state with a `myEvent` event handler: + @property isLoading + @type {Boolean} + @readOnly + */ + isLoading: ember$data$lib$system$model$model$$retrieveFromCurrentState, + /** + If this property is `true` the record is in the `loaded` state. A + record enters this state when its data is populated. Most of a + record's lifecycle is spent inside substates of the `loaded` + state. - ```javascript - aState: DS.State.create({ - myEvent: function(manager, param) { - console.log("Received myEvent with", param); - } - }) - ``` + Example - To trigger this event: + ```javascript + var record = store.createRecord('model'); + record.get('isLoaded'); // true - ```javascript - record.send('myEvent', 'foo'); - //=> "Received myEvent with foo" - ``` + store.find('model', 1).then(function(model) { + model.get('isLoaded'); // true + }); + ``` - Note that an optional parameter can be sent to a record's `send()` method, - which will be passed as the second parameter to the event handler. + @property isLoaded + @type {Boolean} + @readOnly + */ + isLoaded: ember$data$lib$system$model$model$$retrieveFromCurrentState, + /** + If this property is `true` the record is in the `dirty` state. The + record has local changes that have not yet been saved by the + adapter. This includes records that have been created (but not yet + saved) or deleted. - Events should transition to a different state if appropriate. This can be - done by calling the record's `transitionTo()` method with a path to the - desired state. The state manager will attempt to resolve the state path - relative to the current state. If no state is found at that path, it will - attempt to resolve it relative to the current state's parent, and then its - parent, and so on until the root is reached. For example, imagine a hierarchy - like this: + Example - * created - * uncommitted <-- currentState - * inFlight - * updated - * inFlight + ```javascript + var record = store.createRecord('model'); + record.get('isDirty'); // true - If we are currently in the `uncommitted` state, calling - `transitionTo('inFlight')` would transition to the `created.inFlight` state, - while calling `transitionTo('updated.inFlight')` would transition to - the `updated.inFlight` state. + store.find('model', 1).then(function(model) { + model.get('isDirty'); // false + model.set('foo', 'some value'); + model.get('isDirty'); // true + }); + ``` - Remember that *only events* should ever cause a state transition. You should - never call `transitionTo()` from outside a state's event handler. If you are - tempted to do so, create a new event and send that to the state manager. + @property isDirty + @type {Boolean} + @readOnly + */ + isDirty: ember$data$lib$system$model$model$$retrieveFromCurrentState, + /** + If this property is `true` the record is in the `saving` state. A + record enters the saving state when `save` is called, but the + adapter has not yet acknowledged that the changes have been + persisted to the backend. - #### Flags + Example - Flags are Boolean values that can be used to introspect a record's current - state in a more user-friendly way than examining its state path. For example, - instead of doing this: + ```javascript + var record = store.createRecord('model'); + record.get('isSaving'); // false + var promise = record.save(); + record.get('isSaving'); // true + promise.then(function() { + record.get('isSaving'); // false + }); + ``` - ```javascript - var statePath = record.get('stateManager.currentPath'); - if (statePath === 'created.inFlight') { - doSomething(); - } - ``` + @property isSaving + @type {Boolean} + @readOnly + */ + isSaving: ember$data$lib$system$model$model$$retrieveFromCurrentState, + /** + If this property is `true` the record is in the `deleted` state + and has been marked for deletion. When `isDeleted` is true and + `isDirty` is true, the record is deleted locally but the deletion + was not yet persisted. When `isSaving` is true, the change is + in-flight. When both `isDirty` and `isSaving` are false, the + change has persisted. - You can say: + Example - ```javascript - if (record.get('isNew') && record.get('isSaving')) { - doSomething(); - } - ``` + ```javascript + var record = store.createRecord('model'); + record.get('isDeleted'); // false + record.deleteRecord(); - If your state does not set a value for a given flag, the value will - be inherited from its parent (or the first place in the state hierarchy - where it is defined). + // Locally deleted + record.get('isDeleted'); // true + record.get('isDirty'); // true + record.get('isSaving'); // false - The current set of flags are defined below. If you want to add a new flag, - in addition to the area below, you will also need to declare it in the - `DS.Model` class. + // Persisting the deletion + var promise = record.save(); + record.get('isDeleted'); // true + record.get('isSaving'); // true + // Deletion Persisted + promise.then(function() { + record.get('isDeleted'); // true + record.get('isSaving'); // false + record.get('isDirty'); // false + }); + ``` - * [isEmpty](DS.Model.html#property_isEmpty) - * [isLoading](DS.Model.html#property_isLoading) - * [isLoaded](DS.Model.html#property_isLoaded) - * [isDirty](DS.Model.html#property_isDirty) - * [isSaving](DS.Model.html#property_isSaving) - * [isDeleted](DS.Model.html#property_isDeleted) - * [isNew](DS.Model.html#property_isNew) - * [isValid](DS.Model.html#property_isValid) + @property isDeleted + @type {Boolean} + @readOnly + */ + isDeleted: ember$data$lib$system$model$model$$retrieveFromCurrentState, + /** + If this property is `true` the record is in the `new` state. A + record will be in the `new` state when it has been created on the + client and the adapter has not yet report that it was successfully + saved. - @namespace DS - @class RootState - */ + Example - function hasDefinedProperties(object) { - // Ignore internal property defined by simulated `Ember.create`. - var names = Ember.keys(object); - var i, l, name; - for (i = 0, l = names.length; i < l; i++ ) { - name = names[i]; - if (object.hasOwnProperty(name) && object[name]) { return true; } - } + ```javascript + var record = store.createRecord('model'); + record.get('isNew'); // true - return false; - } + record.save().then(function(model) { + model.get('isNew'); // false + }); + ``` - function didSetProperty(record, context) { - if (context.value === context.originalValue) { - delete record._attributes[context.name]; - record.send('propertyWasReset', context.name); - } else if (context.value !== context.oldValue) { - record.send('becomeDirty'); - } + @property isNew + @type {Boolean} + @readOnly + */ + isNew: ember$data$lib$system$model$model$$retrieveFromCurrentState, + /** + If this property is `true` the record is in the `valid` state. - record.updateRecordArraysLater(); - } + A record will be in the `valid` state when the adapter did not report any + server-side validation failures. - // Implementation notes: - // - // Each state has a boolean value for all of the following flags: - // - // * isLoaded: The record has a populated `data` property. When a - // record is loaded via `store.find`, `isLoaded` is false - // until the adapter sets it. When a record is created locally, - // its `isLoaded` property is always true. - // * isDirty: The record has local changes that have not yet been - // saved by the adapter. This includes records that have been - // created (but not yet saved) or deleted. - // * isSaving: The record has been committed, but - // the adapter has not yet acknowledged that the changes have - // been persisted to the backend. - // * isDeleted: The record was marked for deletion. When `isDeleted` - // is true and `isDirty` is true, the record is deleted locally - // but the deletion was not yet persisted. When `isSaving` is - // true, the change is in-flight. When both `isDirty` and - // `isSaving` are false, the change has persisted. - // * isError: The adapter reported that it was unable to save - // local changes to the backend. This may also result in the - // record having its `isValid` property become false if the - // adapter reported that server-side validations failed. - // * isNew: The record was created on the client and the adapter - // did not yet report that it was successfully saved. - // * isValid: No client-side validations have failed and the - // adapter did not report any server-side validation failures. + @property isValid + @type {Boolean} + @readOnly + */ + isValid: ember$data$lib$system$model$model$$retrieveFromCurrentState, + /** + If the record is in the dirty state this property will report what + kind of change has caused it to move into the dirty + state. Possible values are: - // The dirty state is a abstract state whose functionality is - // shared between the `created` and `updated` states. - // - // The deleted state shares the `isDirty` flag with the - // subclasses of `DirtyState`, but with a very different - // implementation. - // - // Dirty states have three child states: - // - // `uncommitted`: the store has not yet handed off the record - // to be saved. - // `inFlight`: the store has handed off the record to be saved, - // but the adapter has not yet acknowledged success. - // `invalid`: the record has invalid information and cannot be - // send to the adapter yet. - var DirtyState = { - initialState: 'uncommitted', + - `created` The record has been created by the client and not yet saved to the adapter. + - `updated` The record has been updated by the client and not yet saved to the adapter. + - `deleted` The record has been deleted by the client and not yet saved to the adapter. - // FLAGS - isDirty: true, + Example - // SUBSTATES + ```javascript + var record = store.createRecord('model'); + record.get('dirtyType'); // 'created' + ``` - // When a record first becomes dirty, it is `uncommitted`. - // This means that there are local pending changes, but they - // have not yet begun to be saved, and are not invalid. - uncommitted: { - // EVENTS - didSetProperty: didSetProperty, + @property dirtyType + @type {String} + @readOnly + */ + dirtyType: ember$data$lib$system$model$model$$retrieveFromCurrentState, - propertyWasReset: function(record, name) { - var stillDirty = false; + /** + If `true` the adapter reported that it was unable to save local + changes to the backend for any reason other than a server-side + validation error. - for (var prop in record._attributes) { - stillDirty = true; - break; - } + Example - if (!stillDirty) { record.send('rolledBack'); } - }, + ```javascript + record.get('isError'); // false + record.set('foo', 'valid value'); + record.save().then(null, function() { + record.get('isError'); // true + }); + ``` + + @property isError + @type {Boolean} + @readOnly + */ + isError: false, + /** + If `true` the store is attempting to reload the record form the adapter. - pushedData: Ember.K, + Example - becomeDirty: Ember.K, + ```javascript + record.get('isReloading'); // false + record.reload(); + record.get('isReloading'); // true + ``` - willCommit: function(record) { - record.transitionTo('inFlight'); - }, + @property isReloading + @type {Boolean} + @readOnly + */ + isReloading: false, - reloadRecord: function(record, resolve) { - resolve(get(record, 'store').reloadRecord(record)); - }, + /** + The `clientId` property is a transient numerical identifier + generated at runtime by the data store. It is important + primarily because newly created objects may not yet have an + externally generated id. - rolledBack: function(record) { - record.transitionTo('loaded.saved'); - }, + @property clientId + @private + @type {Number|String} + */ + clientId: null, + /** + All ember models have an id property. This is an identifier + managed by an external source. These are always coerced to be + strings before being used internally. Note when declaring the + attributes for a model it is an error to declare an id + attribute. - becameInvalid: function(record) { - record.transitionTo('invalid'); - }, + ```javascript + var record = store.createRecord('model'); + record.get('id'); // null - rollback: function(record) { - record.rollback(); - } - }, + store.find('model', 1).then(function(model) { + model.get('id'); // '1' + }); + ``` - // Once a record has been handed off to the adapter to be - // saved, it is in the 'in flight' state. Changes to the - // record cannot be made during this window. - inFlight: { - // FLAGS - isSaving: true, + @property id + @type {String} + */ + id: null, - // EVENTS - didSetProperty: didSetProperty, - becomeDirty: Ember.K, - pushedData: Ember.K, + /** + @property currentState + @private + @type {Object} + */ + currentState: ember$data$lib$system$model$states$$default.empty, - unloadRecord: function(record) { - Ember.assert("You can only unload a record which is not inFlight. `" + Ember.inspect(record) + " `", false); - }, + /** + When the record is in the `invalid` state this object will contain + any errors returned by the adapter. When present the errors hash + typically contains keys corresponding to the invalid property names + and values which are an array of error messages. - // TODO: More robust semantics around save-while-in-flight - willCommit: Ember.K, + ```javascript + record.get('errors.length'); // 0 + record.set('foo', 'invalid value'); + record.save().then(null, function() { + record.get('errors').get('foo'); // ['foo should be a number.'] + }); + ``` - didCommit: function(record) { - var dirtyType = get(this, 'dirtyType'); + @property errors + @type {DS.Errors} + */ + errors: Ember.computed(function() { + var errors = ember$data$lib$system$model$errors$$default.create(); - record.transitionTo('saved'); - record.send('invokeLifecycleCallbacks', dirtyType); - }, + errors.registerHandlers(this, function() { + this.send('becameInvalid'); + }, function() { + this.send('becameValid'); + }); - becameInvalid: function(record) { - record.transitionTo('invalid'); - record.send('invokeLifecycleCallbacks'); - }, + return errors; + }).readOnly(), - becameError: function(record) { - record.transitionTo('uncommitted'); - record.triggerLater('becameError', record); - } - }, + /** + Create a JSON representation of the record, using the serialization + strategy of the store's adapter. - // A record is in the `invalid` state when its client-side - // invalidations have failed, or if the adapter has indicated - // the the record failed server-side invalidations. - invalid: { - // FLAGS - isValid: false, + `serialize` takes an optional hash as a parameter, currently + supported options are: - // EVENTS - deleteRecord: function(record) { - record.transitionTo('deleted.uncommitted'); - record.clearRelationships(); - }, + - `includeId`: `true` if the record's ID should be included in the + JSON representation. - didSetProperty: function(record, context) { - get(record, 'errors').remove(context.name); + @method serialize + @param {Object} options + @return {Object} an object whose values are primitive JSON values only + */ + serialize: function(options) { + var store = ember$data$lib$system$model$model$$get(this, 'store'); + return store.serialize(this, options); + }, - didSetProperty(record, context); - }, + /** + Use [DS.JSONSerializer](DS.JSONSerializer.html) to + get the JSON representation of a record. - becomeDirty: Ember.K, + `toJSON` takes an optional hash as a parameter, currently + supported options are: - rolledBack: function(record) { - get(record, 'errors').clear(); - }, + - `includeId`: `true` if the record's ID should be included in the + JSON representation. - becameValid: function(record) { - record.transitionTo('uncommitted'); - }, + @method toJSON + @param {Object} options + @return {Object} A JSON representation of the object. + */ + toJSON: function(options) { + // container is for lazy transform lookups + var serializer = ember$data$lib$serializers$json_serializer$$default.create({ container: this.container }); + return serializer.serialize(this, options); + }, - invokeLifecycleCallbacks: function(record) { - record.triggerLater('becameInvalid', record); - } - } - }; + /** + Fired when the record is loaded from the server. - // The created and updated states are created outside the state - // chart so we can reopen their substates and add mixins as - // necessary. + @event didLoad + */ + didLoad: Ember.K, - function deepClone(object) { - var clone = {}, value; + /** + Fired when the record is updated. - for (var prop in object) { - value = object[prop]; - if (value && typeof value === 'object') { - clone[prop] = deepClone(value); - } else { - clone[prop] = value; - } - } + @event didUpdate + */ + didUpdate: Ember.K, - return clone; - } + /** + Fired when the record is created. - function mixin(original, hash) { - for (var prop in hash) { - original[prop] = hash[prop]; - } + @event didCreate + */ + didCreate: Ember.K, - return original; - } + /** + Fired when the record is deleted. - function dirtyState(options) { - var newState = deepClone(DirtyState); - return mixin(newState, options); - } + @event didDelete + */ + didDelete: Ember.K, - var createdState = dirtyState({ - dirtyType: 'created', - // FLAGS - isNew: true - }); + /** + Fired when the record becomes invalid. - createdState.uncommitted.rolledBack = function(record) { - record.transitionTo('deleted.saved'); - }; + @event becameInvalid + */ + becameInvalid: Ember.K, - var updatedState = dirtyState({ - dirtyType: 'updated' - }); + /** + Fired when the record enters the error state. - createdState.uncommitted.deleteRecord = function(record) { - record.clearRelationships(); - record.transitionTo('deleted.saved'); - }; + @event becameError + */ + becameError: Ember.K, - createdState.uncommitted.rollback = function(record) { - DirtyState.uncommitted.rollback.apply(this, arguments); - record.transitionTo('deleted.saved'); - }; + /** + @property data + @private + @type {Object} + */ + data: Ember.computed(function() { + this._data = this._data || {}; + return this._data; + }).readOnly(), - createdState.uncommitted.propertyWasReset = Ember.K; + _data: null, - function assertAgainstUnloadRecord(record) { - Ember.assert("You can only unload a record which is not inFlight. `" + Ember.inspect(record) + "`", false); - } + init: function() { + this._super(); + this._setup(); + }, - updatedState.inFlight.unloadRecord = assertAgainstUnloadRecord; + _setup: function() { + this._changesToSync = {}; + this._deferredTriggers = []; + this._data = {}; + this._attributes = Ember.create(null); + this._inFlightAttributes = Ember.create(null); + this._relationships = {}; + /* + implicit relationships are relationship which have not been declared but the inverse side exists on + another record somewhere + For example if there was + ``` + App.Comment = DS.Model.extend({ + name: DS.attr() + }) + ``` + but there is also + ``` + App.Post = DS.Model.extend({ + name: DS.attr(), + comments: DS.hasMany('comment') + }) + ``` + + would have a implicit post relationship in order to be do things like remove ourselves from the post + when we are deleted + */ + this._implicitRelationships = Ember.create(null); + var model = this; + //TODO Move into a getter for better perf + this.constructor.eachRelationship(function(key, descriptor) { + model._relationships[key] = ember$data$lib$system$relationships$state$create$$default(model, descriptor, model.store); + }); - updatedState.uncommitted.deleteRecord = function(record) { - record.transitionTo('deleted.uncommitted'); - record.clearRelationships(); - }; + }, - var RootState = { - // FLAGS - isEmpty: false, - isLoading: false, - isLoaded: false, - isDirty: false, - isSaving: false, - isDeleted: false, - isNew: false, - isValid: true, + /** + @method send + @private + @param {String} name + @param {Object} context + */ + send: function(name, context) { + var currentState = ember$data$lib$system$model$model$$get(this, 'currentState'); - // DEFAULT EVENTS + if (!currentState[name]) { + this._unhandledEvent(currentState, name, context); + } - // Trying to roll back if you're not in the dirty state - // doesn't change your state. For example, if you're in the - // in-flight state, rolling back the record doesn't move - // you out of the in-flight state. - rolledBack: Ember.K, - unloadRecord: function(record) { - // clear relationships before moving to deleted state - // otherwise it fails - record.clearRelationships(); - record.transitionTo('deleted.saved'); + return currentState[name](this, context); }, + /** + @method transitionTo + @private + @param {String} name + */ + transitionTo: function(name) { + // POSSIBLE TODO: Remove this code and replace with + // always having direct references to state objects - propertyWasReset: Ember.K, + var pivotName = ember$data$lib$system$model$model$$extractPivotName(name); + var currentState = ember$data$lib$system$model$model$$get(this, 'currentState'); + var state = currentState; - // SUBSTATES + do { + if (state.exit) { state.exit(this); } + state = state.parentState; + } while (!state.hasOwnProperty(pivotName)); - // A record begins its lifecycle in the `empty` state. - // If its data will come from the adapter, it will - // transition into the `loading` state. Otherwise, if - // the record is being created on the client, it will - // transition into the `created` state. - empty: { - isEmpty: true, + var path = ember$data$lib$system$model$model$$splitOnDot(name); + var setups = [], enters = [], i, l; - // EVENTS - loadingData: function(record, promise) { - record._loadingPromise = promise; - record.transitionTo('loading'); - }, + for (i=0, l=path.length; i')` from " + this.toString(), name !== 'id'); - for (var i = 0; i < length; i++) { - result = result.concat(list[i]); - } + meta.name = name; + map.set(name, meta); + } + }); - return result; - } + return map; + }).readOnly(), - __exports__["default"] = RecordArrayManager; - }); -define("ember-data/lib/system/record_arrays", - ["./record_arrays/record_array","./record_arrays/filtered_record_array","./record_arrays/adapter_populated_record_array","./record_arrays/many_array","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - /** - @module ember-data - */ + /** + A map whose keys are the attributes of the model (properties + described by DS.attr) and whose values are type of transformation + applied to each attribute. This map does not include any + attributes that do not have an transformation type. - var RecordArray = __dependency1__["default"]; - var FilteredRecordArray = __dependency2__["default"]; - var AdapterPopulatedRecordArray = __dependency3__["default"]; - var ManyArray = __dependency4__["default"]; - - __exports__.RecordArray = RecordArray; - __exports__.FilteredRecordArray = FilteredRecordArray; - __exports__.AdapterPopulatedRecordArray = AdapterPopulatedRecordArray; - __exports__.ManyArray = ManyArray; - }); -define("ember-data/lib/system/record_arrays/adapter_populated_record_array", - ["./record_array","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var RecordArray = __dependency1__["default"]; - /** - @module ember-data - */ + Example - var get = Ember.get, set = Ember.set; + ```javascript + App.Person = DS.Model.extend({ + firstName: attr(), + lastName: attr('string'), + birthday: attr('date') + }); - /** - Represents an ordered list of records whose order and membership is - determined by the adapter. For example, a query sent to the adapter - may trigger a search on the server, whose results would be loaded - into an instance of the `AdapterPopulatedRecordArray`. + var transformedAttributes = Ember.get(App.Person, 'transformedAttributes') - @class AdapterPopulatedRecordArray - @namespace DS - @extends DS.RecordArray - */ - var AdapterPopulatedRecordArray = RecordArray.extend({ - query: null, + transformedAttributes.forEach(function(field, type) { + console.log(field, type); + }); - replace: function() { - var type = get(this, 'type').toString(); - throw new Error("The result of a server query (on " + type + ") is immutable."); - }, + // prints: + // lastName string + // birthday date + ``` - /** - @method load - @private - @param {Array} data + @property transformedAttributes + @static + @type {Ember.Map} + @readOnly */ - load: function(data) { - var store = get(this, 'store'), - type = get(this, 'type'), - records = store.pushMany(type, data), - meta = store.metadataFor(type); + transformedAttributes: Ember.computed(function() { + var map = ember$data$lib$system$map$$Map.create(); - this.setProperties({ - content: Ember.A(records), - isLoaded: true, - meta: meta + this.eachAttribute(function(key, meta) { + if (meta.type) { + map.set(key, meta.type); + } }); - // TODO: should triggering didLoad event be the last action of the runLoop? - Ember.run.once(this, 'trigger', 'didLoad'); - } - }); + return map; + }).readOnly(), - __exports__["default"] = AdapterPopulatedRecordArray; - }); -define("ember-data/lib/system/record_arrays/filtered_record_array", - ["./record_array","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var RecordArray = __dependency1__["default"]; + /** + Iterates through the attributes of the model, calling the passed function on each + attribute. - /** - @module ember-data - */ + The callback method you provide should have the following signature (all + parameters are optional): - var get = Ember.get; + ```javascript + function(name, meta); + ``` - /** - Represents a list of records whose membership is determined by the - store. As records are created, loaded, or modified, the store - evaluates them to determine if they should be part of the record - array. + - `name` the name of the current property in the iteration + - `meta` the meta object for the attribute property in the iteration - @class FilteredRecordArray - @namespace DS - @extends DS.RecordArray - */ - var FilteredRecordArray = RecordArray.extend({ - /** - The filterFunction is a function used to test records from the store to - determine if they should be part of the record array. + Note that in addition to a callback, you can also pass an optional target + object that will be set as `this` on the context. Example ```javascript - var allPeople = store.all('person'); - allPeople.mapBy('name'); // ["Tom Dale", "Yehuda Katz", "Trek Glowacki"] + App.Person = DS.Model.extend({ + firstName: attr('string'), + lastName: attr('string'), + birthday: attr('date') + }); - var people = store.filter('person', function(person) { - if (person.get('name').match(/Katz$/)) { return true; } + App.Person.eachAttribute(function(name, meta) { + console.log(name, meta); }); - people.mapBy('name'); // ["Yehuda Katz"] - var notKatzFilter = function(person) { - return !person.get('name').match(/Katz$/); - }; - people.set('filterFunction', notKatzFilter); - people.mapBy('name'); // ["Tom Dale", "Trek Glowacki"] - ``` + // prints: + // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"} + // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"} + // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"} + ``` - @method filterFunction - @param {DS.Model} record - @return {Boolean} `true` if the record should be in the array + @method eachAttribute + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @static */ - filterFunction: null, - isLoaded: true, - - replace: function() { - var type = get(this, 'type').toString(); - throw new Error("The result of a client-side filter (on " + type + ") is immutable."); + eachAttribute: function(callback, binding) { + ember$data$lib$system$model$attributes$$get(this, 'attributes').forEach(function(meta, name) { + callback.call(binding, name, meta); + }, binding); }, /** - @method updateFilter - @private - */ - updateFilter: Ember.observer(function() { - var manager = get(this, 'manager'); - manager.updateFilter(this, get(this, 'type'), get(this, 'filterFunction')); - }, 'filterFunction') - }); + Iterates through the transformedAttributes of the model, calling + the passed function on each attribute. Note the callback will not be + called for any attributes that do not have an transformation type. - __exports__["default"] = FilteredRecordArray; - }); -define("ember-data/lib/system/record_arrays/many_array", - ["./record_array","../changes","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var RecordArray = __dependency1__["default"]; - var RelationshipChange = __dependency2__.RelationshipChange; + The callback method you provide should have the following signature (all + parameters are optional): - /** - @module ember-data - */ + ```javascript + function(name, type); + ``` - var get = Ember.get, set = Ember.set; - var map = Ember.EnumerableUtils.map; + - `name` the name of the current property in the iteration + - `type` a string containing the name of the type of transformed + applied to the attribute - function sync(change) { - change.sync(); - } + Note that in addition to a callback, you can also pass an optional target + object that will be set as `this` on the context. - /** - A `ManyArray` is a `RecordArray` that represents the contents of a has-many - relationship. + Example - The `ManyArray` is instantiated lazily the first time the relationship is - requested. + ```javascript + App.Person = DS.Model.extend({ + firstName: attr(), + lastName: attr('string'), + birthday: attr('date') + }); - ### Inverses + App.Person.eachTransformedAttribute(function(name, type) { + console.log(name, type); + }); - Often, the relationships in Ember Data applications will have - an inverse. For example, imagine the following models are - defined: + // prints: + // lastName string + // birthday date + ``` - ```javascript - App.Post = DS.Model.extend({ - comments: DS.hasMany('comment') - }); + @method eachTransformedAttribute + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @static + */ + eachTransformedAttribute: function(callback, binding) { + ember$data$lib$system$model$attributes$$get(this, 'transformedAttributes').forEach(function(type, name) { + callback.call(binding, name, type); + }); + } + }); - App.Comment = DS.Model.extend({ - post: DS.belongsTo('post') - }); - ``` - If you created a new instance of `App.Post` and added - a `App.Comment` record to its `comments` has-many - relationship, you would expect the comment's `post` - property to be set to the post that contained - the has-many. + ember$data$lib$system$model$model$$default.reopen({ + eachAttribute: function(callback, binding) { + this.constructor.eachAttribute(callback, binding); + } + }); - We call the record to which a relationship belongs the - relationship's _owner_. + function ember$data$lib$system$model$attributes$$getDefaultValue(record, options, key) { + if (typeof options.defaultValue === "function") { + return options.defaultValue.apply(null, arguments); + } else { + return options.defaultValue; + } + } - @class ManyArray - @namespace DS - @extends DS.RecordArray - */ - var ManyArray = RecordArray.extend({ - init: function() { - this._super.apply(this, arguments); - this._changesToSync = Ember.OrderedSet.create(); - }, + function ember$data$lib$system$model$attributes$$hasValue(record, key) { + return key in record._attributes || + key in record._inFlightAttributes || + record._data.hasOwnProperty(key); + } - /** - The property name of the relationship + function ember$data$lib$system$model$attributes$$getValue(record, key) { + if (key in record._attributes) { + return record._attributes[key]; + } else if (key in record._inFlightAttributes) { + return record._inFlightAttributes[key]; + } else { + return record._data[key]; + } + } - @property {String} name - @private - */ - name: null, + function ember$data$lib$system$model$attributes$$attr(type, options) { + if (typeof type === 'object') { + options = type; + type = undefined; + } else { + options = options || {}; + } + + var meta = { + type: type, + isAttribute: true, + options: options + }; + + return Ember.computed(function(key, value) { + if (arguments.length > 1) { + Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('')` from " + this.constructor.toString(), key !== 'id'); + var oldValue = ember$data$lib$system$model$attributes$$getValue(this, key); + + if (value !== oldValue) { + // Add the new value to the changed attributes hash; it will get deleted by + // the 'didSetProperty' handler if it is no different from the original value + this._attributes[key] = value; + + this.send('didSetProperty', { + name: key, + oldValue: oldValue, + originalValue: this._data[key], + value: value + }); + } + + return value; + } else if (ember$data$lib$system$model$attributes$$hasValue(this, key)) { + return ember$data$lib$system$model$attributes$$getValue(this, key); + } else { + return ember$data$lib$system$model$attributes$$getDefaultValue(this, options, key); + } + + // `data` is never set directly. However, it may be + // invalidated from the state manager's setData + // event. + }).meta(meta); + } + var ember$data$lib$system$model$attributes$$default = ember$data$lib$system$model$attributes$$attr; + //Stanley told me to do this + var ember$data$lib$system$store$$Backburner = Ember.__loader.require('backburner')['default'] || Ember.__loader.require('backburner')['Backburner']; + + //Shim Backburner.join + if (!ember$data$lib$system$store$$Backburner.prototype.join) { + var ember$data$lib$system$store$$isString = function(suspect) { + return typeof suspect === 'string'; + }; - /** - The record to which this relationship belongs. + ember$data$lib$system$store$$Backburner.prototype.join = function(/*target, method, args */) { + var method, target; - @property {DS.Model} owner - @private - */ - owner: null, + if (this.currentInstance) { + var length = arguments.length; + if (length === 1) { + method = arguments[0]; + target = null; + } else { + target = arguments[0]; + method = arguments[1]; + } - /** - `true` if the relationship is polymorphic, `false` otherwise. + if (ember$data$lib$system$store$$isString(method)) { + method = target[method]; + } - @property {Boolean} isPolymorphic - @private - */ - isPolymorphic: false, + if (length === 1) { + return method(); + } else if (length === 2) { + return method.call(target); + } else { + var args = new Array(length - 2); + for (var i =0, l = length - 2; i < l; i++) { + args[i] = arguments[i + 2]; + } + return method.apply(target, args); + } + } else { + return this.run.apply(this, arguments); + } + }; + } - // LOADING STATE - isLoaded: false, + var ember$data$lib$system$store$$get = Ember.get; + var ember$data$lib$system$store$$set = Ember.set; + var ember$data$lib$system$store$$once = Ember.run.once; + var ember$data$lib$system$store$$isNone = Ember.isNone; + var ember$data$lib$system$store$$forEach = Ember.EnumerableUtils.forEach; + var ember$data$lib$system$store$$indexOf = Ember.EnumerableUtils.indexOf; + var ember$data$lib$system$store$$map = Ember.EnumerableUtils.map; + var ember$data$lib$system$store$$Promise = Ember.RSVP.Promise; + var ember$data$lib$system$store$$copy = Ember.copy; + var ember$data$lib$system$store$$Store; - /** - Used for async `hasMany` arrays - to keep track of when they will resolve. + var ember$data$lib$system$store$$camelize = Ember.String.camelize; - @property {Ember.RSVP.Promise} promise - @private - */ - promise: null, + // Implementors Note: + // + // The variables in this file are consistently named according to the following + // scheme: + // + // * +id+ means an identifier managed by an external source, provided inside + // the data provided by that source. These are always coerced to be strings + // before being used internally. + // * +clientId+ means a transient numerical identifier generated at runtime by + // the data store. It is important primarily because newly created objects may + // not yet have an externally generated id. + // * +reference+ means a record reference object, which holds metadata about a + // record, even if it has not yet been fully materialized. + // * +type+ means a subclass of DS.Model. - /** - @method loadingRecordsCount - @param {Number} count - @private - */ - loadingRecordsCount: function(count) { - this.loadingRecordsCount = count; - }, + // Used by the store to normalize IDs entering the store. Despite the fact + // that developers may provide IDs as numbers (e.g., `store.find(Person, 1)`), + // it is important that internally we use strings, since IDs may be serialized + // and lose type information. For example, Ember's router may put a record's + // ID into the URL, and if we later try to deserialize that URL and find the + // corresponding record, we will not know if it is a string or a number. + function ember$data$lib$system$store$$coerceId(id) { + return id == null ? null : id+''; + } - /** - @method loadedRecord - @private - */ - loadedRecord: function() { - this.loadingRecordsCount--; - if (this.loadingRecordsCount === 0) { - set(this, 'isLoaded', true); - this.trigger('didLoad'); - } - }, + /** + The store contains all of the data for records loaded from the server. + It is also responsible for creating instances of `DS.Model` that wrap + the individual data for a record, so that they can be bound to in your + Handlebars templates. - /** - @method fetch - @private - */ - fetch: function() { - var records = get(this, 'content'), - store = get(this, 'store'), - owner = get(this, 'owner'), - resolver = Ember.RSVP.defer("DS: ManyArray#fetch " + get(this, 'type')); + Define your application's store like this: - var unloadedRecords = records.filterProperty('isEmpty', true); - store.fetchMany(unloadedRecords, owner, resolver); - }, + ```javascript + MyApp.Store = DS.Store.extend(); + ``` - // Overrides Ember.Array's replace method to implement - replaceContent: function(index, removed, added) { - // Map the array of record objects into an array of client ids. - added = map(added, function(record) { - Ember.assert("You cannot add '" + record.constructor.typeKey + "' records to this relationship (only '" + this.type.typeKey + "' allowed)", !this.type || record instanceof this.type); - return record; - }, this); + Most Ember.js applications will only have a single `DS.Store` that is + automatically created by their `Ember.Application`. - this._super(index, removed, added); - }, + You can retrieve models from the store in several ways. To retrieve a record + for a specific id, use `DS.Store`'s `find()` method: - arrangedContentDidChange: function() { - Ember.run.once(this, 'fetch'); - }, + ```javascript + store.find('person', 123).then(function (person) { + }); + ``` - arrayContentWillChange: function(index, removed, added) { - var owner = get(this, 'owner'), - name = get(this, 'name'); + By default, the store will talk to your backend using a standard + REST mechanism. You can customize how the store talks to your + backend by specifying a custom adapter: - if (!owner._suspendedRelationships) { - // This code is the first half of code that continues inside - // of arrayContentDidChange. It gets or creates a change from - // the child object, adds the current owner as the old - // parent if this is the first time the object was removed - // from a ManyArray, and sets `newParent` to null. - // - // Later, if the object is added to another ManyArray, - // the `arrayContentDidChange` will set `newParent` on - // the change. - for (var i=index; i= 1); + Ember.assert("You may not pass `" + id + "` as id to the store's find method", arguments.length === 1 || !Ember.isNone(id)); - change.sync(); - } + if (arguments.length === 1) { + return this.findAll(type); } - delete this._changesToSync[key]; - }) - }); - - __exports__["default"] = belongsTo; - }); -define("ember-data/lib/system/relationships/ext", - ["../../../../ember-inflector/lib/system","../model"], - function(__dependency1__, __dependency2__) { - "use strict"; - var singularize = __dependency1__.singularize; - var Model = __dependency2__.Model; - - var get = Ember.get, set = Ember.set; - - /** - @module ember-data - */ - - /* - This file defines several extensions to the base `DS.Model` class that - add support for one-to-many relationships. - */ + // We are passed a query instead of an id. + if (Ember.typeOf(id) === 'object') { + return this.findQuery(type, id); + } - /** - @class Model - @namespace DS - */ - Model.reopen({ + return this.findById(type, ember$data$lib$system$store$$coerceId(id), preload); + }, /** - This Ember.js hook allows an object to be notified when a property - is defined. + This method returns a fresh record for a given type and id combination. - In this case, we use it to be notified when an Ember Data user defines a - belongs-to relationship. In that case, we need to set up observers for - each one, allowing us to track relationship changes and automatically - reflect changes in the inverse has-many array. + If a record is available for the given type/id combination, then + it will fetch this record from the store and call `reload()` on it. + That will fire a request to server and return a promise that will + resolve once the record has been reloaded. + If there's no record corresponding in the store it will simply call + `store.find`. - This hook passes the class being set up, as well as the key and value - being defined. So, for example, when the user does this: + Example ```javascript - DS.Model.extend({ - parent: DS.belongsTo('user') + App.PostRoute = Ember.Route.extend({ + model: function(params) { + return this.store.fetch('post', params.post_id); + } }); ``` - This hook would be called with "parent" as the key and the computed - property returned by `DS.belongsTo` as the value. - - @method didDefineProperty - @param proto - @param key - @param value + @method fetch + @param {String or subclass of DS.Model} type + @param {String|Integer} id + @param {Object} preload - optional set of attributes and relationships passed in either as IDs or as actual models + @return {Promise} promise */ - didDefineProperty: function(proto, key, value) { - // Check if the value being set is a computed property. - if (value instanceof Ember.Descriptor) { - - // If it is, get the metadata for the relationship. This is - // populated by the `DS.belongsTo` helper when it is creating - // the computed property. - var meta = value.meta(); - - if (meta.isRelationship && meta.kind === 'belongsTo') { - Ember.addObserver(proto, key, null, 'belongsToDidChange'); - Ember.addBeforeObserver(proto, key, null, 'belongsToWillChange'); - } - - meta.parentType = proto.constructor; + fetch: function(type, id, preload) { + if (this.hasRecordForId(type, id)) { + return this.getById(type, id).reload(); + } else { + return this.find(type, id, preload); } - } - }); - - /* - These DS.Model extensions add class methods that provide relationship - introspection abilities about relationships. + }, - A note about the computed properties contained here: + /** + This method returns a record for a given type and id combination. - **These properties are effectively sealed once called for the first time.** - To avoid repeatedly doing expensive iteration over a model's fields, these - values are computed once and then cached for the remainder of the runtime of - your application. + @method findById + @private + @param {String or subclass of DS.Model} type + @param {String|Integer} id + @param {Object} preload - optional set of attributes and relationships passed in either as IDs or as actual models + @return {Promise} promise + */ + findById: function(typeName, id, preload) { - If your application needs to modify a class after its initial definition - (for example, using `reopen()` to add additional attributes), make sure you - do it before using your model with the store, which uses these properties - extensively. - */ + var type = this.modelFor(typeName); + var record = this.recordForId(type, id); - Model.reopenClass({ - /** - For a given relationship name, returns the model type of the relationship. + return this._findByRecord(record, preload); + }, - For example, if you define a model like this: + _findByRecord: function(record, preload) { + var fetchedRecord; - ```javascript - App.Post = DS.Model.extend({ - comments: DS.hasMany('comment') - }); - ``` + if (preload) { + record._preloadData(preload); + } - Calling `App.Post.typeForRelationship('comments')` will return `App.Comment`. + if (ember$data$lib$system$store$$get(record, 'isEmpty')) { + fetchedRecord = this.scheduleFetch(record); + //TODO double check about reloading + } else if (ember$data$lib$system$store$$get(record, 'isLoading')){ + fetchedRecord = record._loadingPromise; + } - @method typeForRelationship - @static - @param {String} name the name of the relationship - @return {subclass of DS.Model} the type of the relationship, or undefined - */ - typeForRelationship: function(name) { - var relationship = get(this, 'relationshipsByName').get(name); - return relationship && relationship.type; + return ember$data$lib$system$promise_proxies$$promiseObject(fetchedRecord || record, "DS: Store#findByRecord " + record.typeKey + " with id: " + ember$data$lib$system$store$$get(record, 'id')); }, - inverseFor: function(name) { - var inverseType = this.typeForRelationship(name); - - if (!inverseType) { return null; } - - var options = this.metaForProperty(name).options; + /** + This method makes a series of requests to the adapter's `find` method + and returns a promise that resolves once they are all loaded. - if (options.inverse === null) { return null; } + @private + @method findByIds + @param {String} type + @param {Array} ids + @return {Promise} promise + */ + findByIds: function(type, ids) { + var store = this; - var inverseName, inverseKind; + return ember$data$lib$system$promise_proxies$$promiseArray(Ember.RSVP.all(ember$data$lib$system$store$$map(ids, function(id) { + return store.findById(type, id); + })).then(Ember.A, null, "DS: Store#findByIds of " + type + " complete")); + }, - if (options.inverse) { - inverseName = options.inverse; - inverseKind = Ember.get(inverseType, 'relationshipsByName').get(inverseName).kind; - } else { - var possibleRelationships = findPossibleInverses(this, inverseType); + /** + This method is called by `findById` if it discovers that a particular + type/id pair hasn't been loaded yet to kick off a request to the + adapter. - if (possibleRelationships.length === 0) { return null; } + @method fetchRecord + @private + @param {DS.Model} record + @return {Promise} promise + */ + fetchRecord: function(record) { + var type = record.constructor; + var id = ember$data$lib$system$store$$get(record, 'id'); + var adapter = this.adapterFor(type); - Ember.assert("You defined the '" + name + "' relationship on " + this + ", but multiple possible inverse relationships of type " + this + " were found on " + inverseType + ". Look at http://emberjs.com/guides/models/defining-models/#toc_explicit-inverses for how to explicitly specify inverses", possibleRelationships.length === 1); + Ember.assert("You tried to find a record but you have no adapter (for " + type + ")", adapter); + Ember.assert("You tried to find a record but your adapter (for " + type + ") does not implement 'find'", typeof adapter.find === 'function'); - inverseName = possibleRelationships[0].name; - inverseKind = possibleRelationships[0].kind; - } + var promise = ember$data$lib$system$store$$_find(adapter, this, type, id, record); + return promise; + }, - function findPossibleInverses(type, inverseType, possibleRelationships) { - possibleRelationships = possibleRelationships || []; + scheduleFetchMany: function(records) { + return ember$data$lib$system$store$$Promise.all(ember$data$lib$system$store$$map(records, this.scheduleFetch, this)); + }, - var relationshipMap = get(inverseType, 'relationships'); - if (!relationshipMap) { return; } + scheduleFetch: function(record) { + var type = record.constructor; + if (ember$data$lib$system$store$$isNone(record)) { return null; } + if (record._loadingPromise) { return record._loadingPromise; } - var relationships = relationshipMap.get(type); - if (relationships) { - possibleRelationships.push.apply(possibleRelationships, relationshipMap.get(type)); - } + var resolver = Ember.RSVP.defer('Fetching ' + type + 'with id: ' + record.get('id')); + var recordResolverPair = { + record: record, + resolver: resolver + }; + var promise = resolver.promise; - if (type.superclass) { - findPossibleInverses(type.superclass, inverseType, possibleRelationships); - } + record.loadingData(promise); - return possibleRelationships; + if (!this._pendingFetch.get(type)){ + this._pendingFetch.set(type, [recordResolverPair]); + } else { + this._pendingFetch.get(type).push(recordResolverPair); } + Ember.run.scheduleOnce('afterRender', this, this.flushAllPendingFetches); - return { - type: inverseType, - name: inverseName, - kind: inverseKind - }; + return promise; }, - /** - The model's relationships as a map, keyed on the type of the - relationship. The value of each entry is an array containing a descriptor - for each relationship with that type, describing the name of the relationship - as well as the type. + flushAllPendingFetches: function(){ + if (this.isDestroyed || this.isDestroying) { + return; + } - For example, given the following model definition: + this._pendingFetch.forEach(this._flushPendingFetchForType, this); + this._pendingFetch = ember$data$lib$system$map$$Map.create(); + }, - ```javascript - App.Blog = DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), - posts: DS.hasMany('post') - }); - ``` + _flushPendingFetchForType: function (recordResolverPairs, type) { + var store = this; + var adapter = store.adapterFor(type); + var shouldCoalesce = !!adapter.findMany && adapter.coalesceFindRequests; + var records = Ember.A(recordResolverPairs).mapBy('record'); - This computed property would return a map describing these - relationships, like this: + function _fetchRecord(recordResolverPair) { + recordResolverPair.resolver.resolve(store.fetchRecord(recordResolverPair.record)); + } - ```javascript - var relationships = Ember.get(App.Blog, 'relationships'); - relationships.get(App.User); - //=> [ { name: 'users', kind: 'hasMany' }, - // { name: 'owner', kind: 'belongsTo' } ] - relationships.get(App.Post); - //=> [ { name: 'posts', kind: 'hasMany' } ] - ``` + function resolveFoundRecords(records) { + ember$data$lib$system$store$$forEach(records, function(record){ + var pair = Ember.A(recordResolverPairs).findBy('record', record); + if (pair){ + var resolver = pair.resolver; + resolver.resolve(record); + } + }); + } - @property relationships - @static - @type Ember.Map - @readOnly - */ - relationships: Ember.computed(function() { - var map = new Ember.MapWithDefault({ - defaultValue: function() { return []; } - }); + function makeMissingRecordsRejector(requestedRecords) { + return function rejectMissingRecords(resolvedRecords) { + var missingRecords = requestedRecords.without(resolvedRecords); + rejectRecords(missingRecords); + }; + } - // Loop through each computed property on the class - this.eachComputedProperty(function(name, meta) { + function makeRecordsRejector(records) { + return function (error) { + rejectRecords(records, error); + }; + } - // If the computed property is a relationship, add - // it to the map. - if (meta.isRelationship) { - if (typeof meta.type === 'string') { - meta.type = this.store.modelFor(meta.type); + function rejectRecords(records, error) { + ember$data$lib$system$store$$forEach(records, function(record){ + var pair = Ember.A(recordResolverPairs).findBy('record', record); + if (pair){ + var resolver = pair.resolver; + resolver.reject(error); } + }); + } - var relationshipsForType = map.get(meta.type); - - relationshipsForType.push({ name: name, kind: meta.kind }); - } - }); - - return map; - }), + if (recordResolverPairs.length === 1) { + _fetchRecord(recordResolverPairs[0]); + } else if (shouldCoalesce) { + var groups = adapter.groupRecordsForFindMany(this, records); + ember$data$lib$system$store$$forEach(groups, function (groupOfRecords) { + var requestedRecords = Ember.A(groupOfRecords); + var ids = requestedRecords.mapBy('id'); + if (ids.length > 1) { + ember$data$lib$system$store$$_findMany(adapter, store, type, ids, requestedRecords). + then(resolveFoundRecords). + then(makeMissingRecordsRejector(requestedRecords)). + then(null, makeRecordsRejector(requestedRecords)); + } else if (ids.length === 1) { + var pair = Ember.A(recordResolverPairs).findBy('record', groupOfRecords[0]); + _fetchRecord(pair); + } else { + Ember.assert("You cannot return an empty array from adapter's method groupRecordsForFindMany", false); + } + }); + } else { + ember$data$lib$system$store$$forEach(recordResolverPairs, _fetchRecord); + } + }, /** - A hash containing lists of the model's relationships, grouped - by the relationship kind. For example, given a model with this - definition: + Get a record by a given type and ID without triggering a fetch. - ```javascript - App.Blog = DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), + This method will synchronously return the record if it is available in the store, + otherwise it will return `null`. A record is available if it has been fetched earlier, or + pushed manually into the store. - posts: DS.hasMany('post') - }); - ``` + _Note: This is an synchronous method and does not return a promise._ - This property would contain the following: + ```js + var post = store.getById('post', 1); - ```javascript - var relationshipNames = Ember.get(App.Blog, 'relationshipNames'); - relationshipNames.hasMany; - //=> ['users', 'posts'] - relationshipNames.belongsTo; - //=> ['owner'] + post.get('id'); // 1 ``` - @property relationshipNames - @static - @type Object - @readOnly + @method getById + @param {String or subclass of DS.Model} type + @param {String|Integer} id + @return {DS.Model|null} record */ - relationshipNames: Ember.computed(function() { - var names = { hasMany: [], belongsTo: [] }; - - this.eachComputedProperty(function(name, meta) { - if (meta.isRelationship) { - names[meta.kind].push(name); - } - }); - - return names; - }), + getById: function(type, id) { + if (this.hasRecordForId(type, id)) { + return this.recordForId(type, id); + } else { + return null; + } + }, /** - An array of types directly related to a model. Each type will be - included once, regardless of the number of relationships it has with - the model. + This method is called by the record's `reload` method. - For example, given a model with this definition: + This method calls the adapter's `find` method, which returns a promise. When + **that** promise resolves, `reloadRecord` will resolve the promise returned + by the record's `reload`. - ```javascript - App.Blog = DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), + @method reloadRecord + @private + @param {DS.Model} record + @return {Promise} promise + */ + reloadRecord: function(record) { + var type = record.constructor; + var adapter = this.adapterFor(type); + var id = ember$data$lib$system$store$$get(record, 'id'); - posts: DS.hasMany('post') - }); - ``` + Ember.assert("You cannot reload a record without an ID", id); + Ember.assert("You tried to reload a record but you have no adapter (for " + type + ")", adapter); + Ember.assert("You tried to reload a record but your adapter does not implement `find`", typeof adapter.find === 'function'); - This property would contain the following: + return this.scheduleFetch(record); + }, - ```javascript - var relatedTypes = Ember.get(App.Blog, 'relatedTypes'); - //=> [ App.User, App.Post ] - ``` + /** + Returns true if a record for a given type and ID is already loaded. - @property relatedTypes - @static - @type Ember.Array - @readOnly + @method hasRecordForId + @param {String or subclass of DS.Model} type + @param {String|Integer} id + @return {Boolean} */ - relatedTypes: Ember.computed(function() { - var type, - types = Ember.A(); - - // Loop through each computed property on the class, - // and create an array of the unique types involved - // in relationships - this.eachComputedProperty(function(name, meta) { - if (meta.isRelationship) { - type = meta.type; + hasRecordForId: function(typeName, inputId) { + var type = this.modelFor(typeName); + var id = ember$data$lib$system$store$$coerceId(inputId); + return !!this.typeMapFor(type).idToRecord[id]; + }, - if (typeof type === 'string') { - type = get(this, type, false) || this.store.modelFor(type); - } + /** + Returns id record for a given type and ID. If one isn't already loaded, + it builds a new record and leaves it in the `empty` state. - Ember.assert("You specified a hasMany (" + meta.type + ") on " + meta.parentType + " but " + meta.type + " was not found.", type); + @method recordForId + @private + @param {String or subclass of DS.Model} type + @param {String|Integer} id + @return {DS.Model} record + */ + recordForId: function(typeName, inputId) { + var type = this.modelFor(typeName); + var id = ember$data$lib$system$store$$coerceId(inputId); + var idToRecord = this.typeMapFor(type).idToRecord; + var record = idToRecord[id]; - if (!types.contains(type)) { - Ember.assert("Trying to sideload " + name + " on " + this.toString() + " but the type doesn't exist.", !!type); - types.push(type); - } - } - }); + if (!record || !idToRecord[id]) { + record = this.buildRecord(type, id); + } - return types; - }), + return record; + }, /** - A map whose keys are the relationships of a model and whose values are - relationship descriptors. - - For example, given a model with this - definition: + @method findMany + @private + @param {DS.Model} owner + @param {Array} records + @param {String or subclass of DS.Model} type + @param {Resolver} resolver + @return {DS.ManyArray} records + */ + findMany: function(records) { + var store = this; + return ember$data$lib$system$store$$Promise.all(ember$data$lib$system$store$$map(records, function(record) { + return store._findByRecord(record); + })); + }, - ```javascript - App.Blog = DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), - posts: DS.hasMany('post') - }); - ``` + /** + If a relationship was originally populated by the adapter as a link + (as opposed to a list of IDs), this method is called when the + relationship is fetched. - This property would contain the following: + The link (which is usually a URL) is passed through unchanged, so the + adapter can make whatever request it wants. - ```javascript - var relationshipsByName = Ember.get(App.Blog, 'relationshipsByName'); - relationshipsByName.get('users'); - //=> { key: 'users', kind: 'hasMany', type: App.User } - relationshipsByName.get('owner'); - //=> { key: 'owner', kind: 'belongsTo', type: App.User } - ``` + The usual use-case is for the server to register a URL as a link, and + then use that URL in the future to make a request for the relationship. - @property relationshipsByName - @static - @type Ember.Map - @readOnly + @method findHasMany + @private + @param {DS.Model} owner + @param {any} link + @param {String or subclass of DS.Model} type + @return {Promise} promise */ - relationshipsByName: Ember.computed(function() { - var map = Ember.Map.create(), type; + findHasMany: function(owner, link, type) { + var adapter = this.adapterFor(owner.constructor); - this.eachComputedProperty(function(name, meta) { - if (meta.isRelationship) { - meta.key = name; - type = meta.type; + Ember.assert("You tried to load a hasMany relationship but you have no adapter (for " + owner.constructor + ")", adapter); + Ember.assert("You tried to load a hasMany relationship from a specified `link` in the original payload but your adapter does not implement `findHasMany`", typeof adapter.findHasMany === 'function'); - if (!type && meta.kind === 'hasMany') { - type = singularize(name); - } else if (!type) { - type = name; - } + return ember$data$lib$system$store$$_findHasMany(adapter, this, owner, link, type); + }, - if (typeof type === 'string') { - meta.type = this.store.modelFor(type); - } + /** + @method findBelongsTo + @private + @param {DS.Model} owner + @param {any} link + @param {Relationship} relationship + @return {Promise} promise + */ + findBelongsTo: function(owner, link, relationship) { + var adapter = this.adapterFor(owner.constructor); - map.set(name, meta); - } - }); + Ember.assert("You tried to load a belongsTo relationship but you have no adapter (for " + owner.constructor + ")", adapter); + Ember.assert("You tried to load a belongsTo relationship from a specified `link` in the original payload but your adapter does not implement `findBelongsTo`", typeof adapter.findBelongsTo === 'function'); - return map; - }), + return ember$data$lib$system$store$$_findBelongsTo(adapter, this, owner, link, relationship); + }, /** - A map whose keys are the fields of the model and whose values are strings - describing the kind of the field. A model's fields are the union of all of its - attributes and relationships. + This method delegates a query to the adapter. This is the one place where + adapter-level semantics are exposed to the application. - For example: + Exposing queries this way seems preferable to creating an abstract query + language for all server-side queries, and then require all adapters to + implement them. - ```javascript + This method returns a promise, which is resolved with a `RecordArray` + once the server returns. - App.Blog = DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), + @method findQuery + @private + @param {String or subclass of DS.Model} type + @param {any} query an opaque query to be used by the adapter + @return {Promise} promise + */ + findQuery: function(typeName, query) { + var type = this.modelFor(typeName); + var array = this.recordArrayManager + .createAdapterPopulatedRecordArray(type, query); - posts: DS.hasMany('post'), + var adapter = this.adapterFor(type); - title: DS.attr('string') - }); + Ember.assert("You tried to load a query but you have no adapter (for " + type + ")", adapter); + Ember.assert("You tried to load a query but your adapter does not implement `findQuery`", typeof adapter.findQuery === 'function'); - var fields = Ember.get(App.Blog, 'fields'); - fields.forEach(function(field, kind) { - console.log(field, kind); - }); + return ember$data$lib$system$promise_proxies$$promiseArray(ember$data$lib$system$store$$_findQuery(adapter, this, type, query, array)); + }, - // prints: - // users, hasMany - // owner, belongsTo - // posts, hasMany - // title, attribute - ``` + /** + This method returns an array of all records adapter can find. + It triggers the adapter's `findAll` method to give it an opportunity to populate + the array with records of that type. - @property fields - @static - @type Ember.Map - @readOnly + @method findAll + @private + @param {String or subclass of DS.Model} type + @return {DS.AdapterPopulatedRecordArray} */ - fields: Ember.computed(function() { - var map = Ember.Map.create(); - - this.eachComputedProperty(function(name, meta) { - if (meta.isRelationship) { - map.set(name, meta.kind); - } else if (meta.isAttribute) { - map.set(name, 'attribute'); - } - }); + findAll: function(typeName) { + var type = this.modelFor(typeName); - return map; - }), + return this.fetchAll(type, this.all(type)); + }, /** - Given a callback, iterates over each of the relationships in the model, - invoking the callback with the name of each relationship and its relationship - descriptor. + @method fetchAll + @private + @param {DS.Model} type + @param {DS.RecordArray} array + @return {Promise} promise + */ + fetchAll: function(type, array) { + var adapter = this.adapterFor(type); + var sinceToken = this.typeMapFor(type).metadata.since; - @method eachRelationship - @static - @param {Function} callback the callback to invoke - @param {any} binding the value to which the callback's `this` should be bound + ember$data$lib$system$store$$set(array, 'isUpdating', true); + + Ember.assert("You tried to load all records but you have no adapter (for " + type + ")", adapter); + Ember.assert("You tried to load all records but your adapter does not implement `findAll`", typeof adapter.findAll === 'function'); + + return ember$data$lib$system$promise_proxies$$promiseArray(ember$data$lib$system$store$$_findAll(adapter, this, type, sinceToken)); + }, + + /** + @method didUpdateAll + @param {DS.Model} type */ - eachRelationship: function(callback, binding) { - get(this, 'relationshipsByName').forEach(function(name, relationship) { - callback.call(binding, name, relationship); - }); + didUpdateAll: function(type) { + var findAllCache = this.typeMapFor(type).findAllCache; + ember$data$lib$system$store$$set(findAllCache, 'isUpdating', false); }, /** - Given a callback, iterates over each of the types related to a model, - invoking the callback with the related type's class. Each type will be - returned just once, regardless of how many different relationships it has - with a model. + This method returns a filtered array that contains all of the + known records for a given type in the store. - @method eachRelatedType - @static - @param {Function} callback the callback to invoke - @param {any} binding the value to which the callback's `this` should be bound + Note that because it's just a filter, the result will contain any + locally created records of the type, however, it will not make a + request to the backend to retrieve additional records. If you + would like to request all the records from the backend please use + [store.find](#method_find). + + Also note that multiple calls to `all` for a given type will always + return the same `RecordArray`. + + Example + + ```javascript + var localPosts = store.all('post'); + ``` + + @method all + @param {String or subclass of DS.Model} type + @return {DS.RecordArray} */ - eachRelatedType: function(callback, binding) { - get(this, 'relatedTypes').forEach(function(type) { - callback.call(binding, type); - }); - } - }); + all: function(typeName) { + var type = this.modelFor(typeName); + var typeMap = this.typeMapFor(type); + var findAllCache = typeMap.findAllCache; + + if (findAllCache) { + this.recordArrayManager.updateFilter(findAllCache, type); + return findAllCache; + } + + var array = this.recordArrayManager.createRecordArray(type); + + typeMap.findAllCache = array; + return array; + }, - Model.reopen({ - /** - Given a callback, iterates over each of the relationships in the model, - invoking the callback with the name of each relationship and its relationship - descriptor. - @method eachRelationship - @param {Function} callback the callback to invoke - @param {any} binding the value to which the callback's `this` should be bound - */ - eachRelationship: function(callback, binding) { - this.constructor.eachRelationship(callback, binding); - } - }); - }); -define("ember-data/lib/system/relationships/has_many", - ["exports"], - function(__exports__) { - "use strict"; - /** - @module ember-data - */ + /** + This method unloads all of the known records for a given type. - var get = Ember.get, set = Ember.set, setProperties = Ember.setProperties; + ```javascript + store.unloadAll('post'); + ``` - function asyncHasMany(type, options, meta) { - return Ember.computed('data', function(key) { - var relationship = this._relationships[key], - promiseLabel = "DS: Async hasMany " + this + " : " + key; + @method unloadAll + @param {String or subclass of DS.Model} type + */ + unloadAll: function(type) { + var modelType = this.modelFor(type); + var typeMap = this.typeMapFor(modelType); + var records = typeMap.records.slice(); + var record; - if (!relationship) { - var resolver = Ember.RSVP.defer(promiseLabel); - relationship = buildRelationship(this, key, options, function(store, data) { - var link = data.links && data.links[key]; - var rel; - if (link) { - rel = store.findHasMany(this, link, meta, resolver); - } else { - rel = store.findMany(this, data[key], meta.type, resolver); - } - // cache the promise so we can use it - // when we come back and don't need to rebuild - // the relationship. - set(rel, 'promise', resolver.promise); - return rel; - }); + for (var i = 0; i < records.length; i++) { + record = records[i]; + record.unloadRecord(); + record.destroy(); // maybe within unloadRecord } - var promise = relationship.get('promise').then(function() { - return relationship; - }, null, "DS: Async hasMany records received"); + typeMap.findAllCache = null; + }, - return DS.PromiseArray.create({ - promise: promise - }); - }).meta(meta).readOnly(); - } + /** + Takes a type and filter function, and returns a live RecordArray that + remains up to date as new records are loaded into the store or created + locally. - function buildRelationship(record, key, options, callback) { - var rels = record._relationships; + The filter function takes a materialized record, and returns true + if the record should be included in the filter and false if it should + not. - if (rels[key]) { return rels[key]; } + Example - var data = get(record, 'data'), - store = get(record, 'store'); + ```javascript + store.filter('post', function(post) { + return post.get('unread'); + }); + ``` - var relationship = rels[key] = callback.call(record, store, data); + The filter function is called once on all records for the type when + it is created, and then once on each newly loaded or created record. - return setProperties(relationship, { - owner: record, - name: key, - isPolymorphic: options.polymorphic - }); - } + If any of a record's properties change, or if it changes state, the + filter function will be invoked again to determine whether it should + still be in the array. - function hasRelationship(type, options) { - options = options || {}; + Optionally you can pass a query, which is the equivalent of calling + [find](#method_find) with that same query, to fetch additional records + from the server. The results returned by the server could then appear + in the filter if they match the filter function. - var meta = { - type: type, - isRelationship: true, - options: options, - kind: 'hasMany' - }; + The query itself is not used to filter records, it's only sent to your + server for you to be able to do server-side filtering. The filter + function will be applied on the returned results regardless. - if (options.async) { - return asyncHasMany(type, options, meta); - } + Example - return Ember.computed('data', function(key) { - return buildRelationship(this, key, options, function(store, data) { - var records = data[key]; - Ember.assert("You looked up the '" + key + "' relationship on '" + this + "' but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.hasMany({ async: true })`)", Ember.A(records).everyProperty('isEmpty', false)); - return store.findMany(this, data[key], meta.type); + ```javascript + store.filter('post', { unread: true }, function(post) { + return post.get('unread'); + }).then(function(unreadPosts) { + unreadPosts.get('length'); // 5 + var unreadPost = unreadPosts.objectAt(0); + unreadPost.set('unread', false); + unreadPosts.get('length'); // 4 }); - }).meta(meta).readOnly(); - } - - /** - `DS.hasMany` is used to define One-To-Many and Many-To-Many - relationships on a [DS.Model](/api/data/classes/DS.Model.html). + ``` - `DS.hasMany` takes an optional hash as a second parameter, currently - supported options are: + @method filter + @param {String or subclass of DS.Model} type + @param {Object} query optional query + @param {Function} filter + @return {DS.PromiseArray} + */ + filter: function(type, query, filter) { + var promise; + var length = arguments.length; + var array; + var hasQuery = length === 3; - - `async`: A boolean value used to explicitly declare this to be an async relationship. - - `inverse`: A string used to identify the inverse property on a related model. + // allow an optional server query + if (hasQuery) { + promise = this.findQuery(type, query); + } else if (arguments.length === 2) { + filter = query; + } - #### One-To-Many - To declare a one-to-many relationship between two models, use - `DS.belongsTo` in combination with `DS.hasMany`, like this: + type = this.modelFor(type); - ```javascript - App.Post = DS.Model.extend({ - comments: DS.hasMany('comment') - }); + if (hasQuery) { + array = this.recordArrayManager.createFilteredRecordArray(type, filter, query); + } else { + array = this.recordArrayManager.createFilteredRecordArray(type, filter); + } - App.Comment = DS.Model.extend({ - post: DS.belongsTo('post') - }); - ``` + promise = promise || ember$data$lib$system$store$$Promise.cast(array); - #### Many-To-Many - To declare a many-to-many relationship between two models, use - `DS.hasMany`: + return ember$data$lib$system$promise_proxies$$promiseArray(promise.then(function() { + return array; + }, null, "DS: Store#filter of " + type)); + }, - ```javascript - App.Post = DS.Model.extend({ - tags: DS.hasMany('tag') - }); + /** + This method returns if a certain record is already loaded + in the store. Use this function to know beforehand if a find() + will result in a request or that it will be a cache hit. - App.Tag = DS.Model.extend({ - posts: DS.hasMany('post') - }); - ``` + Example - #### Explicit Inverses + ```javascript + store.recordIsLoaded('post', 1); // false + store.find('post', 1).then(function() { + store.recordIsLoaded('post', 1); // true + }); + ``` - Ember Data will do its best to discover which relationships map to - one another. In the one-to-many code above, for example, Ember Data - can figure out that changing the `comments` relationship should update - the `post` relationship on the inverse because post is the only - relationship to that model. + @method recordIsLoaded + @param {String or subclass of DS.Model} type + @param {string} id + @return {boolean} + */ + recordIsLoaded: function(type, id) { + if (!this.hasRecordForId(type, id)) { return false; } + return !ember$data$lib$system$store$$get(this.recordForId(type, id), 'isEmpty'); + }, - However, sometimes you may have multiple `belongsTo`/`hasManys` for the - same type. You can specify which property on the related model is - the inverse using `DS.hasMany`'s `inverse` option: + /** + This method returns the metadata for a specific type. - ```javascript - var belongsTo = DS.belongsTo, - hasMany = DS.hasMany; + @method metadataFor + @param {String or subclass of DS.Model} typeName + @return {object} + */ + metadataFor: function(typeName) { + var type = this.modelFor(typeName); + return this.typeMapFor(type).metadata; + }, - App.Comment = DS.Model.extend({ - onePost: belongsTo('post'), - twoPost: belongsTo('post'), - redPost: belongsTo('post'), - bluePost: belongsTo('post') - }); + /** + This method sets the metadata for a specific type. - App.Post = DS.Model.extend({ - comments: hasMany('comment', { - inverse: 'redPost' - }) - }); - ``` + @method setMetadataFor + @param {String or subclass of DS.Model} typeName + @param {Object} metadata metadata to set + @return {object} + */ + setMetadataFor: function(typeName, metadata) { + var type = this.modelFor(typeName); + Ember.merge(this.typeMapFor(type).metadata, metadata); + }, - You can also specify an inverse on a `belongsTo`, which works how - you'd expect. + // ............ + // . UPDATING . + // ............ - @namespace - @method hasMany - @for DS - @param {String or DS.Model} type the model type of the relationship - @param {Object} options a hash of options - @return {Ember.computed} relationship - */ - function hasMany(type, options) { - if (typeof type === 'object') { - options = type; - type = undefined; - } - return hasRelationship(type, options); - } + /** + If the adapter updates attributes or acknowledges creation + or deletion, the record will notify the store to update its + membership in any filters. + To avoid thrashing, this method is invoked only once per - __exports__["default"] = hasMany; - }); -define("ember-data/lib/system/store", - ["exports"], - function(__exports__) { - "use strict"; - /*globals Ember*/ - /*jshint eqnull:true*/ + run loop per record. - /** - @module ember-data - */ + @method dataWasUpdated + @private + @param {Class} type + @param {DS.Model} record + */ + dataWasUpdated: function(type, record) { + this.recordArrayManager.recordDidChange(record); + }, - var get = Ember.get, set = Ember.set; - var once = Ember.run.once; - var isNone = Ember.isNone; - var forEach = Ember.EnumerableUtils.forEach; - var indexOf = Ember.EnumerableUtils.indexOf; - var map = Ember.EnumerableUtils.map; - var Promise = Ember.RSVP.Promise; - var copy = Ember.copy; - var Store, PromiseObject, PromiseArray; + // .............. + // . PERSISTING . + // .............. - // Implementors Note: - // - // The variables in this file are consistently named according to the following - // scheme: - // - // * +id+ means an identifier managed by an external source, provided inside - // the data provided by that source. These are always coerced to be strings - // before being used internally. - // * +clientId+ means a transient numerical identifier generated at runtime by - // the data store. It is important primarily because newly created objects may - // not yet have an externally generated id. - // * +reference+ means a record reference object, which holds metadata about a - // record, even if it has not yet been fully materialized. - // * +type+ means a subclass of DS.Model. + /** + This method is called by `record.save`, and gets passed a + resolver for the promise that `record.save` returns. - // Used by the store to normalize IDs entering the store. Despite the fact - // that developers may provide IDs as numbers (e.g., `store.find(Person, 1)`), - // it is important that internally we use strings, since IDs may be serialized - // and lose type information. For example, Ember's router may put a record's - // ID into the URL, and if we later try to deserialize that URL and find the - // corresponding record, we will not know if it is a string or a number. - function coerceId(id) { - return id == null ? null : id+''; - } + It schedules saving to happen at the end of the run loop. - /** - The store contains all of the data for records loaded from the server. - It is also responsible for creating instances of `DS.Model` that wrap - the individual data for a record, so that they can be bound to in your - Handlebars templates. + @method scheduleSave + @private + @param {DS.Model} record + @param {Resolver} resolver + */ + scheduleSave: function(record, resolver) { + record.adapterWillCommit(); + this._pendingSave.push([record, resolver]); + ember$data$lib$system$store$$once(this, 'flushPendingSave'); + }, - Define your application's store like this: + /** + This method is called at the end of the run loop, and + flushes any records passed into `scheduleSave` - ```javascript - MyApp.Store = DS.Store.extend(); - ``` + @method flushPendingSave + @private + */ + flushPendingSave: function() { + var pending = this._pendingSave.slice(); + this._pendingSave = []; - Most Ember.js applications will only have a single `DS.Store` that is - automatically created by their `Ember.Application`. + ember$data$lib$system$store$$forEach(pending, function(tuple) { + var record = tuple[0], resolver = tuple[1]; + var adapter = this.adapterFor(record.constructor); + var operation; - You can retrieve models from the store in several ways. To retrieve a record - for a specific id, use `DS.Store`'s `find()` method: + if (ember$data$lib$system$store$$get(record, 'currentState.stateName') === 'root.deleted.saved') { + return resolver.resolve(record); + } else if (ember$data$lib$system$store$$get(record, 'isNew')) { + operation = 'createRecord'; + } else if (ember$data$lib$system$store$$get(record, 'isDeleted')) { + operation = 'deleteRecord'; + } else { + operation = 'updateRecord'; + } - ```javascript - var person = store.find('person', 123); - ``` + resolver.resolve(ember$data$lib$system$store$$_commit(adapter, this, operation, record)); + }, this); + }, - If your application has multiple `DS.Store` instances (an unusual case), you can - specify which store should be used: + /** + This method is called once the promise returned by an + adapter's `createRecord`, `updateRecord` or `deleteRecord` + is resolved. - ```javascript - var person = store.find(App.Person, 123); - ``` + If the data provides a server-generated ID, it will + update the record and the store's indexes. - By default, the store will talk to your backend using a standard - REST mechanism. You can customize how the store talks to your - backend by specifying a custom adapter: + @method didSaveRecord + @private + @param {DS.Model} record the in-flight record + @param {Object} data optional data (see above) + */ + didSaveRecord: function(record, data) { + if (data) { + // normalize relationship IDs into records + this._backburner.schedule('normalizeRelationships', this, '_setupRelationships', record, record.constructor, data); + this.updateId(record, data); + } - ```javascript - MyApp.store = DS.Store.create({ - adapter: 'MyApp.CustomAdapter' - }); - ``` + //We first make sure the primary data has been updated + //TODO try to move notification to the user to the end of the runloop + record.adapterDidCommit(data); + }, - You can learn more about writing a custom adapter by reading the `DS.Adapter` - documentation. + /** + This method is called once the promise returned by an + adapter's `createRecord`, `updateRecord` or `deleteRecord` + is rejected with a `DS.InvalidError`. - @class Store - @namespace DS - @extends Ember.Object - */ - Store = Ember.Object.extend({ + @method recordWasInvalid + @private + @param {DS.Model} record + @param {Object} errors + */ + recordWasInvalid: function(record, errors) { + record.adapterDidInvalidate(errors); + }, /** - @method init + This method is called once the promise returned by an + adapter's `createRecord`, `updateRecord` or `deleteRecord` + is rejected (with anything other than a `DS.InvalidError`). + + @method recordWasError @private + @param {DS.Model} record */ - init: function() { - // internal bookkeeping; not observable - this.typeMaps = {}; - this.recordArrayManager = DS.RecordArrayManager.create({ - store: this - }); - this._relationshipChanges = {}; - this._pendingSave = []; + recordWasError: function(record) { + record.adapterDidError(); }, /** - The adapter to use to communicate to a backend server or other persistence layer. + When an adapter's `createRecord`, `updateRecord` or `deleteRecord` + resolves with data, this method extracts the ID from the supplied + data. - This can be specified as an instance, class, or string. + @method updateId + @private + @param {DS.Model} record + @param {Object} data + */ + updateId: function(record, data) { + var oldId = ember$data$lib$system$store$$get(record, 'id'); + var id = ember$data$lib$system$store$$coerceId(data.id); - If you want to specify `App.CustomAdapter` as a string, do: + Ember.assert("An adapter cannot assign a new id to a record that already has an id. " + record + " had id: " + oldId + " and you tried to update it with " + id + ". This likely happened because your server returned data in response to a find or update that had a different id than the one you sent.", oldId === null || id === oldId); - ```js - adapter: 'custom' - ``` + this.typeMapFor(record.constructor).idToRecord[id] = record; - @property adapter - @default DS.RESTAdapter - @type {DS.Adapter|String} - */ - adapter: '-rest', + ember$data$lib$system$store$$set(record, 'id', id); + }, /** - Returns a JSON representation of the record using a custom - type-specific serializer, if one exists. - - The available options are: - - * `includeId`: `true` if the record's ID should be included in - the JSON representation + Returns a map of IDs to client IDs for a given type. - @method serialize + @method typeMapFor @private - @param {DS.Model} record the record to serialize - @param {Object} options an options hash + @param {subclass of DS.Model} type + @return {Object} typeMap */ - serialize: function(record, options) { - return this.serializerFor(record.constructor.typeKey).serialize(record, options); - }, + typeMapFor: function(type) { + var typeMaps = ember$data$lib$system$store$$get(this, 'typeMaps'); + var guid = Ember.guidFor(type); + var typeMap; - /** - This property returns the adapter, after resolving a possible - string key. + typeMap = typeMaps[guid]; - If the supplied `adapter` was a class, or a String property - path resolved to a class, this property will instantiate the - class. + if (typeMap) { return typeMap; } - This property is cacheable, so the same instance of a specified - adapter class should be used for the lifetime of the store. + typeMap = { + idToRecord: Ember.create(null), + records: [], + metadata: Ember.create(null), + type: type + }; - @property defaultAdapter + typeMaps[guid] = typeMap; + + return typeMap; + }, + + // ................ + // . LOADING DATA . + // ................ + + /** + This internal method is used by `push`. + + @method _load @private - @returns DS.Adapter + @param {String or subclass of DS.Model} type + @param {Object} data */ - defaultAdapter: Ember.computed('adapter', function() { - var adapter = get(this, 'adapter'); + _load: function(type, data) { + var id = ember$data$lib$system$store$$coerceId(data.id); + var record = this.recordForId(type, id); + + record.setupData(data); + this.recordArrayManager.recordDidChange(record); - Ember.assert('You tried to set `adapter` property to an instance of `DS.Adapter`, where it should be a name or a factory', !(adapter instanceof DS.Adapter)); + return record; + }, - if (typeof adapter === 'string') { - adapter = this.container.lookup('adapter:' + adapter) || this.container.lookup('adapter:application') || this.container.lookup('adapter:-rest'); - } + /** + Returns a model class for a particular key. Used by + methods that take a type key (like `find`, `createRecord`, + etc.) - if (DS.Adapter.detect(adapter)) { - adapter = adapter.create({ - container: this.container - }); + @method modelFor + @param {String or subclass of DS.Model} key + @return {subclass of DS.Model} + */ + modelFor: function(key) { + var factory; + + if (typeof key === 'string') { + factory = this.modelFactoryFor(key); + if (!factory) { + throw new Ember.Error("No model was found for '" + key + "'"); + } + factory.typeKey = factory.typeKey || this._normalizeTypeKey(key); + } else { + // A factory already supplied. Ensure it has a normalized key. + factory = key; + if (factory.typeKey) { + factory.typeKey = this._normalizeTypeKey(factory.typeKey); + } } - return adapter; - }), + factory.store = this; + return factory; + }, - // ..................... - // . CREATE NEW RECORD . - // ..................... + modelFactoryFor: function(key){ + return this.container.lookupFactory('model:' + key); + }, /** - Create a new record in the current store. The properties passed - to this method are set on the newly created record. + Push some data for a given type into the store. - To create a new instance of `App.Post`: + This method expects normalized data: + + * The ID is a key named `id` (an ID is mandatory) + * The names of attributes are the ones you used in + your model's `DS.attr`s. + * Your relationships must be: + * represented as IDs or Arrays of IDs + * represented as model instances + * represented as URLs, under the `links` key + + For this model: ```js - store.createRecord('post', { - title: "Rails is omakase" + App.Person = DS.Model.extend({ + firstName: DS.attr(), + lastName: DS.attr(), + + children: DS.hasMany('person') }); ``` - @method createRecord - @param {String} type - @param {Object} properties a hash of properties to set on the - newly created record. - @returns {DS.Model} record - */ - createRecord: function(type, properties) { - type = this.modelFor(type); + To represent the children as IDs: + + ```js + { + id: 1, + firstName: "Tom", + lastName: "Dale", + children: [1, 2, 3] + } + ``` + + To represent the children relationship as a URL: + + ```js + { + id: 1, + firstName: "Tom", + lastName: "Dale", + links: { + children: "/people/1/children" + } + } + ``` + + If you're streaming data or implementing an adapter, make sure + that you have converted the incoming data into this form. The + store's [normalize](#method_normalize) method is a convenience + helper for converting a json payload into the form Ember Data + expects. + + ```js + store.push('person', store.normalize('person', data)); + ``` + + This method can be used both to push in brand new + records, as well as to update existing records. - properties = copy(properties) || {}; + @method push + @param {String or subclass of DS.Model} type + @param {Object} data + @return {DS.Model} the record that was created or + updated. + */ + push: function(typeName, data) { + Ember.assert("Expected an object as `data` in a call to `push` for " + typeName + " , but was " + data, Ember.typeOf(data) === 'object'); + Ember.assert("You must include an `id` for " + typeName + " in an object passed to `push`", data.id != null && data.id !== ''); - // If the passed properties do not include a primary key, - // give the adapter an opportunity to generate one. Typically, - // client-side ID generators will use something like uuid.js - // to avoid conflicts. + var type = this.modelFor(typeName); + var filter = Ember.EnumerableUtils.filter; - if (isNone(properties.id)) { - properties.id = this._generateId(type); + // If the payload contains unused keys log a warning. + // Adding `Ember.ENV.DS_NO_WARN_ON_UNUSED_KEYS = true` will suppress the warning. + if (!Ember.ENV.DS_NO_WARN_ON_UNUSED_KEYS) { + Ember.warn("The payload for '" + type.typeKey + "' contains these unknown keys: " + + Ember.inspect(filter(Ember.keys(data), function(key) { + return !ember$data$lib$system$store$$get(type, 'fields').has(key) && key !== 'id' && key !== 'links'; + })) + ". Make sure they've been defined in your model.", + filter(Ember.keys(data), function(key) { + return !ember$data$lib$system$store$$get(type, 'fields').has(key) && key !== 'id' && key !== 'links'; + }).length === 0 + ); } - // Coerce ID to a string - properties.id = coerceId(properties.id); + // Actually load the record into the store. - var record = this.buildRecord(type, properties.id); + this._load(type, data); - // Move the record out of its initial `empty` state into - // the `loaded` state. - record.loadedData(); + var record = this.recordForId(type, data.id); + var store = this; - // Set the properties specified on the record. - record.setProperties(properties); + this._backburner.join(function() { + store._backburner.schedule('normalizeRelationships', store, '_setupRelationships', record, type, data); + }); return record; }, - /** - If possible, this method asks the adapter to generate an ID for - a newly created record. + _setupRelationships: function(record, type, data) { + // If the payload contains relationships that are specified as + // IDs, normalizeRelationships will convert them into DS.Model instances + // (possibly unloaded) before we push the payload into the + // store. - @method _generateId - @private - @param {String} type - @returns {String} if the adapter can generate one, an ID - */ - _generateId: function(type) { - var adapter = this.adapterFor(type); + data = ember$data$lib$system$store$$normalizeRelationships(this, type, data); - if (adapter && adapter.generateIdForRecord) { - return adapter.generateIdForRecord(this); - } - return null; + // Now that the pushed record as well as any related records + // are in the store, create the data structures used to track + // relationships. + ember$data$lib$system$store$$setupRelationships(this, record, data); }, - // ................. - // . DELETE RECORD . - // ................. - /** - For symmetry, a record can be deleted via the store. + Push some raw data into the store. - Example + This method can be used both to push in brand new + records, as well as to update existing records. You + can push in more than one type of object at once. + All objects should be in the format expected by the + serializer. - ```javascript - var post = store.createRecord('post', { - title: "Rails is omakase" - }); + ```js + App.ApplicationSerializer = DS.ActiveModelSerializer; - store.deleteRecord(post); + var pushData = { + posts: [ + {id: 1, post_title: "Great post", comment_ids: [2]} + ], + comments: [ + {id: 2, comment_body: "Insightful comment"} + ] + } + + store.pushPayload(pushData); ``` - @method deleteRecord - @param {DS.Model} record + By default, the data will be deserialized using a default + serializer (the application serializer if it exists). + + Alternatively, `pushPayload` will accept a model type which + will determine which serializer will process the payload. + However, the serializer itself (processing this data via + `normalizePayload`) will not know which model it is + deserializing. + + ```js + App.ApplicationSerializer = DS.ActiveModelSerializer; + App.PostSerializer = DS.JSONSerializer; + store.pushPayload('comment', pushData); // Will use the ApplicationSerializer + store.pushPayload('post', pushData); // Will use the PostSerializer + ``` + + @method pushPayload + @param {String} type Optionally, a model used to determine which serializer will be used + @param {Object} payload */ - deleteRecord: function(record) { - record.deleteRecord(); + pushPayload: function (type, inputPayload) { + var serializer; + var payload; + if (!inputPayload) { + payload = type; + serializer = ember$data$lib$system$store$$defaultSerializer(this.container); + Ember.assert("You cannot use `store#pushPayload` without a type unless your default serializer defines `pushPayload`", typeof serializer.pushPayload === 'function'); + } else { + payload = inputPayload; + serializer = this.serializerFor(type); + } + var store = this; + ember$data$lib$system$store$$_adapterRun(this, function() { + serializer.pushPayload(store, payload); + }); }, /** - For symmetry, a record can be unloaded via the store. Only - non-dirty records can be unloaded. + `normalize` converts a json payload into the normalized form that + [push](#method_push) expects. Example - ```javascript - store.find('post', 1).then(function(post) { - store.unloadRecord(post); + ```js + socket.on('message', function(message) { + var modelName = message.model; + var data = message.data; + store.push(modelName, store.normalize(modelName, data)); }); ``` - @method unloadRecord - @param {DS.Model} record + @method normalize + @param {String} type The name of the model type for this payload + @param {Object} payload + @return {Object} The normalized payload */ - unloadRecord: function(record) { - record.unloadRecord(); + normalize: function (type, payload) { + var serializer = this.serializerFor(type); + var model = this.modelFor(type); + return serializer.normalize(model, payload); }, - // ................ - // . FIND RECORDS . - // ................ + /** + @method update + @param {String} type + @param {Object} data + @return {DS.Model} the record that was updated. + @deprecated Use [push](#method_push) instead + */ + update: function(type, data) { + Ember.deprecate('Using store.update() has been deprecated since store.push() now handles partial updates. You should use store.push() instead.'); + return this.push(type, data); + }, /** - This is the main entry point into finding records. The first parameter to - this method is the model's name as a string. + If you have an Array of normalized data to push, + you can call `pushMany` with the Array, and it will + call `push` repeatedly for you. - --- + @method pushMany + @param {String or subclass of DS.Model} type + @param {Array} datas + @return {Array} + */ + pushMany: function(type, datas) { + var length = datas.length; + var result = new Array(length); - To find a record by ID, pass the `id` as the second parameter: + for (var i = 0; i < length; i++) { + result[i] = this.push(type, datas[i]); + } - ```javascript - store.find('person', 1); - ``` + return result; + }, - The `find` method will always return a **promise** that will be resolved - with the record. If the record was already in the store, the promise will - be resolved immediately. Otherwise, the store will ask the adapter's `find` - method to find the necessary data. + /** + @method metaForType + @param {String or subclass of DS.Model} typeName + @param {Object} metadata + @deprecated Use [setMetadataFor](#method_setMetadataFor) instead + */ + metaForType: function(typeName, metadata) { + Ember.deprecate('Using store.metaForType() has been deprecated. Use store.setMetadataFor() to set metadata for a specific type.'); + this.setMetadataFor(typeName, metadata); + }, - The `find` method will always resolve its promise with the same object for - a given type and `id`. + /** + Build a brand new record for a given type, ID, and + initial data. - --- + @method buildRecord + @private + @param {subclass of DS.Model} type + @param {String} id + @param {Object} data + @return {DS.Model} record + */ + buildRecord: function(type, id, data) { + var typeMap = this.typeMapFor(type); + var idToRecord = typeMap.idToRecord; - To find all records for a type, call `find` with no additional parameters: + Ember.assert('The id ' + id + ' has already been used with another record of type ' + type.toString() + '.', !id || !idToRecord[id]); + Ember.assert("`" + Ember.inspect(type)+ "` does not appear to be an ember-data model", (typeof type._create === 'function') ); - ```javascript - store.find('person'); - ``` + // lookupFactory should really return an object that creates + // instances with the injections applied + var record = type._create({ + id: id, + store: this, + container: this.container + }); - This will ask the adapter's `findAll` method to find the records for the - given type, and return a promise that will be resolved once the server - returns the values. + if (data) { + record.setupData(data); + } - --- + // if we're creating an item, this process will be done + // later, once the object has been persisted. + if (id) { + idToRecord[id] = record; + } - To find a record by a query, call `find` with a hash as the second - parameter: + typeMap.records.push(record); - ```javascript - store.find(App.Person, { page: 1 }); - ``` + return record; + }, - This will ask the adapter's `findQuery` method to find the records for - the query, and return a promise that will be resolved once the server - responds. + // ............... + // . DESTRUCTION . + // ............... - @method find - @param {String or subclass of DS.Model} type - @param {Object|String|Integer|null} id - @return {Promise} promise + /** + When a record is destroyed, this un-indexes it and + removes it from any record arrays so it can be GCed. + + @method dematerializeRecord + @private + @param {DS.Model} record */ - find: function(type, id) { - Ember.assert("You need to pass a type to the store's find method", arguments.length >= 1); - Ember.assert("You may not pass `" + id + "` as id to the store's find method", arguments.length === 1 || !Ember.isNone(id)); + dematerializeRecord: function(record) { + var type = record.constructor; + var typeMap = this.typeMapFor(type); + var id = ember$data$lib$system$store$$get(record, 'id'); - if (arguments.length === 1) { - return this.findAll(type); - } + record.updateRecordArrays(); - // We are passed a query instead of an id. - if (Ember.typeOf(id) === 'object') { - return this.findQuery(type, id); + if (id) { + delete typeMap.idToRecord[id]; } - return this.findById(type, coerceId(id)); + var loc = ember$data$lib$system$store$$indexOf(typeMap.records, record); + typeMap.records.splice(loc, 1); }, + // ...................... + // . PER-TYPE ADAPTERS + // ...................... + /** - This method returns a record for a given type and id combination. + Returns the adapter for a given type. - @method findById + @method adapterFor @private - @param {String or subclass of DS.Model} type - @param {String|Integer} id - @return {Promise} promise + @param {subclass of DS.Model} type + @return DS.Adapter */ - findById: function(type, id) { - type = this.modelFor(type); + adapterFor: function(type) { + var container = this.container, adapter; - var record = this.recordForId(type, id); - var fetchedRecord = this.fetchRecord(record); + if (container) { + adapter = container.lookup('adapter:' + type.typeKey) || container.lookup('adapter:application'); + } - return promiseObject(fetchedRecord || record, "DS: Store#findById " + type + " with id: " + id); + return adapter || ember$data$lib$system$store$$get(this, 'defaultAdapter'); }, + // .............................. + // . RECORD CHANGE NOTIFICATION . + // .............................. + /** - This method makes a series of requests to the adapter's `find` method - and returns a promise that resolves once they are all loaded. + Returns an instance of the serializer for a given type. For + example, `serializerFor('person')` will return an instance of + `App.PersonSerializer`. + + If no `App.PersonSerializer` is found, this method will look + for an `App.ApplicationSerializer` (the default serializer for + your entire application). + + If no `App.ApplicationSerializer` is found, it will fall back + to an instance of `DS.JSONSerializer`. + @method serializerFor @private - @method findByIds - @param {String} type - @param {Array} ids - @returns {Promise} promise + @param {String} type the record to serialize + @return {DS.Serializer} */ - findByIds: function(type, ids) { - var store = this; - var promiseLabel = "DS: Store#findByIds " + type; - return promiseArray(Ember.RSVP.all(map(ids, function(id) { - return store.findById(type, id); - })).then(Ember.A, null, "DS: Store#findByIds of " + type + " complete")); + serializerFor: function(type) { + type = this.modelFor(type); + var adapter = this.adapterFor(type); + + return ember$data$lib$system$store$$serializerFor(this.container, type.typeKey, adapter && adapter.defaultSerializer); + }, + + willDestroy: function() { + var typeMaps = this.typeMaps; + var keys = Ember.keys(typeMaps); + + var types = ember$data$lib$system$store$$map(keys, byType); + + this.recordArrayManager.destroy(); + + ember$data$lib$system$store$$forEach(types, this.unloadAll, this); + + function byType(entry) { + return typeMaps[entry]['type']; + } + }, /** - This method is called by `findById` if it discovers that a particular - type/id pair hasn't been loaded yet to kick off a request to the - adapter. + All typeKeys are camelCase internally. Changing this function may + require changes to other normalization hooks (such as typeForRoot). - @method fetchRecord + @method _normalizeTypeKey @private - @param {DS.Model} record - @returns {Promise} promise + @param {String} type + @return {String} if the adapter can generate one, an ID */ - fetchRecord: function(record) { - if (isNone(record)) { return null; } - if (record._loadingPromise) { return record._loadingPromise; } - if (!get(record, 'isEmpty')) { return null; } + _normalizeTypeKey: function(key) { + return ember$data$lib$system$store$$camelize(ember$inflector$lib$system$string$$singularize(key)); + } + }); - var type = record.constructor, - id = get(record, 'id'); - var adapter = this.adapterFor(type); + function ember$data$lib$system$store$$normalizeRelationships(store, type, data, record) { + type.eachRelationship(function(key, relationship) { + var kind = relationship.kind; + var value = data[key]; + if (kind === 'belongsTo') { + ember$data$lib$system$store$$deserializeRecordId(store, data, key, relationship, value); + } else if (kind === 'hasMany') { + ember$data$lib$system$store$$deserializeRecordIds(store, data, key, relationship, value); + } + }); - Ember.assert("You tried to find a record but you have no adapter (for " + type + ")", adapter); - Ember.assert("You tried to find a record but your adapter (for " + type + ") does not implement 'find'", adapter.find); + return data; + } - var promise = _find(adapter, this, type, id); - record.loadingData(promise); - return promise; - }, + function ember$data$lib$system$store$$deserializeRecordId(store, data, key, relationship, id) { + if (ember$data$lib$system$store$$isNone(id) || id instanceof ember$data$lib$system$model$model$$default) { + return; + } + Ember.assert("A " + relationship.parentType + " record was pushed into the store with the value of " + key + " being " + Ember.inspect(id) + ", but " + key + " is a belongsTo relationship so the value must not be an array. You should probably check your data payload or serializer.", !Ember.isArray(id)); - /** - Get a record by a given type and ID without triggering a fetch. + var type; - This method will synchronously return the record if it's available. - Otherwise, it will return null. + if (typeof id === 'number' || typeof id === 'string') { + type = ember$data$lib$system$store$$typeFor(relationship, key, data); + data[key] = store.recordForId(type, id); + } else if (typeof id === 'object') { + // polymorphic + Ember.assert('Ember Data expected a number or string to represent the record(s) in the `' + relationship.key + '` relationship instead it found an object. If this is a polymorphic relationship please specify a `type` key. If this is an embedded relationship please include the `DS.EmbeddedRecordsMixin` and specify the `' + relationship.key +'` property in your serializer\'s attrs hash.', id.type); + data[key] = store.recordForId(id.type, id.id); + } + } - ```js - var post = store.getById('post', 1); - ``` + function ember$data$lib$system$store$$typeFor(relationship, key, data) { + if (relationship.options.polymorphic) { + return data[key + "Type"]; + } else { + return relationship.type; + } + } - @method getById - @param {String or subclass of DS.Model} type - @param {String|Integer} id - @param {DS.Model} record - */ - getById: function(type, id) { - if (this.hasRecordForId(type, id)) { - return this.recordForId(type, id); - } else { - return null; - } - }, + function ember$data$lib$system$store$$deserializeRecordIds(store, data, key, relationship, ids) { + if (ember$data$lib$system$store$$isNone(ids)) { + return; + } - /** - This method is called by the record's `reload` method. + Ember.assert("A " + relationship.parentType + " record was pushed into the store with the value of " + key + " being '" + Ember.inspect(ids) + "', but " + key + " is a hasMany relationship so the value must be an array. You should probably check your data payload or serializer.", Ember.isArray(ids)); + for (var i=0, l=ids.length; i self.attributeLimit) { return false; } + var desc = ember$data$lib$system$debug$debug_adapter$$capitalize(ember$data$lib$system$debug$debug_adapter$$underscore(name).replace('_', ' ')); + columns.push({ name: name, desc: desc }); }); - ``` + return columns; + }, - @method filter - @param {String or subclass of DS.Model} type - @param {Object} query optional query - @param {Function} filter - @return {DS.PromiseArray} - */ - filter: function(type, query, filter) { - var promise; + getRecords: function(type) { + return this.get('store').all(type); + }, - // allow an optional server query - if (arguments.length === 3) { - promise = this.findQuery(type, query); - } else if (arguments.length === 2) { - filter = query; - } + getRecordColumnValues: function(record) { + var self = this, count = 0; + var columnValues = { id: ember$data$lib$system$debug$debug_adapter$$get(record, 'id') }; - type = this.modelFor(type); + record.eachAttribute(function(key) { + if (count++ > self.attributeLimit) { + return false; + } + var value = ember$data$lib$system$debug$debug_adapter$$get(record, key); + columnValues[key] = value; + }); + return columnValues; + }, - var array = this.recordArrayManager - .createFilteredRecordArray(type, filter); - promise = promise || Promise.cast(array); + getRecordKeywords: function(record) { + var keywords = []; + var keys = Ember.A(['id']); + record.eachAttribute(function(key) { + keys.push(key); + }); + keys.forEach(function(key) { + keywords.push(ember$data$lib$system$debug$debug_adapter$$get(record, key)); + }); + return keywords; + }, - return promiseArray(promise.then(function() { - return array; - }, null, "DS: Store#filter of " + type)); + getRecordFilterValues: function(record) { + return { + isNew: record.get('isNew'), + isModified: record.get('isDirty') && !record.get('isNew'), + isClean: !record.get('isDirty') + }; }, - /** - This method returns if a certain record is already loaded - in the store. Use this function to know beforehand if a find() - will result in a request or that it will be a cache hit. + getRecordColor: function(record) { + var color = 'black'; + if (record.get('isNew')) { + color = 'green'; + } else if (record.get('isDirty')) { + color = 'blue'; + } + return color; + }, - Example + observeRecord: function(record, recordUpdated) { + var releaseMethods = Ember.A(), self = this; + var keysToObserve = Ember.A(['id', 'isNew', 'isDirty']); - ```javascript - store.recordIsLoaded(App.Post, 1); // false - store.find(App.Post, 1).then(function() { - store.recordIsLoaded(App.Post, 1); // true + record.eachAttribute(function(key) { + keysToObserve.push(key); }); - ``` - @method recordIsLoaded - @param {String or subclass of DS.Model} type - @param {string} id - @return {boolean} - */ - recordIsLoaded: function(type, id) { - if (!this.hasRecordForId(type, id)) { return false; } - return !get(this.recordForId(type, id), 'isEmpty'); - }, + keysToObserve.forEach(function(key) { + var handler = function() { + recordUpdated(self.wrapRecord(record)); + }; + Ember.addObserver(record, key, handler); + releaseMethods.push(function() { + Ember.removeObserver(record, key, handler); + }); + }); - /** - This method returns the metadata for a specific type. + var release = function() { + releaseMethods.forEach(function(fn) { fn(); } ); + }; - @method metadataFor - @param {String or subclass of DS.Model} type - @return {object} - */ - metadataFor: function(type) { - type = this.modelFor(type); - return this.typeMapFor(type).metadata; - }, + return release; + } - // ............ - // . UPDATING . - // ............ + }); - /** - If the adapter updates attributes or acknowledges creation - or deletion, the record will notify the store to update its - membership in any filters. - To avoid thrashing, this method is invoked only once per + function ember$data$lib$initializers$data_adapter$$initializeDebugAdapter(container){ + container.register('data-adapter:main', ember$data$lib$system$debug$debug_adapter$$default); + } + var ember$data$lib$initializers$data_adapter$$default = ember$data$lib$initializers$data_adapter$$initializeDebugAdapter; + function ember$data$lib$setup$container$$setupContainer(container, application){ + // application is not a required argument. This ensures + // testing setups can setup a container without booting an + // entire ember application. + + ember$data$lib$initializers$data_adapter$$default(container, application); + ember$data$lib$initializers$transforms$$default(container, application); + ember$data$lib$initializers$store_injections$$default(container, application); + ember$data$lib$initializers$store$$default(container, application); + activemodel$adapter$lib$setup$container$$default(container, application); + } + var ember$data$lib$setup$container$$default = ember$data$lib$setup$container$$setupContainer; - run loop per record. + var ember$data$lib$ember$initializer$$K = Ember.K; - @method dataWasUpdated - @private - @param {Class} type - @param {DS.Model} record - */ - dataWasUpdated: function(type, record) { - this.recordArrayManager.recordDidChange(record); - }, + /** + @module ember-data + */ - // .............. - // . PERSISTING . - // .............. + /* - /** - This method is called by `record.save`, and gets passed a - resolver for the promise that `record.save` returns. + This code initializes Ember-Data onto an Ember application. - It schedules saving to happen at the end of the run loop. + If an Ember.js developer defines a subclass of DS.Store on their application, + as `App.ApplicationStore` (or via a module system that resolves to `store:application`) + this code will automatically instantiate it and make it available on the + router. - @method scheduleSave - @private - @param {DS.Model} record - @param {Resolver} resolver - */ - scheduleSave: function(record, resolver) { - record.adapterWillCommit(); - this._pendingSave.push([record, resolver]); - once(this, 'flushPendingSave'); - }, + Additionally, after an application's controllers have been injected, they will + each have the store made available to them. - /** - This method is called at the end of the run loop, and - flushes any records passed into `scheduleSave` + For example, imagine an Ember.js application with the following classes: - @method flushPendingSave - @private - */ - flushPendingSave: function() { - var pending = this._pendingSave.slice(); - this._pendingSave = []; + App.ApplicationStore = DS.Store.extend({ + adapter: 'custom' + }); - forEach(pending, function(tuple) { - var record = tuple[0], resolver = tuple[1], - adapter = this.adapterFor(record.constructor), - operation; + App.PostsController = Ember.ArrayController.extend({ + // ... + }); - if (get(record, 'isNew')) { - operation = 'createRecord'; - } else if (get(record, 'isDeleted')) { - operation = 'deleteRecord'; - } else { - operation = 'updateRecord'; - } + When the application is initialized, `App.ApplicationStore` will automatically be + instantiated, and the instance of `App.PostsController` will have its `store` + property set to that instance. - resolver.resolve(_commit(adapter, this, operation, record)); - }, this); - }, + Note that this code will only be run if the `ember-application` package is + loaded. If Ember Data is being used in an environment other than a + typical application (e.g., node.js where only `ember-runtime` is available), + this code will be ignored. + */ - /** - This method is called once the promise returned by an - adapter's `createRecord`, `updateRecord` or `deleteRecord` - is resolved. + Ember.onLoad('Ember.Application', function(Application) { - If the data provides a server-generated ID, it will - update the record and the store's indexes. + Application.initializer({ + name: "ember-data", + initialize: ember$data$lib$setup$container$$default + }); - @method didSaveRecord - @private - @param {DS.Model} record the in-flight record - @param {Object} data optional data (see above) - */ - didSaveRecord: function(record, data) { - if (data) { - // normalize relationship IDs into records - data = normalizeRelationships(this, record.constructor, data, record); + // Deprecated initializers to satisfy old code that depended on them - this.updateId(record, data); - } + Application.initializer({ + name: "store", + after: "ember-data", + initialize: ember$data$lib$ember$initializer$$K + }); - record.adapterDidCommit(data); - }, + Application.initializer({ + name: "activeModelAdapter", + before: "store", + initialize: ember$data$lib$ember$initializer$$K + }); - /** - This method is called once the promise returned by an - adapter's `createRecord`, `updateRecord` or `deleteRecord` - is rejected with a `DS.InvalidError`. + Application.initializer({ + name: "transforms", + before: "store", + initialize: ember$data$lib$ember$initializer$$K + }); - @method recordWasInvalid - @private - @param {DS.Model} record - @param {Object} errors - */ - recordWasInvalid: function(record, errors) { - record.adapterDidInvalidate(errors); - }, + Application.initializer({ + name: "data-adapter", + before: "store", + initialize: ember$data$lib$ember$initializer$$K + }); - /** - This method is called once the promise returned by an - adapter's `createRecord`, `updateRecord` or `deleteRecord` - is rejected (with anything other than a `DS.InvalidError`). + Application.initializer({ + name: "injectStore", + before: "store", + initialize: ember$data$lib$ember$initializer$$K + }); + }); + /** + @module ember-data + */ - @method recordWasError - @private - @param {DS.Model} record - */ - recordWasError: function(record) { - record.adapterDidError(); - }, + /** + Date.parse with progressive enhancement for ISO 8601 - /** - When an adapter's `createRecord`, `updateRecord` or `deleteRecord` - resolves with data, this method extracts the ID from the supplied - data. + © 2011 Colin Snover - @method updateId - @private - @param {DS.Model} record - @param {Object} data - */ - updateId: function(record, data) { - var oldId = get(record, 'id'), - id = coerceId(data.id); + Released under MIT license. - Ember.assert("An adapter cannot assign a new id to a record that already has an id. " + record + " had id: " + oldId + " and you tried to update it with " + id + ". This likely happened because your server returned data in response to a find or update that had a different id than the one you sent.", oldId === null || id === oldId); + @class Date + @namespace Ember + @static + */ + Ember.Date = Ember.Date || {}; - this.typeMapFor(record.constructor).idToRecord[id] = record; + var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ]; - set(record, 'id', id); - }, + /** + @method parse + @param {Date} date + @return {Number} timestamp + */ + Ember.Date.parse = function (date) { + var timestamp, struct, minutesOffset = 0; - /** - Returns a map of IDs to client IDs for a given type. + // ES5 §15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string + // before falling back to any implementation-specific date parsing, so that’s what we do, even if native + // implementations could be faster + // 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ± 10 tzHH 11 tzmm + if ((struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(date))) { + // avoid NaN timestamps caused by “undefined” values being passed to Date.UTC + for (var i = 0, k; (k = numericKeys[i]); ++i) { + struct[k] = +struct[k] || 0; + } - @method typeMapFor - @private - @param type - @return {Object} typeMap - */ - typeMapFor: function(type) { - var typeMaps = get(this, 'typeMaps'), - guid = Ember.guidFor(type), - typeMap; + // allow undefined days and months + struct[2] = (+struct[2] || 1) - 1; + struct[3] = +struct[3] || 1; - typeMap = typeMaps[guid]; + if (struct[8] !== 'Z' && struct[9] !== undefined) { + minutesOffset = struct[10] * 60 + struct[11]; - if (typeMap) { return typeMap; } + if (struct[9] === '+') { + minutesOffset = 0 - minutesOffset; + } + } - typeMap = { - idToRecord: {}, - records: [], - metadata: {}, - type: type - }; + timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]); + } + else { + timestamp = origParse ? origParse(date) : NaN; + } - typeMaps[guid] = typeMap; + return timestamp; + }; - return typeMap; - }, + if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Date) { + Date.parse = Ember.Date.parse; + } + /* + Detect if the user has a correct Object.create shim. + Ember has provided this for a long time but has had an incorrect shim before 1.8 + TODO: Remove for Ember Data 1.0. + */ + var object = Ember.create(null); + if (object.toString !== undefined && Ember.keys(Ember.create({}))[0] === '__proto__'){ + throw new Error("Ember Data requires a correct Object.create shim. You should upgrade to Ember >= 1.8 which provides one for you. If you are using ES5-shim, you should try removing that after upgrading Ember."); + } - // ................ - // . LOADING DATA . - // ................ + ember$data$lib$system$model$model$$default.reopen({ /** - This internal method is used by `push`. + Provides info about the model for debugging purposes + by grouping the properties into more semantic groups. - @method _load + Meant to be used by debugging tools such as the Chrome Ember Extension. + + - Groups all attributes in "Attributes" group. + - Groups all belongsTo relationships in "Belongs To" group. + - Groups all hasMany relationships in "Has Many" group. + - Groups all flags in "Flags" group. + - Flags relationship CPs as expensive properties. + + @method _debugInfo + @for DS.Model @private - @param {String or subclass of DS.Model} type - @param {Object} data - @param {Boolean} partial the data should be merged into - the existing data, not replace it. */ - _load: function(type, data, partial) { - var id = coerceId(data.id), - record = this.recordForId(type, id); + _debugInfo: function() { + var attributes = ['id'], + relationships = { belongsTo: [], hasMany: [] }, + expensiveProperties = []; + + this.eachAttribute(function(name, meta) { + attributes.push(name); + }, this); - record.setupData(data, partial); - this.recordArrayManager.recordDidChange(record); + this.eachRelationship(function(name, relationship) { + relationships[relationship.kind].push(name); + expensiveProperties.push(name); + }); - return record; - }, + var groups = [ + { + name: 'Attributes', + properties: attributes, + expand: true + }, + { + name: 'Belongs To', + properties: relationships.belongsTo, + expand: true + }, + { + name: 'Has Many', + properties: relationships.hasMany, + expand: true + }, + { + name: 'Flags', + properties: ['isLoaded', 'isDirty', 'isSaving', 'isDeleted', 'isError', 'isNew', 'isValid'] + } + ]; - /** - Returns a model class for a particular key. Used by - methods that take a type key (like `find`, `createRecord`, - etc.) + return { + propertyInfo: { + // include all other mixins / properties (not just the grouped ones) + includeOtherProperties: true, + groups: groups, + // don't pre-calculate unless cached + expensiveProperties: expensiveProperties + } + }; + } + }); - @method modelFor - @param {String or subclass of DS.Model} key - @returns {subclass of DS.Model} - */ - modelFor: function(key) { - var factory; + var ember$data$lib$system$debug$debug_info$$default = ember$data$lib$system$model$model$$default; + var ember$data$lib$system$debug$$default = ember$data$lib$system$debug$debug_adapter$$default; + var ember$data$lib$serializers$embedded_records_mixin$$get = Ember.get; + var ember$data$lib$serializers$embedded_records_mixin$$forEach = Ember.EnumerableUtils.forEach; + var ember$data$lib$serializers$embedded_records_mixin$$camelize = Ember.String.camelize; + /** + ## Using Embedded Records - if (typeof key === 'string') { - var normalizedKey = this.container.normalize('model:' + key); + `DS.EmbeddedRecordsMixin` supports serializing embedded records. - factory = this.container.lookupFactory(normalizedKey); - if (!factory) { throw new Ember.Error("No model was found for '" + key + "'"); } - factory.typeKey = normalizedKey.split(':', 2)[1]; - } else { - // A factory already supplied. - factory = key; - } + To set up embedded records, include the mixin when extending a serializer + then define and configure embedded (model) relationships. - factory.store = this; - return factory; - }, + Below is an example of a per-type serializer ('post' type). - /** - Push some data for a given type into the store. + ```js + App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + author: { embedded: 'always' }, + comments: { serialize: 'ids' } + } + }); + ``` + Note that this use of `{ embedded: 'always' }` is unrelated to + the `{ embedded: 'always' }` that is defined as an option on `DS.attr` as part of + defining a model while working with the ActiveModelSerializer. Nevertheless, + using `{ embedded: 'always' }` as an option to DS.attr is not a valid way to setup + embedded records. - This method expects normalized data: + The `attrs` option for a resource `{ embedded: 'always' }` is shorthand for: - * The ID is a key named `id` (an ID is mandatory) - * The names of attributes are the ones you used in - your model's `DS.attr`s. - * Your relationships must be: - * represented as IDs or Arrays of IDs - * represented as model instances - * represented as URLs, under the `links` key + ```js + { + serialize: 'records', + deserialize: 'records' + } + ``` - For this model: + ### Configuring Attrs - ```js - App.Person = DS.Model.extend({ - firstName: DS.attr(), - lastName: DS.attr(), + A resource's `attrs` option may be set to use `ids`, `records` or false for the + `serialize` and `deserialize` settings. - children: DS.hasMany('person') - }); - ``` + The `attrs` property can be set on the ApplicationSerializer or a per-type + serializer. - To represent the children as IDs: + In the case where embedded JSON is expected while extracting a payload (reading) + the setting is `deserialize: 'records'`, there is no need to use `ids` when + extracting as that is the default behavior without this mixin if you are using + the vanilla EmbeddedRecordsMixin. Likewise, to embed JSON in the payload while + serializing `serialize: 'records'` is the setting to use. There is an option of + not embedding JSON in the serialized payload by using `serialize: 'ids'`. If you + do not want the relationship sent at all, you can use `serialize: false`. - ```js - { - id: 1, - firstName: "Tom", - lastName: "Dale", - children: [1, 2, 3] - } - ``` - To represent the children relationship as a URL: + ### EmbeddedRecordsMixin defaults + If you do not overwrite `attrs` for a specific relationship, the `EmbeddedRecordsMixin` + will behave in the following way: - ```js - { - id: 1, - firstName: "Tom", - lastName: "Dale", - links: { - children: "/people/1/children" - } - } - ``` + BelongsTo: `{ serialize: 'id', deserialize: 'id' }` + HasMany: `{ serialize: false, deserialize: 'ids' }` - If you're streaming data or implementing an adapter, - make sure that you have converted the incoming data - into this form. + ### Model Relationships - This method can be used both to push in brand new - records, as well as to update existing records. + Embedded records must have a model defined to be extracted and serialized. Note that + when defining any relationships on your model such as `belongsTo` and `hasMany`, you + should not both specify `async:true` and also indicate through the serializer's + `attrs` attribute that the related model should be embedded. If a model is + declared embedded, then do not use `async:true`. - @method push - @param {String or subclass of DS.Model} type - @param {Object} data - @returns {DS.Model} the record that was created or - updated. - */ - push: function(type, data, _partial) { - // _partial is an internal param used by `update`. - // If passed, it means that the data should be - // merged into the existing data, not replace it. + To successfully extract and serialize embedded records the model relationships + must be setup correcty See the + [defining relationships](/guides/models/defining-models/#toc_defining-relationships) + section of the **Defining Models** guide page. - Ember.assert("You must include an `id` in a hash passed to `push`", data.id != null); + Records without an `id` property are not considered embedded records, model + instances must have an `id` property to be used with Ember Data. - type = this.modelFor(type); + ### Example JSON payloads, Models and Serializers - // normalize relationship IDs into records - data = normalizeRelationships(this, type, data); + **When customizing a serializer it is important to grok what the customizations + are. Please read the docs for the methods this mixin provides, in case you need + to modify it to fit your specific needs.** - this._load(type, data, _partial); + For example review the docs for each method of this mixin: + * [normalize](/api/data/classes/DS.EmbeddedRecordsMixin.html#method_normalize) + * [serializeBelongsTo](/api/data/classes/DS.EmbeddedRecordsMixin.html#method_serializeBelongsTo) + * [serializeHasMany](/api/data/classes/DS.EmbeddedRecordsMixin.html#method_serializeHasMany) - return this.recordForId(type, data.id); - }, + @class EmbeddedRecordsMixin + @namespace DS + */ + var ember$data$lib$serializers$embedded_records_mixin$$EmbeddedRecordsMixin = Ember.Mixin.create({ /** - Push some raw data into the store. - - The data will be automatically deserialized using the - serializer for the `type` param. - - This method can be used both to push in brand new - records, as well as to update existing records. + Normalize the record and recursively normalize/extract all the embedded records + while pushing them into the store as they are encountered - You can push in more than one type of object at once. - All objects should be in the format expected by the - serializer. + A payload with an attr configured for embedded records needs to be extracted: ```js - App.ApplicationSerializer = DS.ActiveModelSerializer; - - var pushData = { - posts: [ - {id: 1, post_title: "Great post", comment_ids: [2]} - ], - comments: [ - {id: 2, comment_body: "Insightful comment"} - ] + { + "post": { + "id": "1" + "title": "Rails is omakase", + "comments": [{ + "id": "1", + "body": "Rails is unagi" + }, { + "id": "2", + "body": "Omakase O_o" + }] + } } - - store.pushPayload('post', pushData); ``` + @method normalize + @param {subclass of DS.Model} type + @param {Object} hash to be normalized + @param {String} key the hash has been referenced by + @return {Object} the normalized hash + **/ + normalize: function(type, hash, prop) { + var normalizedHash = this._super(type, hash, prop); + return ember$data$lib$serializers$embedded_records_mixin$$extractEmbeddedRecords(this, this.store, type, normalizedHash); + }, - @method pushPayload - @param {String} type - @param {Object} payload - */ - pushPayload: function (type, payload) { - var serializer; - if (!payload) { - payload = type; - serializer = defaultSerializer(this.container); - Ember.assert("You cannot use `store#pushPayload` without a type unless your default serializer defines `pushPayload`", serializer.pushPayload); + keyForRelationship: function(key, type){ + if (this.hasDeserializeRecordsOption(key)) { + return this.keyForAttribute(key); } else { - serializer = this.serializerFor(type); + return this._super(key, type) || key; } - serializer.pushPayload(this, payload); - }, - - update: function(type, data) { - Ember.assert("You must include an `id` in a hash passed to `update`", data.id != null); - - return this.push(type, data, true); - }, - - /** - If you have an Array of normalized data to push, - you can call `pushMany` with the Array, and it will - call `push` repeatedly for you. - - @method pushMany - @param {String or subclass of DS.Model} type - @param {Array} datas - @return {Array} - */ - pushMany: function(type, datas) { - return map(datas, function(data) { - return this.push(type, data); - }, this); }, /** - If you have some metadata to set for a type - you can call `metaForType`. + Serialize `belongsTo` relationship when it is configured as an embedded object. - @method metaForType - @param {String or subclass of DS.Model} type - @param {Object} metadata - */ - metaForType: function(type, metadata) { - type = this.modelFor(type); + This example of an author model belongs to a post model: - Ember.merge(this.typeMapFor(type).metadata, metadata); - }, + ```js + Post = DS.Model.extend({ + title: DS.attr('string'), + body: DS.attr('string'), + author: DS.belongsTo('author') + }); - /** - Build a brand new record for a given type, ID, and - initial data. + Author = DS.Model.extend({ + name: DS.attr('string'), + post: DS.belongsTo('post') + }); + ``` - @method buildRecord - @private - @param {subclass of DS.Model} type - @param {String} id - @param {Object} data - @returns {DS.Model} record - */ - buildRecord: function(type, id, data) { - var typeMap = this.typeMapFor(type), - idToRecord = typeMap.idToRecord; + Use a custom (type) serializer for the post model to configure embedded author - Ember.assert('The id ' + id + ' has already been used with another record of type ' + type.toString() + '.', !id || !idToRecord[id]); - Ember.assert("`" + Ember.inspect(type)+ "` does not appear to be an ember-data model", (typeof type._create === 'function') ); + ```js + App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + author: {embedded: 'always'} + } + }) + ``` - // lookupFactory should really return an object that creates - // instances with the injections applied - var record = type._create({ - id: id, - store: this, - container: this.container - }); + A payload with an attribute configured for embedded records can serialize + the records together under the root attribute's payload: - if (data) { - record.setupData(data); + ```js + { + "post": { + "id": "1" + "title": "Rails is omakase", + "author": { + "id": "2" + "name": "dhh" + } + } } + ``` - // if we're creating an item, this process will be done - // later, once the object has been persisted. - if (id) { - idToRecord[id] = record; + @method serializeBelongsTo + @param {DS.Model} record + @param {Object} json + @param {Object} relationship + */ + serializeBelongsTo: function(record, json, relationship) { + var attr = relationship.key; + if (this.noSerializeOptionSpecified(attr)) { + this._super(record, json, relationship); + return; + } + var includeIds = this.hasSerializeIdsOption(attr); + var includeRecords = this.hasSerializeRecordsOption(attr); + var embeddedRecord = record.get(attr); + var key; + if (includeIds) { + key = this.keyForRelationship(attr, relationship.kind); + if (!embeddedRecord) { + json[key] = null; + } else { + json[key] = ember$data$lib$serializers$embedded_records_mixin$$get(embeddedRecord, 'id'); + } + } else if (includeRecords) { + key = this.keyForAttribute(attr); + if (!embeddedRecord) { + json[key] = null; + } else { + json[key] = embeddedRecord.serialize({includeId: true}); + this.removeEmbeddedForeignKey(record, embeddedRecord, relationship, json[key]); + } } - - typeMap.records.push(record); - - return record; }, - // ............... - // . DESTRUCTION . - // ............... - /** - When a record is destroyed, this un-indexes it and - removes it from any record arrays so it can be GCed. - - @method dematerializeRecord - @private - @param {DS.Model} record - */ - dematerializeRecord: function(record) { - var type = record.constructor, - typeMap = this.typeMapFor(type), - id = get(record, 'id'); - - record.updateRecordArrays(); - - if (id) { - delete typeMap.idToRecord[id]; - } + Serialize `hasMany` relationship when it is configured as embedded objects. - var loc = indexOf(typeMap.records, record); - typeMap.records.splice(loc, 1); - }, + This example of a post model has many comments: - // ........................ - // . RELATIONSHIP CHANGES . - // ........................ + ```js + Post = DS.Model.extend({ + title: DS.attr('string'), + body: DS.attr('string'), + comments: DS.hasMany('comment') + }); - addRelationshipChangeFor: function(childRecord, childKey, parentRecord, parentKey, change) { - var clientId = childRecord.clientId, - parentClientId = parentRecord ? parentRecord : parentRecord; - var key = childKey + parentKey; - var changes = this._relationshipChanges; - if (!(clientId in changes)) { - changes[clientId] = {}; - } - if (!(parentClientId in changes[clientId])) { - changes[clientId][parentClientId] = {}; - } - if (!(key in changes[clientId][parentClientId])) { - changes[clientId][parentClientId][key] = {}; - } - changes[clientId][parentClientId][key][change.changeType] = change; - }, + Comment = DS.Model.extend({ + body: DS.attr('string'), + post: DS.belongsTo('post') + }); + ``` - removeRelationshipChangeFor: function(clientRecord, childKey, parentRecord, parentKey, type) { - var clientId = clientRecord.clientId, - parentClientId = parentRecord ? parentRecord.clientId : parentRecord; - var changes = this._relationshipChanges; - var key = childKey + parentKey; - if (!(clientId in changes) || !(parentClientId in changes[clientId]) || !(key in changes[clientId][parentClientId])){ - return; - } - delete changes[clientId][parentClientId][key][type]; - }, + Use a custom (type) serializer for the post model to configure embedded comments - relationshipChangePairsFor: function(record){ - var toReturn = []; + ```js + App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + comments: {embedded: 'always'} + } + }) + ``` - if( !record ) { return toReturn; } + A payload with an attribute configured for embedded records can serialize + the records together under the root attribute's payload: - //TODO(Igor) What about the other side - var changesObject = this._relationshipChanges[record.clientId]; - for (var objKey in changesObject){ - if(changesObject.hasOwnProperty(objKey)){ - for (var changeKey in changesObject[objKey]){ - if(changesObject[objKey].hasOwnProperty(changeKey)){ - toReturn.push(changesObject[objKey][changeKey]); - } - } + ```js + { + "post": { + "id": "1" + "title": "Rails is omakase", + "body": "I want this for my ORM, I want that for my template language..." + "comments": [{ + "id": "1", + "body": "Rails is unagi" + }, { + "id": "2", + "body": "Omakase O_o" + }] } } - return toReturn; - }, + ``` - // ...................... - // . PER-TYPE ADAPTERS - // ...................... + The attrs options object can use more specific instruction for extracting and + serializing. When serializing, an option to embed `ids` or `records` can be set. + When extracting the only option is `records`. - /** - Returns the adapter for a given type. + So `{embedded: 'always'}` is shorthand for: + `{serialize: 'records', deserialize: 'records'}` - @method adapterFor - @private - @param {subclass of DS.Model} type - @returns DS.Adapter - */ - adapterFor: function(type) { - var container = this.container, adapter; + To embed the `ids` for a related object (using a hasMany relationship): - if (container) { - adapter = container.lookup('adapter:' + type.typeKey) || container.lookup('adapter:application'); + ```js + App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + comments: {serialize: 'ids', deserialize: 'records'} + } + }) + ``` + + ```js + { + "post": { + "id": "1" + "title": "Rails is omakase", + "body": "I want this for my ORM, I want that for my template language..." + "comments": ["1", "2"] + } } + ``` - return adapter || get(this, 'defaultAdapter'); + @method serializeHasMany + @param {DS.Model} record + @param {Object} json + @param {Object} relationship + */ + serializeHasMany: function(record, json, relationship) { + var attr = relationship.key; + if (this.noSerializeOptionSpecified(attr)) { + this._super(record, json, relationship); + return; + } + var includeIds = this.hasSerializeIdsOption(attr); + var includeRecords = this.hasSerializeRecordsOption(attr); + var key; + if (includeIds) { + key = this.keyForRelationship(attr, relationship.kind); + json[key] = ember$data$lib$serializers$embedded_records_mixin$$get(record, attr).mapBy('id'); + } else if (includeRecords) { + key = this.keyForAttribute(attr); + json[key] = ember$data$lib$serializers$embedded_records_mixin$$get(record, attr).map(function(embeddedRecord) { + var serializedEmbeddedRecord = embeddedRecord.serialize({includeId: true}); + this.removeEmbeddedForeignKey(record, embeddedRecord, relationship, serializedEmbeddedRecord); + return serializedEmbeddedRecord; + }, this); + } }, - // .............................. - // . RECORD CHANGE NOTIFICATION . - // .............................. - /** - Returns an instance of the serializer for a given type. For - example, `serializerFor('person')` will return an instance of - `App.PersonSerializer`. + When serializing an embedded record, modify the property (in the json payload) + that refers to the parent record (foreign key for relationship). - If no `App.PersonSerializer` is found, this method will look - for an `App.ApplicationSerializer` (the default serializer for - your entire application). + Serializing a `belongsTo` relationship removes the property that refers to the + parent record - If no `App.ApplicationSerializer` is found, it will fall back - to an instance of `DS.JSONSerializer`. + Serializing a `hasMany` relationship does not remove the property that refers to + the parent record. - @method serializerFor - @private - @param {String} type the record to serialize - @return {DS.Serializer} + @method removeEmbeddedForeignKey + @param {DS.Model} record + @param {DS.Model} embeddedRecord + @param {Object} relationship + @param {Object} json */ - serializerFor: function(type) { - type = this.modelFor(type); - var adapter = this.adapterFor(type); + removeEmbeddedForeignKey: function (record, embeddedRecord, relationship, json) { + if (relationship.kind === 'hasMany') { + return; + } else if (relationship.kind === 'belongsTo') { + var parentRecord = record.constructor.inverseFor(relationship.key); + if (parentRecord) { + var name = parentRecord.name; + var embeddedSerializer = this.store.serializerFor(embeddedRecord.constructor); + var parentKey = embeddedSerializer.keyForRelationship(name, parentRecord.kind); + if (parentKey) { + delete json[parentKey]; + } + } + } + }, - return serializerFor(this.container, type.typeKey, adapter && adapter.defaultSerializer); + // checks config for attrs option to embedded (always) - serialize and deserialize + hasEmbeddedAlwaysOption: function (attr) { + var option = this.attrsOption(attr); + return option && option.embedded === 'always'; }, - willDestroy: function() { - var map = this.typeMaps; - var keys = Ember.keys(map); - var store = this; - var types = keys.map(byType); + // checks config for attrs option to serialize ids + hasSerializeRecordsOption: function(attr) { + var alwaysEmbed = this.hasEmbeddedAlwaysOption(attr); + var option = this.attrsOption(attr); + return alwaysEmbed || (option && (option.serialize === 'records')); + }, - this.recordArrayManager.destroy(); + // checks config for attrs option to serialize records + hasSerializeIdsOption: function(attr) { + var option = this.attrsOption(attr); + return option && (option.serialize === 'ids' || option.serialize === 'id'); + }, - types.forEach(this.unloadAll, this); + // checks config for attrs option to serialize records + noSerializeOptionSpecified: function(attr) { + var option = this.attrsOption(attr); + return !(option && (option.serialize || option.embedded)); + }, - function byType(entry) { - return map[entry].type; - } + // checks config for attrs option to deserialize records + // a defined option object for a resource is treated the same as + // `deserialize: 'records'` + hasDeserializeRecordsOption: function(attr) { + var alwaysEmbed = this.hasEmbeddedAlwaysOption(attr); + var option = this.attrsOption(attr); + return alwaysEmbed || (option && option.deserialize === 'records'); + }, + + attrsOption: function(attr) { + var attrs = this.get('attrs'); + return attrs && (attrs[ember$data$lib$serializers$embedded_records_mixin$$camelize(attr)] || attrs[attr]); } }); - function normalizeRelationships(store, type, data, record) { + // chooses a relationship kind to branch which function is used to update payload + // does not change payload if attr is not embedded + function ember$data$lib$serializers$embedded_records_mixin$$extractEmbeddedRecords(serializer, store, type, partial) { + type.eachRelationship(function(key, relationship) { - // A link (usually a URL) was already provided in - // normalized form - if (data.links && data.links[key]) { - if (record && relationship.options.async) { record._relationships[key] = null; } - return; + if (serializer.hasDeserializeRecordsOption(key)) { + var embeddedType = store.modelFor(relationship.type.typeKey); + if (relationship.kind === "hasMany") { + if (relationship.options.polymorphic) { + ember$data$lib$serializers$embedded_records_mixin$$extractEmbeddedHasManyPolymorphic(store, key, partial); + } + else { + ember$data$lib$serializers$embedded_records_mixin$$extractEmbeddedHasMany(store, key, embeddedType, partial); + } + } + if (relationship.kind === "belongsTo") { + ember$data$lib$serializers$embedded_records_mixin$$extractEmbeddedBelongsTo(store, key, embeddedType, partial); + } } + }); - var kind = relationship.kind, - value = data[key]; + return partial; + } - if (value == null) { return; } + // handles embedding for `hasMany` relationship + function ember$data$lib$serializers$embedded_records_mixin$$extractEmbeddedHasMany(store, key, embeddedType, hash) { + if (!hash[key]) { + return hash; + } - if (kind === 'belongsTo') { - deserializeRecordId(store, data, key, relationship, value); - } else if (kind === 'hasMany') { - deserializeRecordIds(store, data, key, relationship, value); - addUnsavedRecords(record, key, value); - } + var ids = []; + + var embeddedSerializer = store.serializerFor(embeddedType.typeKey); + ember$data$lib$serializers$embedded_records_mixin$$forEach(hash[key], function(data) { + var embeddedRecord = embeddedSerializer.normalize(embeddedType, data, null); + store.push(embeddedType, embeddedRecord); + ids.push(embeddedRecord.id); }); - return data; + hash[key] = ids; + return hash; } - function deserializeRecordId(store, data, key, relationship, id) { - if (isNone(id) || id instanceof DS.Model) { - return; + function ember$data$lib$serializers$embedded_records_mixin$$extractEmbeddedHasManyPolymorphic(store, key, hash) { + if (!hash[key]) { + return hash; } - var type; + var ids = []; - if (typeof id === 'number' || typeof id === 'string') { - type = typeFor(relationship, key, data); - data[key] = store.recordForId(type, id); - } else if (typeof id === 'object') { - // polymorphic - data[key] = store.recordForId(id.type, id.id); - } - } + ember$data$lib$serializers$embedded_records_mixin$$forEach(hash[key], function(data) { + var typeKey = data.type; + var embeddedSerializer = store.serializerFor(typeKey); + var embeddedType = store.modelFor(typeKey); + var primaryKey = ember$data$lib$serializers$embedded_records_mixin$$get(embeddedSerializer, 'primaryKey'); - function typeFor(relationship, key, data) { - if (relationship.options.polymorphic) { - return data[key + "Type"]; - } else { - return relationship.type; - } - } + var embeddedRecord = embeddedSerializer.normalize(embeddedType, data, null); + store.push(embeddedType, embeddedRecord); + ids.push({ id: embeddedRecord[primaryKey], type: typeKey }); + }); - function deserializeRecordIds(store, data, key, relationship, ids) { - for (var i=0, l=ids.length; i1) { + if ( value === undefined ) { + value = null; + } + if (value && value.then) { + this._relationships[key].setRecordPromise(value); + } else { + this._relationships[key].setRecord(value); + } + } + + return this._relationships[key].getRecord(); + }).meta(meta); + } + + /** + These observers observe all `belongsTo` relationships on the record. See + `relationships/ext` to see how these observers get their dependencies. + + @class Model @namespace DS - @extends Ember.ArrayProxy - @uses Ember.PromiseProxyMixin */ - PromiseArray = Ember.ArrayProxy.extend(Ember.PromiseProxyMixin); + ember$data$lib$system$model$model$$default.reopen({ + notifyBelongsToChanged: function(key) { + this.notifyPropertyChange(key); + } + }); + + var ember$data$lib$system$relationships$belongs_to$$default = ember$data$lib$system$relationships$belongs_to$$belongsTo; + /** - A `PromiseObject` is an object that acts like both an `Ember.Object` - and a promise. When the promise is resolved the the resulting value - will be set to the `PromiseObject`'s `content` property. This makes - it easy to create data bindings with the `PromiseObject` that will - be updated when the promise resolves. + `DS.hasMany` is used to define One-To-Many and Many-To-Many + relationships on a [DS.Model](/api/data/classes/DS.Model.html). - For more information see the [Ember.PromiseProxyMixin - documentation](/api/classes/Ember.PromiseProxyMixin.html). + `DS.hasMany` takes an optional hash as a second parameter, currently + supported options are: - Example + - `async`: A boolean value used to explicitly declare this to be an async relationship. + - `inverse`: A string used to identify the inverse property on a related model. + + #### One-To-Many + To declare a one-to-many relationship between two models, use + `DS.belongsTo` in combination with `DS.hasMany`, like this: ```javascript - var promiseObject = DS.PromiseObject.create({ - promise: $.getJSON('/some/remote/data.json') + App.Post = DS.Model.extend({ + comments: DS.hasMany('comment') + }); + + App.Comment = DS.Model.extend({ + post: DS.belongsTo('post') + }); + ``` + + #### Many-To-Many + To declare a many-to-many relationship between two models, use + `DS.hasMany`: + + ```javascript + App.Post = DS.Model.extend({ + tags: DS.hasMany('tag') + }); + + App.Tag = DS.Model.extend({ + posts: DS.hasMany('post') }); + ``` - promiseObject.get('name'); // null + You can avoid passing a string as the first parameter. In that case Ember Data + will infer the type from the singularized key name. - promiseObject.then(function() { - promiseObject.get('name'); // 'Tomster' + ```javascript + App.Post = DS.Model.extend({ + tags: DS.hasMany() }); ``` - @class PromiseObject - @namespace DS - @extends Ember.ObjectProxy - @uses Ember.PromiseProxyMixin - */ - PromiseObject = Ember.ObjectProxy.extend(Ember.PromiseProxyMixin); + will lookup for a Tag type. - function promiseObject(promise, label) { - return PromiseObject.create({ - promise: Promise.cast(promise, label) - }); - } + #### Explicit Inverses - function promiseArray(promise, label) { - return PromiseArray.create({ - promise: Promise.cast(promise, label) - }); - } + Ember Data will do its best to discover which relationships map to + one another. In the one-to-many code above, for example, Ember Data + can figure out that changing the `comments` relationship should update + the `post` relationship on the inverse because post is the only + relationship to that model. - function isThenable(object) { - return object && typeof object.then === 'function'; - } + However, sometimes you may have multiple `belongsTo`/`hasManys` for the + same type. You can specify which property on the related model is + the inverse using `DS.hasMany`'s `inverse` option: - function serializerFor(container, type, defaultSerializer) { - return container.lookup('serializer:'+type) || - container.lookup('serializer:application') || - container.lookup('serializer:' + defaultSerializer) || - container.lookup('serializer:-default'); - } + ```javascript + var belongsTo = DS.belongsTo, + hasMany = DS.hasMany; - function defaultSerializer(container) { - return container.lookup('serializer:application') || - container.lookup('serializer:-default'); - } + App.Comment = DS.Model.extend({ + onePost: belongsTo('post'), + twoPost: belongsTo('post'), + redPost: belongsTo('post'), + bluePost: belongsTo('post') + }); - function serializerForAdapter(adapter, type) { - var serializer = adapter.serializer, - defaultSerializer = adapter.defaultSerializer, - container = adapter.container; + App.Post = DS.Model.extend({ + comments: hasMany('comment', { + inverse: 'redPost' + }) + }); + ``` - if (container && serializer === undefined) { - serializer = serializerFor(container, type.typeKey, defaultSerializer); - } + You can also specify an inverse on a `belongsTo`, which works how + you'd expect. - if (serializer === null || serializer === undefined) { - serializer = { - extract: function(store, type, payload) { return payload; } - }; + @namespace + @method hasMany + @for DS + @param {String} type (optional) type of the relationship + @param {Object} options (optional) a hash of options + @return {Ember.computed} relationship + */ + function ember$data$lib$system$relationships$has_many$$hasMany(type, options) { + if (typeof type === 'object') { + options = type; + type = undefined; } - return serializer; - } + Ember.assert("The first argument to DS.hasMany must be a string representing a model type key, not an instance of " + Ember.inspect(type) + ". E.g., to define a relation to the Comment model, use DS.hasMany('comment')", typeof type === 'string' || typeof type === 'undefined'); - function _find(adapter, store, type, id) { - var promise = adapter.find(store, type, id), - serializer = serializerForAdapter(adapter, type), - label = "DS: Handle Adapter#find of " + type + " with id: " + id; + options = options || {}; - return Promise.cast(promise, label).then(function(adapterPayload) { - Ember.assert("You made a request for a " + type.typeKey + " with id " + id + ", but the adapter's response did not have any data", adapterPayload); - var payload = serializer.extract(store, type, adapterPayload, id, 'find'); + // Metadata about relationships is stored on the meta of + // the relationship. This is used for introspection and + // serialization. Note that `key` is populated lazily + // the first time the CP is called. + var meta = { + type: type, + isRelationship: true, + options: options, + kind: 'hasMany', + key: null + }; - return store.push(type, payload); - }, function(error) { - var record = store.getById(type, id); - record.notFound(); - throw error; - }, "DS: Extract payload of '" + type + "'"); + return Ember.computed(function(key) { + var relationship = this._relationships[key]; + return relationship.getRecords(); + }).meta(meta).readOnly(); } - function _findMany(adapter, store, type, ids, owner) { - var promise = adapter.findMany(store, type, ids, owner), - serializer = serializerForAdapter(adapter, type), - label = "DS: Handle Adapter#findMany of " + type; + ember$data$lib$system$model$model$$default.reopen({ + notifyHasManyAdded: function(key) { + //We need to notifyPropertyChange in the adding case because we need to make sure + //we fetch the newly added record in case it is unloaded + //TODO(Igor): Consider whether we could do this only if the record state is unloaded - return Promise.cast(promise, label).then(function(adapterPayload) { - var payload = serializer.extract(store, type, adapterPayload, null, 'findMany'); - - Ember.assert("The response from a findMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array'); - - store.pushMany(type, payload); - }, null, "DS: Extract payload of " + type); - } + //Goes away once hasMany is double promisified + this.notifyPropertyChange(key); + }, + }); - function _findHasMany(adapter, store, record, link, relationship) { - var promise = adapter.findHasMany(store, record, link, relationship), - serializer = serializerForAdapter(adapter, relationship.type), - label = "DS: Handle Adapter#findHasMany of " + record + " : " + relationship.type; - return Promise.cast(promise, label).then(function(adapterPayload) { - var payload = serializer.extract(store, relationship.type, adapterPayload, null, 'findHasMany'); + var ember$data$lib$system$relationships$has_many$$default = ember$data$lib$system$relationships$has_many$$hasMany; + function ember$data$lib$system$relationship$meta$$typeForRelationshipMeta(store, meta) { + var typeKey, type; - Ember.assert("The response from a findHasMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array'); + typeKey = meta.type || meta.key; + if (typeof typeKey === 'string') { + if (meta.kind === 'hasMany') { + typeKey = ember$inflector$lib$system$string$$singularize(typeKey); + } + type = store.modelFor(typeKey); + } else { + type = meta.type; + } - var records = store.pushMany(relationship.type, payload); - record.updateHasMany(relationship.key, records); - }, null, "DS: Extract payload of " + record + " : hasMany " + relationship.type); + return type; } - function _findBelongsTo(adapter, store, record, link, relationship) { - var promise = adapter.findBelongsTo(store, record, link, relationship), - serializer = serializerForAdapter(adapter, relationship.type), - label = "DS: Handle Adapter#findBelongsTo of " + record + " : " + relationship.type; - - return Promise.cast(promise, label).then(function(adapterPayload) { - var payload = serializer.extract(store, relationship.type, adapterPayload, null, 'findBelongsTo'); - var record = store.push(relationship.type, payload); - - record.updateBelongsTo(relationship.key, record); - return record; - }, null, "DS: Extract payload of " + record + " : " + relationship.type); + function ember$data$lib$system$relationship$meta$$relationshipFromMeta(store, meta) { + return { + key: meta.key, + kind: meta.kind, + type: ember$data$lib$system$relationship$meta$$typeForRelationshipMeta(store, meta), + options: meta.options, + parentType: meta.parentType, + isRelationship: true + }; } - function _findAll(adapter, store, type, sinceToken) { - var promise = adapter.findAll(store, type, sinceToken), - serializer = serializerForAdapter(adapter, type), - label = "DS: Handle Adapter#findAll of " + type; - - return Promise.cast(promise, label).then(function(adapterPayload) { - var payload = serializer.extract(store, type, adapterPayload, null, 'findAll'); + var ember$data$lib$system$relationships$ext$$get = Ember.get; + var ember$data$lib$system$relationships$ext$$filter = Ember.ArrayPolyfills.filter; - Ember.assert("The response from a findAll must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array'); - - store.pushMany(type, payload); - store.didUpdateAll(type); - return store.all(type); - }, null, "DS: Extract payload of findAll " + type); - } + var ember$data$lib$system$relationships$ext$$relationshipsDescriptor = Ember.computed(function() { + if (Ember.testing === true && ember$data$lib$system$relationships$ext$$relationshipsDescriptor._cacheable === true) { + ember$data$lib$system$relationships$ext$$relationshipsDescriptor._cacheable = false; + } - function _findQuery(adapter, store, type, query, recordArray) { - var promise = adapter.findQuery(store, type, query, recordArray), - serializer = serializerForAdapter(adapter, type), - label = "DS: Handle Adapter#findQuery of " + type; + var map = new ember$data$lib$system$map$$MapWithDefault({ + defaultValue: function() { return []; } + }); - return Promise.cast(promise, label).then(function(adapterPayload) { - var payload = serializer.extract(store, type, adapterPayload, null, 'findQuery'); + // Loop through each computed property on the class + this.eachComputedProperty(function(name, meta) { + // If the computed property is a relationship, add + // it to the map. + if (meta.isRelationship) { + meta.key = name; + var relationshipsForType = map.get(ember$data$lib$system$relationship$meta$$typeForRelationshipMeta(this.store, meta)); - Ember.assert("The response from a findQuery must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array'); + relationshipsForType.push({ + name: name, + kind: meta.kind + }); + } + }); - recordArray.load(payload); - return recordArray; - }, null, "DS: Extract payload of findQuery " + type); - } + return map; + }).readOnly(); - function _commit(adapter, store, operation, record) { - var type = record.constructor, - promise = adapter[operation](store, type, record), - serializer = serializerForAdapter(adapter, type), - label = "DS: Extract and notify about " + operation + " completion of " + record; + var ember$data$lib$system$relationships$ext$$relatedTypesDescriptor = Ember.computed(function() { + if (Ember.testing === true && ember$data$lib$system$relationships$ext$$relatedTypesDescriptor._cacheable === true) { + ember$data$lib$system$relationships$ext$$relatedTypesDescriptor._cacheable = false; + } - Ember.assert("Your adapter's '" + operation + "' method must return a promise, but it returned " + promise, isThenable(promise)); + var type; + var types = Ember.A(); - return promise.then(function(adapterPayload) { - var payload; + // Loop through each computed property on the class, + // and create an array of the unique types involved + // in relationships + this.eachComputedProperty(function(name, meta) { + if (meta.isRelationship) { + meta.key = name; + type = ember$data$lib$system$relationship$meta$$typeForRelationshipMeta(this.store, meta); - if (adapterPayload) { - payload = serializer.extract(store, type, adapterPayload, get(record, 'id'), operation); - } else { - payload = adapterPayload; - } + Ember.assert("You specified a hasMany (" + meta.type + ") on " + meta.parentType + " but " + meta.type + " was not found.", type); - store.didSaveRecord(record, payload); - return record; - }, function(reason) { - if (reason instanceof DS.InvalidError) { - store.recordWasInvalid(record, reason.errors); - } else { - store.recordWasError(record, reason); + if (!types.contains(type)) { + Ember.assert("Trying to sideload " + name + " on " + this.toString() + " but the type doesn't exist.", !!type); + types.push(type); + } } + }); - throw reason; - }, label); - } + return types; + }).readOnly(); - __exports__.Store = Store; - __exports__.PromiseArray = PromiseArray; - __exports__.PromiseObject = PromiseObject; - __exports__["default"] = Store; - }); -define("ember-data/lib/transforms", - ["./transforms/base","./transforms/number","./transforms/date","./transforms/string","./transforms/boolean","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { - "use strict"; - var Transform = __dependency1__["default"]; - var NumberTransform = __dependency2__["default"]; - var DateTransform = __dependency3__["default"]; - var StringTransform = __dependency4__["default"]; - var BooleanTransform = __dependency5__["default"]; - - __exports__.Transform = Transform; - __exports__.NumberTransform = NumberTransform; - __exports__.DateTransform = DateTransform; - __exports__.StringTransform = StringTransform; - __exports__.BooleanTransform = BooleanTransform; - }); -define("ember-data/lib/transforms/base", - ["exports"], - function(__exports__) { - "use strict"; - /** - The `DS.Transform` class is used to serialize and deserialize model - attributes when they are saved or loaded from an - adapter. Subclassing `DS.Transform` is useful for creating custom - attributes. All subclasses of `DS.Transform` must implement a - `serialize` and a `deserialize` method. + var ember$data$lib$system$relationships$ext$$relationshipsByNameDescriptor = Ember.computed(function() { + if (Ember.testing === true && ember$data$lib$system$relationships$ext$$relationshipsByNameDescriptor._cacheable === true) { + ember$data$lib$system$relationships$ext$$relationshipsByNameDescriptor._cacheable = false; + } - Example + var map = ember$data$lib$system$map$$Map.create(); - ```javascript - App.RawTransform = DS.Transform.extend({ - deserialize: function(serialized) { - return serialized; - }, - serialize: function(deserialized) { - return deserialized; + this.eachComputedProperty(function(name, meta) { + if (meta.isRelationship) { + meta.key = name; + var relationship = ember$data$lib$system$relationship$meta$$relationshipFromMeta(this.store, meta); + relationship.type = ember$data$lib$system$relationship$meta$$typeForRelationshipMeta(this.store, meta); + map.set(name, relationship); } }); - ``` - Usage + return map; + }).readOnly(); - ```javascript - var attr = DS.attr; - App.Requirement = DS.Model.extend({ - name: attr('string'), - optionsArray: attr('raw') - }); - ``` + /** + @module ember-data + */ + + /* + This file defines several extensions to the base `DS.Model` class that + add support for one-to-many relationships. + */ - @class Transform + /** + @class Model @namespace DS - */ - var Transform = Ember.Object.extend({ + */ + ember$data$lib$system$model$model$$default.reopen({ + /** - When given a deserialized value from a record attribute this - method must return the serialized value. + This Ember.js hook allows an object to be notified when a property + is defined. - Example + In this case, we use it to be notified when an Ember Data user defines a + belongs-to relationship. In that case, we need to set up observers for + each one, allowing us to track relationship changes and automatically + reflect changes in the inverse has-many array. + + This hook passes the class being set up, as well as the key and value + being defined. So, for example, when the user does this: ```javascript - serialize: function(deserialized) { - return Ember.isEmpty(deserialized) ? null : Number(deserialized); - } + DS.Model.extend({ + parent: DS.belongsTo('user') + }); ``` - @method serialize - @param deserialized The deserialized value - @return The serialized value - */ - serialize: Ember.required(), + This hook would be called with "parent" as the key and the computed + property returned by `DS.belongsTo` as the value. - /** - When given a serialize value from a JSON object this method must - return the deserialized value for the record attribute. + @method didDefineProperty + @param {Object} proto + @param {String} key + @param {Ember.ComputedProperty} value + */ + didDefineProperty: function(proto, key, value) { + // Check if the value being set is a computed property. + if (value instanceof Ember.ComputedProperty) { - Example + // If it is, get the metadata for the relationship. This is + // populated by the `DS.belongsTo` helper when it is creating + // the computed property. + var meta = value.meta(); - ```javascript - deserialize: function(serialized) { - return empty(serialized) ? null : Number(serialized); + meta.parentType = proto.constructor; } - ``` + } + }); - @method deserialize - @param serialized The serialized value - @return The deserialized value - */ - deserialize: Ember.required() + /* + These DS.Model extensions add class methods that provide relationship + introspection abilities about relationships. - }); + A note about the computed properties contained here: - __exports__["default"] = Transform; - }); -define("ember-data/lib/transforms/boolean", - ["./base","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Transform = __dependency1__["default"]; + **These properties are effectively sealed once called for the first time.** + To avoid repeatedly doing expensive iteration over a model's fields, these + values are computed once and then cached for the remainder of the runtime of + your application. - /** - The `DS.BooleanTransform` class is used to serialize and deserialize - boolean attributes on Ember Data record objects. This transform is - used when `boolean` is passed as the type parameter to the - [DS.attr](../../data#method_attr) function. + If your application needs to modify a class after its initial definition + (for example, using `reopen()` to add additional attributes), make sure you + do it before using your model with the store, which uses these properties + extensively. + */ - Usage + ember$data$lib$system$model$model$$default.reopenClass({ - ```javascript - var attr = DS.attr; - App.User = DS.Model.extend({ - isAdmin: attr('boolean'), - name: attr('string'), - email: attr('string') - }); - ``` + /** + For a given relationship name, returns the model type of the relationship. - @class BooleanTransform - @extends DS.Transform - @namespace DS - */ - var BooleanTransform = Transform.extend({ - deserialize: function(serialized) { - var type = typeof serialized; + For example, if you define a model like this: - if (type === "boolean") { - return serialized; - } else if (type === "string") { - return serialized.match(/^true$|^t$|^1$/i) !== null; - } else if (type === "number") { - return serialized === 1; - } else { - return false; - } + ```javascript + App.Post = DS.Model.extend({ + comments: DS.hasMany('comment') + }); + ``` + + Calling `App.Post.typeForRelationship('comments')` will return `App.Comment`. + + @method typeForRelationship + @static + @param {String} name the name of the relationship + @return {subclass of DS.Model} the type of the relationship, or undefined + */ + typeForRelationship: function(name) { + var relationship = ember$data$lib$system$relationships$ext$$get(this, 'relationshipsByName').get(name); + return relationship && relationship.type; }, - serialize: function(deserialized) { - return Boolean(deserialized); - } - }); - __exports__["default"] = BooleanTransform; - }); -define("ember-data/lib/transforms/date", - ["./base","exports"], - function(__dependency1__, __exports__) { - "use strict"; - /** - The `DS.DateTransform` class is used to serialize and deserialize - date attributes on Ember Data record objects. This transform is used - when `date` is passed as the type parameter to the - [DS.attr](../../data#method_attr) function. + inverseMap: Ember.computed(function() { + return Ember.create(null); + }), - ```javascript - var attr = DS.attr; - App.Score = DS.Model.extend({ - value: attr('number'), - player: DS.belongsTo('player'), - date: attr('date') - }); - ``` + /** + Find the relationship which is the inverse of the one asked for. - @class DateTransform - @extends DS.Transform - @namespace DS - */ - var Transform = __dependency1__["default"]; - var DateTransform = Transform.extend({ + For example, if you define models like this: - deserialize: function(serialized) { - var type = typeof serialized; + ```javascript + App.Post = DS.Model.extend({ + comments: DS.hasMany('message') + }); - if (type === "string") { - return new Date(Ember.Date.parse(serialized)); - } else if (type === "number") { - return new Date(serialized); - } else if (serialized === null || serialized === undefined) { - // if the value is not present in the data, - // return undefined, not null. - return serialized; + App.Message = DS.Model.extend({ + owner: DS.belongsTo('post') + }); + ``` + + App.Post.inverseFor('comments') -> {type: App.Message, name:'owner', kind:'belongsTo'} + App.Message.inverseFor('owner') -> {type: App.Post, name:'comments', kind:'hasMany'} + + @method inverseFor + @static + @param {String} name the name of the relationship + @return {Object} the inverse relationship, or null + */ + inverseFor: function(name) { + var inverseMap = ember$data$lib$system$relationships$ext$$get(this, 'inverseMap'); + if (inverseMap[name]) { + return inverseMap[name]; } else { - return null; + var inverse = this._findInverseFor(name); + inverseMap[name] = inverse; + return inverse; } }, - serialize: function(date) { - if (date instanceof Date) { - var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; - var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; + //Calculate the inverse, ignoring the cache + _findInverseFor: function(name) { - var pad = function(num) { - return num < 10 ? "0"+num : ""+num; - }; + var inverseType = this.typeForRelationship(name); + if (!inverseType) { + return null; + } + + //If inverse is manually specified to be null, like `comments: DS.hasMany('message', {inverse: null})` + var options = this.metaForProperty(name).options; + if (options.inverse === null) { return null; } - var utcYear = date.getUTCFullYear(), - utcMonth = date.getUTCMonth(), - utcDayOfMonth = date.getUTCDate(), - utcDay = date.getUTCDay(), - utcHours = date.getUTCHours(), - utcMinutes = date.getUTCMinutes(), - utcSeconds = date.getUTCSeconds(); + var inverseName, inverseKind, inverse; + //If inverse is specified manually, return the inverse + if (options.inverse) { + inverseName = options.inverse; + inverse = Ember.get(inverseType, 'relationshipsByName').get(inverseName); - var dayOfWeek = days[utcDay]; - var dayOfMonth = pad(utcDayOfMonth); - var month = months[utcMonth]; + Ember.assert("We found no inverse relationships by the name of '" + inverseName + "' on the '" + inverseType.typeKey + + "' model. This is most likely due to a missing attribute on your model definition.", !Ember.isNone(inverse)); - return dayOfWeek + ", " + dayOfMonth + " " + month + " " + utcYear + " " + - pad(utcHours) + ":" + pad(utcMinutes) + ":" + pad(utcSeconds) + " GMT"; + inverseKind = inverse.kind; } else { - return null; - } - } + //No inverse was specified manually, we need to use a heuristic to guess one + var possibleRelationships = findPossibleInverses(this, inverseType); - }); + if (possibleRelationships.length === 0) { return null; } - __exports__["default"] = DateTransform; - }); -define("ember-data/lib/transforms/number", - ["./base","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Transform = __dependency1__["default"]; + var filteredRelationships = ember$data$lib$system$relationships$ext$$filter.call(possibleRelationships, function(possibleRelationship) { + var optionsForRelationship = inverseType.metaForProperty(possibleRelationship.name).options; + return name === optionsForRelationship.inverse; + }); - var empty = Ember.isEmpty; + Ember.assert("You defined the '" + name + "' relationship on " + this + ", but you defined the inverse relationships of type " + + inverseType.toString() + " multiple times. Look at http://emberjs.com/guides/models/defining-models/#toc_explicit-inverses for how to explicitly specify inverses", + filteredRelationships.length < 2); - /** - The `DS.NumberTransform` class is used to serialize and deserialize - numeric attributes on Ember Data record objects. This transform is - used when `number` is passed as the type parameter to the - [DS.attr](../../data#method_attr) function. + if (filteredRelationships.length === 1 ) { + possibleRelationships = filteredRelationships; + } - Usage + Ember.assert("You defined the '" + name + "' relationship on " + this + ", but multiple possible inverse relationships of type " + + this + " were found on " + inverseType + ". Look at http://emberjs.com/guides/models/defining-models/#toc_explicit-inverses for how to explicitly specify inverses", + possibleRelationships.length === 1); - ```javascript - var attr = DS.attr; - App.Score = DS.Model.extend({ - value: attr('number'), - player: DS.belongsTo('player'), - date: attr('date') - }); - ``` + inverseName = possibleRelationships[0].name; + inverseKind = possibleRelationships[0].kind; + } - @class NumberTransform - @extends DS.Transform - @namespace DS - */ - var NumberTransform = Transform.extend({ + function findPossibleInverses(type, inverseType, relationshipsSoFar) { + var possibleRelationships = relationshipsSoFar || []; - deserialize: function(serialized) { - return empty(serialized) ? null : Number(serialized); - }, + var relationshipMap = ember$data$lib$system$relationships$ext$$get(inverseType, 'relationships'); + if (!relationshipMap) { return; } - serialize: function(deserialized) { - return empty(deserialized) ? null : Number(deserialized); - } - }); + var relationships = relationshipMap.get(type); - __exports__["default"] = NumberTransform; - }); -define("ember-data/lib/transforms/string", - ["./base","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Transform = __dependency1__["default"]; - var none = Ember.isNone; + relationships = ember$data$lib$system$relationships$ext$$filter.call(relationships, function(relationship) { + var optionsForRelationship = inverseType.metaForProperty(relationship.name).options; - /** - The `DS.StringTransform` class is used to serialize and deserialize - string attributes on Ember Data record objects. This transform is - used when `string` is passed as the type parameter to the - [DS.attr](../../data#method_attr) function. + if (!optionsForRelationship.inverse){ + return true; + } - Usage + return name === optionsForRelationship.inverse; + }); - ```javascript - var attr = DS.attr; - App.User = DS.Model.extend({ - isAdmin: attr('boolean'), - name: attr('string'), - email: attr('string') - }); - ``` + if (relationships) { + possibleRelationships.push.apply(possibleRelationships, relationships); + } - @class StringTransform - @extends DS.Transform - @namespace DS - */ - var StringTransform = Transform.extend({ + //Recurse to support polymorphism + if (type.superclass) { + findPossibleInverses(type.superclass, inverseType, possibleRelationships); + } - deserialize: function(serialized) { - return none(serialized) ? null : String(serialized); - }, + return possibleRelationships; + } - serialize: function(deserialized) { - return none(deserialized) ? null : String(deserialized); - } + return { + type: inverseType, + name: inverseName, + kind: inverseKind + }; + }, - }); + /** + The model's relationships as a map, keyed on the type of the + relationship. The value of each entry is an array containing a descriptor + for each relationship with that type, describing the name of the relationship + as well as the type. - __exports__["default"] = StringTransform; - }); -define("ember-inflector/lib/ext/string", - ["../system/string"], - function(__dependency1__) { - "use strict"; - var pluralize = __dependency1__.pluralize; - var singularize = __dependency1__.singularize; + For example, given the following model definition: - if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { - /** - See {{#crossLink "Ember.String/pluralize"}}{{/crossLink}} + ```javascript + App.Blog = DS.Model.extend({ + users: DS.hasMany('user'), + owner: DS.belongsTo('user'), + posts: DS.hasMany('post') + }); + ``` - @method pluralize - @for String - */ - String.prototype.pluralize = function() { - return pluralize(this); - }; + This computed property would return a map describing these + relationships, like this: - /** - See {{#crossLink "Ember.String/singularize"}}{{/crossLink}} + ```javascript + var relationships = Ember.get(App.Blog, 'relationships'); + relationships.get(App.User); + //=> [ { name: 'users', kind: 'hasMany' }, + // { name: 'owner', kind: 'belongsTo' } ] + relationships.get(App.Post); + //=> [ { name: 'posts', kind: 'hasMany' } ] + ``` - @method singularize - @for String + @property relationships + @static + @type Ember.Map + @readOnly */ - String.prototype.singularize = function() { - return singularize(this); - }; - } - }); -define("ember-inflector/lib/main", - ["./system","./ext/string","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var Inflector = __dependency1__.Inflector; - var inflections = __dependency1__.inflections; - var pluralize = __dependency1__.pluralize; - var singularize = __dependency1__.singularize; - Inflector.defaultRules = inflections; - Ember.Inflector = Inflector; + relationships: ember$data$lib$system$relationships$ext$$relationshipsDescriptor, - Ember.String.pluralize = pluralize; - Ember.String.singularize = singularize; + /** + A hash containing lists of the model's relationships, grouped + by the relationship kind. For example, given a model with this + definition: + ```javascript + App.Blog = DS.Model.extend({ + users: DS.hasMany('user'), + owner: DS.belongsTo('user'), - __exports__["default"] = Inflector; + posts: DS.hasMany('post') + }); + ``` - __exports__.pluralize = pluralize; - __exports__.singularize = singularize; - }); -define("ember-inflector/lib/system", - ["./system/inflector","./system/string","./system/inflections","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var Inflector = __dependency1__["default"]; - - var pluralize = __dependency2__.pluralize; - var singularize = __dependency2__.singularize; - - var defaultRules = __dependency3__["default"]; - - - Inflector.inflector = new Inflector(defaultRules); - - __exports__.Inflector = Inflector; - __exports__.singularize = singularize; - __exports__.pluralize = pluralize; - __exports__.defaultRules = defaultRules; - }); -define("ember-inflector/lib/system/inflections", - ["exports"], - function(__exports__) { - "use strict"; - var defaultRules = { - plurals: [ - [/$/, 's'], - [/s$/i, 's'], - [/^(ax|test)is$/i, '$1es'], - [/(octop|vir)us$/i, '$1i'], - [/(octop|vir)i$/i, '$1i'], - [/(alias|status)$/i, '$1es'], - [/(bu)s$/i, '$1ses'], - [/(buffal|tomat)o$/i, '$1oes'], - [/([ti])um$/i, '$1a'], - [/([ti])a$/i, '$1a'], - [/sis$/i, 'ses'], - [/(?:([^f])fe|([lr])f)$/i, '$1$2ves'], - [/(hive)$/i, '$1s'], - [/([^aeiouy]|qu)y$/i, '$1ies'], - [/(x|ch|ss|sh)$/i, '$1es'], - [/(matr|vert|ind)(?:ix|ex)$/i, '$1ices'], - [/^(m|l)ouse$/i, '$1ice'], - [/^(m|l)ice$/i, '$1ice'], - [/^(ox)$/i, '$1en'], - [/^(oxen)$/i, '$1'], - [/(quiz)$/i, '$1zes'] - ], + This property would contain the following: - singular: [ - [/s$/i, ''], - [/(ss)$/i, '$1'], - [/(n)ews$/i, '$1ews'], - [/([ti])a$/i, '$1um'], - [/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '$1sis'], - [/(^analy)(sis|ses)$/i, '$1sis'], - [/([^f])ves$/i, '$1fe'], - [/(hive)s$/i, '$1'], - [/(tive)s$/i, '$1'], - [/([lr])ves$/i, '$1f'], - [/([^aeiouy]|qu)ies$/i, '$1y'], - [/(s)eries$/i, '$1eries'], - [/(m)ovies$/i, '$1ovie'], - [/(x|ch|ss|sh)es$/i, '$1'], - [/^(m|l)ice$/i, '$1ouse'], - [/(bus)(es)?$/i, '$1'], - [/(o)es$/i, '$1'], - [/(shoe)s$/i, '$1'], - [/(cris|test)(is|es)$/i, '$1is'], - [/^(a)x[ie]s$/i, '$1xis'], - [/(octop|vir)(us|i)$/i, '$1us'], - [/(alias|status)(es)?$/i, '$1'], - [/^(ox)en/i, '$1'], - [/(vert|ind)ices$/i, '$1ex'], - [/(matr)ices$/i, '$1ix'], - [/(quiz)zes$/i, '$1'], - [/(database)s$/i, '$1'] - ], + ```javascript + var relationshipNames = Ember.get(App.Blog, 'relationshipNames'); + relationshipNames.hasMany; + //=> ['users', 'posts'] + relationshipNames.belongsTo; + //=> ['owner'] + ``` - irregularPairs: [ - ['person', 'people'], - ['man', 'men'], - ['child', 'children'], - ['sex', 'sexes'], - ['move', 'moves'], - ['cow', 'kine'], - ['zombie', 'zombies'] - ], + @property relationshipNames + @static + @type Object + @readOnly + */ + relationshipNames: Ember.computed(function() { + var names = { + hasMany: [], + belongsTo: [] + }; - uncountable: [ - 'equipment', - 'information', - 'rice', - 'money', - 'species', - 'series', - 'fish', - 'sheep', - 'jeans', - 'police' - ] - }; + this.eachComputedProperty(function(name, meta) { + if (meta.isRelationship) { + names[meta.kind].push(name); + } + }); - __exports__["default"] = defaultRules; - }); -define("ember-inflector/lib/system/inflector", - ["exports"], - function(__exports__) { - "use strict"; - var BLANK_REGEX = /^\s*$/; + return names; + }), - function loadUncountable(rules, uncountable) { - for (var i = 0, length = uncountable.length; i < length; i++) { - rules.uncountable[uncountable[i].toLowerCase()] = true; - } - } + /** + An array of types directly related to a model. Each type will be + included once, regardless of the number of relationships it has with + the model. - function loadIrregular(rules, irregularPairs) { - var pair; + For example, given a model with this definition: - for (var i = 0, length = irregularPairs.length; i < length; i++) { - pair = irregularPairs[i]; + ```javascript + App.Blog = DS.Model.extend({ + users: DS.hasMany('user'), + owner: DS.belongsTo('user'), - rules.irregular[pair[0].toLowerCase()] = pair[1]; - rules.irregularInverse[pair[1].toLowerCase()] = pair[0]; - } - } + posts: DS.hasMany('post') + }); + ``` - /** - Inflector.Ember provides a mechanism for supplying inflection rules for your - application. Ember includes a default set of inflection rules, and provides an - API for providing additional rules. + This property would contain the following: - Examples: + ```javascript + var relatedTypes = Ember.get(App.Blog, 'relatedTypes'); + //=> [ App.User, App.Post ] + ``` - Creating an inflector with no rules. + @property relatedTypes + @static + @type Ember.Array + @readOnly + */ + relatedTypes: ember$data$lib$system$relationships$ext$$relatedTypesDescriptor, - ```js - var inflector = new Ember.Inflector(); - ``` + /** + A map whose keys are the relationships of a model and whose values are + relationship descriptors. - Creating an inflector with the default ember ruleset. + For example, given a model with this + definition: - ```js - var inflector = new Ember.Inflector(Ember.Inflector.defaultRules); + ```javascript + App.Blog = DS.Model.extend({ + users: DS.hasMany('user'), + owner: DS.belongsTo('user'), - inflector.pluralize('cow') //=> 'kine' - inflector.singularize('kine') //=> 'cow' - ``` + posts: DS.hasMany('post') + }); + ``` - Creating an inflector and adding rules later. + This property would contain the following: - ```javascript - var inflector = Ember.Inflector.inflector; + ```javascript + var relationshipsByName = Ember.get(App.Blog, 'relationshipsByName'); + relationshipsByName.get('users'); + //=> { key: 'users', kind: 'hasMany', type: App.User } + relationshipsByName.get('owner'); + //=> { key: 'owner', kind: 'belongsTo', type: App.User } + ``` - inflector.pluralize('advice') // => 'advices' - inflector.uncountable('advice'); - inflector.pluralize('advice') // => 'advice' + @property relationshipsByName + @static + @type Ember.Map + @readOnly + */ + relationshipsByName: ember$data$lib$system$relationships$ext$$relationshipsByNameDescriptor, - inflector.pluralize('formula') // => 'formulas' - inflector.irregular('formula', 'formulae'); - inflector.pluralize('formula') // => 'formulae' + /** + A map whose keys are the fields of the model and whose values are strings + describing the kind of the field. A model's fields are the union of all of its + attributes and relationships. - // you would not need to add these as they are the default rules - inflector.plural(/$/, 's'); - inflector.singular(/s$/i, ''); - ``` + For example: - Creating an inflector with a nondefault ruleset. + ```javascript - ```javascript - var rules = { - plurals: [ /$/, 's' ], - singular: [ /\s$/, '' ], - irregularPairs: [ - [ 'cow', 'kine' ] - ], - uncountable: [ 'fish' ] - }; + App.Blog = DS.Model.extend({ + users: DS.hasMany('user'), + owner: DS.belongsTo('user'), - var inflector = new Ember.Inflector(rules); - ``` + posts: DS.hasMany('post'), - @class Inflector - @namespace Ember - */ - function Inflector(ruleSet) { - ruleSet = ruleSet || {}; - ruleSet.uncountable = ruleSet.uncountable || {}; - ruleSet.irregularPairs = ruleSet.irregularPairs || {}; + title: DS.attr('string') + }); - var rules = this.rules = { - plurals: ruleSet.plurals || [], - singular: ruleSet.singular || [], - irregular: {}, - irregularInverse: {}, - uncountable: {} - }; + var fields = Ember.get(App.Blog, 'fields'); + fields.forEach(function(kind, field) { + console.log(field, kind); + }); - loadUncountable(rules, ruleSet.uncountable); - loadIrregular(rules, ruleSet.irregularPairs); - } + // prints: + // users, hasMany + // owner, belongsTo + // posts, hasMany + // title, attribute + ``` - Inflector.prototype = { - /** - @method plural - @param {RegExp} regex - @param {String} string + @property fields + @static + @type Ember.Map + @readOnly */ - plural: function(regex, string) { - this.rules.plurals.push([regex, string.toLowerCase()]); - }, + fields: Ember.computed(function() { + var map = ember$data$lib$system$map$$Map.create(); - /** - @method singular - @param {RegExp} regex - @param {String} string - */ - singular: function(regex, string) { - this.rules.singular.push([regex, string.toLowerCase()]); - }, + this.eachComputedProperty(function(name, meta) { + if (meta.isRelationship) { + map.set(name, meta.kind); + } else if (meta.isAttribute) { + map.set(name, 'attribute'); + } + }); + + return map; + }).readOnly(), /** - @method uncountable - @param {String} regex + Given a callback, iterates over each of the relationships in the model, + invoking the callback with the name of each relationship and its relationship + descriptor. + + @method eachRelationship + @static + @param {Function} callback the callback to invoke + @param {any} binding the value to which the callback's `this` should be bound */ - uncountable: function(string) { - loadUncountable(this.rules, [string.toLowerCase()]); + eachRelationship: function(callback, binding) { + ember$data$lib$system$relationships$ext$$get(this, 'relationshipsByName').forEach(function(relationship, name) { + callback.call(binding, name, relationship); + }); }, /** - @method irregular - @param {String} singular - @param {String} plural + Given a callback, iterates over each of the types related to a model, + invoking the callback with the related type's class. Each type will be + returned just once, regardless of how many different relationships it has + with a model. + + @method eachRelatedType + @static + @param {Function} callback the callback to invoke + @param {any} binding the value to which the callback's `this` should be bound */ - irregular: function (singular, plural) { - loadIrregular(this.rules, [[singular, plural]]); + eachRelatedType: function(callback, binding) { + ember$data$lib$system$relationships$ext$$get(this, 'relatedTypes').forEach(function(type) { + callback.call(binding, type); + }); }, + determineRelationshipType: function(knownSide) { + var knownKey = knownSide.key; + var knownKind = knownSide.kind; + var inverse = this.inverseFor(knownKey); + var key, otherKind; + + if (!inverse) { + return knownKind === 'belongsTo' ? 'oneToNone' : 'manyToNone'; + } + + key = inverse.name; + otherKind = inverse.kind; + + if (otherKind === 'belongsTo') { + return knownKind === 'belongsTo' ? 'oneToOne' : 'manyToOne'; + } else { + return knownKind === 'belongsTo' ? 'oneToMany' : 'manyToMany'; + } + } + + }); + + ember$data$lib$system$model$model$$default.reopen({ /** - @method pluralize - @param {String} word + Given a callback, iterates over each of the relationships in the model, + invoking the callback with the name of each relationship and its relationship + descriptor. + + @method eachRelationship + @param {Function} callback the callback to invoke + @param {any} binding the value to which the callback's `this` should be bound */ - pluralize: function(word) { - return this.inflect(word, this.rules.plurals, this.rules.irregular); + eachRelationship: function(callback, binding) { + this.constructor.eachRelationship(callback, binding); }, - /** - @method singularize - @param {String} word - */ - singularize: function(word) { - return this.inflect(word, this.rules.singular, this.rules.irregularInverse); + relationshipFor: function(name) { + return ember$data$lib$system$relationships$ext$$get(this.constructor, 'relationshipsByName').get(name); }, - /** - @protected + inverseFor: function(key) { + return this.constructor.inverseFor(key); + } - @method inflect - @param {String} word - @param {Object} typeRules - @param {Object} irregular - */ - inflect: function(word, typeRules, irregular) { - var inflection, substitution, result, lowercase, isBlank, - isUncountable, isIrregular, isIrregularInverse, rule; + }); + /** + Ember Data + @module ember-data + @main ember-data + */ + + // support RSVP 2.x via resolve, but prefer RSVP 3.x's Promise.cast + Ember.RSVP.Promise.cast = Ember.RSVP.Promise.cast || Ember.RSVP.resolve; - isBlank = BLANK_REGEX.test(word); + Ember.runInDebug(function(){ + if (Ember.VERSION.match(/1\.[0-7]\./)){ + throw new Ember.Error("Ember Data requires at least Ember 1.8.0, but you have " + + Ember.VERSION + + ". Please upgrade your version of Ember, then upgrade Ember Data"); + } + }); - if (isBlank) { - return word; - } + ember$data$lib$core$$default.Store = ember$data$lib$system$store$$Store; + ember$data$lib$core$$default.PromiseArray = ember$data$lib$system$promise_proxies$$PromiseArray; + ember$data$lib$core$$default.PromiseObject = ember$data$lib$system$promise_proxies$$PromiseObject; - lowercase = word.toLowerCase(); + ember$data$lib$core$$default.PromiseManyArray = ember$data$lib$system$promise_proxies$$PromiseManyArray; - isUncountable = this.rules.uncountable[lowercase]; + ember$data$lib$core$$default.Model = ember$data$lib$system$model$model$$default; + ember$data$lib$core$$default.RootState = ember$data$lib$system$model$states$$default; + ember$data$lib$core$$default.attr = ember$data$lib$system$model$attributes$$default; + ember$data$lib$core$$default.Errors = ember$data$lib$system$model$errors$$default; - if (isUncountable) { - return word; - } + ember$data$lib$core$$default.Adapter = ember$data$lib$system$adapter$$Adapter; + ember$data$lib$core$$default.InvalidError = ember$data$lib$system$adapter$$InvalidError; - isIrregular = irregular && irregular[lowercase]; + ember$data$lib$core$$default.DebugAdapter = ember$data$lib$system$debug$$default; - if (isIrregular) { - return isIrregular; - } + ember$data$lib$core$$default.RecordArray = ember$data$lib$system$record_arrays$record_array$$default; + ember$data$lib$core$$default.FilteredRecordArray = ember$data$lib$system$record_arrays$filtered_record_array$$default; + ember$data$lib$core$$default.AdapterPopulatedRecordArray = ember$data$lib$system$record_arrays$adapter_populated_record_array$$default; + ember$data$lib$core$$default.ManyArray = ember$data$lib$system$record_arrays$many_array$$default; - for (var i = typeRules.length, min = 0; i > min; i--) { - inflection = typeRules[i-1]; - rule = inflection[0]; + ember$data$lib$core$$default.RecordArrayManager = ember$data$lib$system$record_array_manager$$default; - if (rule.test(word)) { - break; - } - } + ember$data$lib$core$$default.RESTAdapter = ember$data$lib$adapters$rest_adapter$$default; + ember$data$lib$core$$default.FixtureAdapter = ember$data$lib$adapters$fixture_adapter$$default; - inflection = inflection || []; + ember$data$lib$core$$default.RESTSerializer = ember$data$lib$serializers$rest_serializer$$default; + ember$data$lib$core$$default.JSONSerializer = ember$data$lib$serializers$json_serializer$$default; - rule = inflection[0]; - substitution = inflection[1]; + ember$data$lib$core$$default.Transform = ember$data$lib$transforms$base$$default; + ember$data$lib$core$$default.DateTransform = ember$data$lib$transforms$date$$default; + ember$data$lib$core$$default.StringTransform = ember$data$lib$transforms$string$$default; + ember$data$lib$core$$default.NumberTransform = ember$data$lib$transforms$number$$default; + ember$data$lib$core$$default.BooleanTransform = ember$data$lib$transforms$boolean$$default; - result = word.replace(rule, substitution); + ember$data$lib$core$$default.ActiveModelAdapter = activemodel$adapter$lib$system$active_model_adapter$$default; + ember$data$lib$core$$default.ActiveModelSerializer = activemodel$adapter$lib$system$active_model_serializer$$default; + ember$data$lib$core$$default.EmbeddedRecordsMixin = ember$data$lib$serializers$embedded_records_mixin$$default; - return result; - } - }; + ember$data$lib$core$$default.belongsTo = ember$data$lib$system$relationships$belongs_to$$default; + ember$data$lib$core$$default.hasMany = ember$data$lib$system$relationships$has_many$$default; - __exports__["default"] = Inflector; - }); -define("ember-inflector/lib/system/string", - ["./inflector","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Inflector = __dependency1__["default"]; - var pluralize = function(word) { - return Inflector.inflector.pluralize(word); - }; + ember$data$lib$core$$default.Relationship = ember$data$lib$system$relationships$state$relationship$$default; - var singularize = function(word) { - return Inflector.inflector.singularize(word); - }; + ember$data$lib$core$$default.ContainerProxy = ember$data$lib$system$container_proxy$$default; + + ember$data$lib$core$$default._setupContainer = ember$data$lib$setup$container$$default; + + Ember.lookup.DS = ember$data$lib$core$$default; + + var ember$data$lib$main$$default = ember$data$lib$core$$default; +}).call(this); - __exports__.pluralize = pluralize; - __exports__.singularize = singularize; - }); -global.DS = requireModule('ember-data/lib/main')['default']; -}(window)); \ No newline at end of file +//# sourceMappingURL=ember-data.js.map \ No newline at end of file diff --git a/examples/emberjs/bower_components/ember-localstorage-adapter/localstorage_adapter.js b/examples/emberjs/node_modules/ember-localstorage-adapter/localstorage_adapter.js similarity index 99% rename from examples/emberjs/bower_components/ember-localstorage-adapter/localstorage_adapter.js rename to examples/emberjs/node_modules/ember-localstorage-adapter/localstorage_adapter.js index ada6f76817..9b2d22b3f8 100644 --- a/examples/emberjs/bower_components/ember-localstorage-adapter/localstorage_adapter.js +++ b/examples/emberjs/node_modules/ember-localstorage-adapter/localstorage_adapter.js @@ -8,7 +8,7 @@ serializeHasMany: function(record, json, relationship) { var key = relationship.key; var payloadKey = this.keyForRelationship ? this.keyForRelationship(key, "hasMany") : key; - var relationshipType = DS.RelationshipChange.determineRelationshipType(record.constructor, relationship); + var relationshipType = record.constructor.determineRelationshipType(relationship); if (relationshipType === 'manyToNone' || relationshipType === 'manyToMany' || diff --git a/examples/emberjs/bower_components/handlebars/handlebars.js b/examples/emberjs/node_modules/handlebars/dist/handlebars.js similarity index 59% rename from examples/emberjs/bower_components/handlebars/handlebars.js rename to examples/emberjs/node_modules/handlebars/dist/handlebars.js index bec7085c51..f826bbfd38 100644 --- a/examples/emberjs/bower_components/handlebars/handlebars.js +++ b/examples/emberjs/node_modules/handlebars/dist/handlebars.js @@ -1,8 +1,8 @@ /*! - handlebars v1.3.0 + handlebars v2.0.0 -Copyright (C) 2011 by Yehuda Katz +Copyright (C) 2011-2014 by Yehuda Katz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -25,7 +25,15 @@ THE SOFTWARE. @license */ /* exported Handlebars */ -var Handlebars = (function() { +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + define([], factory); + } else if (typeof exports === 'object') { + module.exports = factory(); + } else { + root.Handlebars = root.Handlebars || factory(); + } +}(this, function () { // handlebars/safe-string.js var __module4__ = (function() { "use strict"; @@ -63,15 +71,19 @@ var __module3__ = (function(__dependency1__) { var possible = /[&<>"'`]/; function escapeChar(chr) { - return escape[chr] || "&"; + return escape[chr]; } - function extend(obj, value) { - for(var key in value) { - if(Object.prototype.hasOwnProperty.call(value, key)) { - obj[key] = value[key]; + 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]; + } } } + + return obj; } __exports__.extend = extend;var toString = Object.prototype.toString; @@ -82,6 +94,7 @@ var __module3__ = (function(__dependency1__) { 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]'; @@ -89,6 +102,7 @@ var __module3__ = (function(__dependency1__) { } 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; }; @@ -98,8 +112,10 @@ var __module3__ = (function(__dependency1__) { // don't escape SafeStrings, since they're already safe if (string instanceof SafeString) { return string.toString(); - } else if (!string && string !== 0) { + } else if (string == null) { return ""; + } else if (!string) { + return string + ''; } // Force a string conversion as this will be done by the append regardless and @@ -121,7 +137,11 @@ var __module3__ = (function(__dependency1__) { } } - __exports__.isEmpty = isEmpty; + __exports__.isEmpty = isEmpty;function appendContextPath(contextPath, id) { + return (contextPath ? contextPath + '.' : '') + id; + } + + __exports__.appendContextPath = appendContextPath; return __exports__; })(__module4__); @@ -166,14 +186,16 @@ var __module2__ = (function(__dependency1__, __dependency2__) { var Utils = __dependency1__; var Exception = __dependency2__; - var VERSION = "1.3.0"; - __exports__.VERSION = VERSION;var COMPILER_REVISION = 4; + var VERSION = "2.0.0"; + __exports__.VERSION = VERSION;var COMPILER_REVISION = 6; __exports__.COMPILER_REVISION = COMPILER_REVISION; var REVISION_CHANGES = { 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it 2: '== 1.0.0-rc.3', 3: '== 1.0.0-rc.4', - 4: '>= 1.0.0' + 4: '== 1.x.x', + 5: '== 2.0.0-alpha.x', + 6: '>= 2.0.0-beta.1' }; __exports__.REVISION_CHANGES = REVISION_CHANGES; var isArray = Utils.isArray, @@ -194,38 +216,44 @@ var __module2__ = (function(__dependency1__, __dependency2__) { logger: logger, log: log, - registerHelper: function(name, fn, inverse) { + registerHelper: function(name, fn) { if (toString.call(name) === objectType) { - if (inverse || fn) { throw new Exception('Arg not supported with multiple helpers'); } + if (fn) { throw new Exception('Arg not supported with multiple helpers'); } Utils.extend(this.helpers, name); } else { - if (inverse) { fn.not = inverse; } this.helpers[name] = fn; } }, + unregisterHelper: function(name) { + delete this.helpers[name]; + }, - registerPartial: function(name, str) { + registerPartial: function(name, partial) { if (toString.call(name) === objectType) { Utils.extend(this.partials, name); } else { - this.partials[name] = str; + this.partials[name] = partial; } + }, + unregisterPartial: function(name) { + delete this.partials[name]; } }; function registerDefaultHelpers(instance) { - instance.registerHelper('helperMissing', function(arg) { - if(arguments.length === 2) { + instance.registerHelper('helperMissing', function(/* [args, ]options */) { + if(arguments.length === 1) { + // A missing field in a {{foo}} constuct. return undefined; } else { - throw new Exception("Missing helper: '" + arg + "'"); + // Someone is actually trying to call something, blow up. + throw new Exception("Missing helper: '" + arguments[arguments.length-1].name + "'"); } }); instance.registerHelper('blockHelperMissing', function(context, options) { - var inverse = options.inverse || function() {}, fn = options.fn; - - if (isFunction(context)) { context = context.call(this); } + var inverse = options.inverse, + fn = options.fn; if(context === true) { return fn(this); @@ -233,19 +261,38 @@ var __module2__ = (function(__dependency1__, __dependency2__) { return inverse(this); } else if (isArray(context)) { if(context.length > 0) { + if (options.ids) { + options.ids = [options.name]; + } + return instance.helpers.each(context, options); } else { return inverse(this); } } else { - return fn(context); + if (options.data && options.ids) { + var data = createFrame(options.data); + data.contextPath = Utils.appendContextPath(options.data.contextPath, options.name); + options = {data: data}; + } + + return fn(context, options); } }); instance.registerHelper('each', function(context, options) { + if (!options) { + throw new Exception('Must pass iterator to #each'); + } + var fn = options.fn, inverse = options.inverse; var i = 0, ret = "", data; + var contextPath; + if (options.data && options.ids) { + contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.'; + } + if (isFunction(context)) { context = context.call(this); } if (options.data) { @@ -259,16 +306,24 @@ var __module2__ = (function(__dependency1__, __dependency2__) { data.index = i; data.first = (i === 0); data.last = (i === (context.length-1)); + + if (contextPath) { + data.contextPath = contextPath + i; + } } ret = ret + fn(context[i], { data: data }); } } else { for(var key in context) { if(context.hasOwnProperty(key)) { - if(data) { - data.key = key; + if(data) { + data.key = key; data.index = i; data.first = (i === 0); + + if (contextPath) { + data.contextPath = contextPath + key; + } } ret = ret + fn(context[key], {data: data}); i++; @@ -304,12 +359,28 @@ var __module2__ = (function(__dependency1__, __dependency2__) { instance.registerHelper('with', function(context, options) { if (isFunction(context)) { context = context.call(this); } - if (!Utils.isEmpty(context)) return options.fn(context); + var fn = options.fn; + + if (!Utils.isEmpty(context)) { + if (options.data && options.ids) { + var data = createFrame(options.data); + data.contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]); + options = {data:data}; + } + + return fn(context, options); + } else { + return options.inverse(this); + } }); - instance.registerHelper('log', function(context, options) { + instance.registerHelper('log', function(message, options) { var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1; - instance.log(level, context); + instance.log(level, message); + }); + + instance.registerHelper('lookup', function(obj, field) { + return obj && obj[field]; }); } @@ -324,22 +395,22 @@ var __module2__ = (function(__dependency1__, __dependency2__) { level: 3, // can be overridden in the host environment - log: function(level, obj) { + log: function(level, message) { if (logger.level <= level) { var method = logger.methodMap[level]; if (typeof console !== 'undefined' && console[method]) { - console[method].call(console, obj); + console[method].call(console, message); } } } }; __exports__.logger = logger; - function log(level, obj) { logger.log(level, obj); } - - __exports__.log = log;var createFrame = function(object) { - var obj = {}; - Utils.extend(obj, object); - return obj; + var log = logger.log; + __exports__.log = log; + var createFrame = function(object) { + var frame = Utils.extend({}, object); + frame._parent = object; + return frame; }; __exports__.createFrame = createFrame; return __exports__; @@ -353,6 +424,7 @@ var __module6__ = (function(__dependency1__, __dependency2__, __dependency3__) { var Exception = __dependency2__; var COMPILER_REVISION = __dependency3__.COMPILER_REVISION; var REVISION_CHANGES = __dependency3__.REVISION_CHANGES; + var createFrame = __dependency3__.createFrame; function checkRevision(compilerInfo) { var compilerRevision = compilerInfo && compilerInfo[0] || 1, @@ -375,20 +447,43 @@ var __module6__ = (function(__dependency1__, __dependency2__, __dependency3__) { __exports__.checkRevision = checkRevision;// TODO: Remove this line and break up compilePartial function template(templateSpec, env) { + /* istanbul ignore next */ if (!env) { throw new Exception("No environment passed to template"); } + if (!templateSpec || !templateSpec.main) { + throw new Exception('Unknown template object: ' + typeof templateSpec); + } // Note: Using env.VM references rather than local var references throughout this section to allow // for external users to override these as psuedo-supported APIs. - var invokePartialWrapper = function(partial, name, context, helpers, partials, data) { - var result = env.VM.invokePartial.apply(this, arguments); - if (result != null) { return result; } - - if (env.compile) { - var options = { helpers: helpers, partials: partials, data: data }; - partials[name] = env.compile(partial, { data: data !== undefined }, env); - return partials[name](context, options); + env.VM.checkRevision(templateSpec.compiler); + + var invokePartialWrapper = function(partial, indent, name, context, hash, helpers, partials, data, depths) { + if (hash) { + context = Utils.extend({}, context, hash); + } + + var result = env.VM.invokePartial.call(this, partial, name, context, helpers, partials, data, depths); + + if (result == null && env.compile) { + var options = { helpers: helpers, partials: partials, data: data, depths: depths }; + partials[name] = env.compile(partial, { data: data !== undefined, compat: templateSpec.compat }, env); + result = partials[name](context, options); + } + if (result != null) { + if (indent) { + var lines = result.split('\n'); + for (var i = 0, l = lines.length; i < l; i++) { + if (!lines[i] && i + 1 === l) { + break; + } + + lines[i] = indent + lines[i]; + } + result = lines.join('\n'); + } + return result; } else { throw new Exception("The partial " + name + " could not be compiled when running in runtime-only mode"); } @@ -396,84 +491,110 @@ var __module6__ = (function(__dependency1__, __dependency2__, __dependency3__) { // Just add water var container = { + lookup: function(depths, name) { + var len = depths.length; + for (var i = 0; i < len; i++) { + if (depths[i] && depths[i][name] != null) { + return depths[i][name]; + } + } + }, + lambda: function(current, context) { + return typeof current === 'function' ? current.call(context) : current; + }, + escapeExpression: Utils.escapeExpression, invokePartial: invokePartialWrapper, + + fn: function(i) { + return templateSpec[i]; + }, + programs: [], - program: function(i, fn, data) { - var programWrapper = this.programs[i]; - if(data) { - programWrapper = program(i, fn, data); + program: function(i, data, depths) { + var programWrapper = this.programs[i], + fn = this.fn(i); + if (data || depths) { + programWrapper = program(this, i, fn, data, depths); } else if (!programWrapper) { - programWrapper = this.programs[i] = program(i, fn); + programWrapper = this.programs[i] = program(this, i, fn); } return programWrapper; }, + + data: function(data, depth) { + while (data && depth--) { + data = data._parent; + } + return data; + }, merge: function(param, common) { var ret = param || common; if (param && common && (param !== common)) { - ret = {}; - Utils.extend(ret, common); - Utils.extend(ret, param); + ret = Utils.extend({}, common, param); } + return ret; }, - programWithDepth: env.VM.programWithDepth, + noop: env.VM.noop, - compilerInfo: null + compilerInfo: templateSpec.compiler }; - return function(context, options) { + var ret = function(context, options) { options = options || {}; - var namespace = options.partial ? options : env, - helpers, - partials; + var data = options.data; - if (!options.partial) { - helpers = options.helpers; - partials = options.partials; + ret._setup(options); + if (!options.partial && templateSpec.useData) { + data = initData(context, data); } - var result = templateSpec.call( - container, - namespace, context, - helpers, - partials, - options.data); - - if (!options.partial) { - env.VM.checkRevision(container.compilerInfo); + var depths; + if (templateSpec.useDepths) { + depths = options.depths ? [context].concat(options.depths) : [context]; } - return result; + return templateSpec.main.call(container, context, container.helpers, container.partials, data, depths); }; - } + ret.isTop = true; - __exports__.template = template;function programWithDepth(i, fn, data /*, $depth */) { - var args = Array.prototype.slice.call(arguments, 3); + ret._setup = function(options) { + if (!options.partial) { + container.helpers = container.merge(options.helpers, env.helpers); - var prog = function(context, options) { - options = options || {}; + if (templateSpec.usePartial) { + container.partials = container.merge(options.partials, env.partials); + } + } else { + container.helpers = options.helpers; + container.partials = options.partials; + } + }; - return fn.apply(this, [context, options.data || data].concat(args)); + ret._child = function(i, data, depths) { + if (templateSpec.useDepths && !depths) { + throw new Exception('must pass parent depths'); + } + + return program(container, i, templateSpec[i], data, depths); }; - prog.program = i; - prog.depth = args.length; - return prog; + return ret; } - __exports__.programWithDepth = programWithDepth;function program(i, fn, data) { + __exports__.template = template;function program(container, i, fn, data, depths) { var prog = function(context, options) { options = options || {}; - return fn(context, options.data || data); + return fn.call(container, context, container.helpers, container.partials, options.data || data, depths && [context].concat(depths)); }; prog.program = i; - prog.depth = 0; + prog.depth = depths ? depths.length : 0; return prog; } - __exports__.program = program;function invokePartial(partial, name, context, helpers, partials, data) { - var options = { partial: true, helpers: helpers, partials: partials, data: data }; + __exports__.program = program;function invokePartial(partial, name, context, helpers, partials, data, depths) { + var options = { partial: true, helpers: helpers, partials: partials, data: data, depths: depths }; if(partial === undefined) { throw new Exception("The partial " + name + " could not be found"); @@ -484,7 +605,13 @@ var __module6__ = (function(__dependency1__, __dependency2__, __dependency3__) { __exports__.invokePartial = invokePartial;function noop() { return ""; } - __exports__.noop = noop; + __exports__.noop = noop;function initData(context, data) { + if (!data || !('root' in data)) { + data = data ? createFrame(data) : {}; + data.root = context; + } + return data; + } return __exports__; })(__module3__, __module5__, __module2__); @@ -510,6 +637,7 @@ var __module1__ = (function(__dependency1__, __dependency2__, __dependency3__, _ hb.SafeString = SafeString; hb.Exception = Exception; hb.Utils = Utils; + hb.escapeExpression = Utils.escapeExpression; hb.VM = runtime; hb.template = function(spec) { @@ -522,6 +650,8 @@ var __module1__ = (function(__dependency1__, __dependency2__, __dependency3__, _ var Handlebars = create(); Handlebars.create = create; + Handlebars['default'] = Handlebars; + __exports__ = Handlebars; return __exports__; })(__module2__, __module4__, __module5__, __module3__, __module6__); @@ -532,7 +662,7 @@ var __module7__ = (function(__dependency1__) { var __exports__; var Exception = __dependency1__; - function LocationInfo(locInfo){ + function LocationInfo(locInfo) { locInfo = locInfo || {}; this.firstLine = locInfo.first_line; this.firstColumn = locInfo.first_column; @@ -541,38 +671,11 @@ var __module7__ = (function(__dependency1__) { } var AST = { - ProgramNode: function(statements, inverseStrip, inverse, locInfo) { - var inverseLocationInfo, firstInverseNode; - if (arguments.length === 3) { - locInfo = inverse; - inverse = null; - } else if (arguments.length === 2) { - locInfo = inverseStrip; - inverseStrip = null; - } - + ProgramNode: function(statements, strip, locInfo) { LocationInfo.call(this, locInfo); this.type = "program"; this.statements = statements; - this.strip = {}; - - if(inverse) { - firstInverseNode = inverse[0]; - if (firstInverseNode) { - inverseLocationInfo = { - first_line: firstInverseNode.firstLine, - last_line: firstInverseNode.lastLine, - last_column: firstInverseNode.lastColumn, - first_column: firstInverseNode.firstColumn - }; - this.inverse = new AST.ProgramNode(inverse, inverseStrip, inverseLocationInfo); - } else { - this.inverse = new AST.ProgramNode(inverse, inverseStrip); - } - this.strip.right = inverseStrip.left; - } else if (inverseStrip) { - this.strip.left = inverseStrip.right; - } + this.strip = strip; }, MustacheNode: function(rawParams, hash, open, strip, locInfo) { @@ -596,8 +699,6 @@ var __module7__ = (function(__dependency1__) { this.sexpr = new AST.SexprNode(rawParams, hash); } - this.sexpr.isRoot = true; - // Support old AST API that stored this info in MustacheNode this.id = this.sexpr.id; this.params = this.sexpr.params; @@ -615,57 +716,63 @@ var __module7__ = (function(__dependency1__) { var id = this.id = rawParams[0]; var params = this.params = rawParams.slice(1); - // a mustache is an eligible helper if: - // * its id is simple (a single part, not `this` or `..`) - var eligibleHelper = this.eligibleHelper = id.isSimple; - // a mustache is definitely a helper if: // * it is an eligible helper, and // * it has at least one parameter or hash segment - this.isHelper = eligibleHelper && (params.length || hash); + this.isHelper = !!(params.length || hash); + + // a mustache is an eligible helper if: + // * its id is simple (a single part, not `this` or `..`) + this.eligibleHelper = this.isHelper || id.isSimple; // if a mustache is an eligible helper but not a definite // helper, it is ambiguous, and will be resolved in a later // pass or at runtime. }, - PartialNode: function(partialName, context, strip, locInfo) { + PartialNode: function(partialName, context, hash, strip, locInfo) { LocationInfo.call(this, locInfo); this.type = "partial"; this.partialName = partialName; this.context = context; + this.hash = hash; this.strip = strip; + + this.strip.inlineStandalone = true; }, - BlockNode: function(mustache, program, inverse, close, locInfo) { + BlockNode: function(mustache, program, inverse, strip, locInfo) { LocationInfo.call(this, locInfo); - if(mustache.sexpr.id.original !== close.path.original) { - throw new Exception(mustache.sexpr.id.original + " doesn't match " + close.path.original, this); - } - this.type = 'block'; this.mustache = mustache; this.program = program; this.inverse = inverse; - - this.strip = { - left: mustache.strip.left, - right: close.strip.right - }; - - (program || inverse).strip.left = mustache.strip.right; - (inverse || program).strip.right = close.strip.left; + this.strip = strip; if (inverse && !program) { this.isInverse = true; } }, + RawBlockNode: function(mustache, content, close, locInfo) { + LocationInfo.call(this, locInfo); + + if (mustache.sexpr.id.original !== close) { + throw new Exception(mustache.sexpr.id.original + " doesn't match " + close, this); + } + + content = new AST.ContentNode(content, locInfo); + + this.type = 'block'; + this.mustache = mustache; + this.program = new AST.ProgramNode([content], {}, locInfo); + }, + ContentNode: function(string, locInfo) { LocationInfo.call(this, locInfo); this.type = "content"; - this.string = string; + this.original = this.string = string; }, HashNode: function(pairs, locInfo) { @@ -680,7 +787,8 @@ var __module7__ = (function(__dependency1__) { var original = "", dig = [], - depth = 0; + depth = 0, + depthString = ''; for(var i=0,l=parts.length; i)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:-?[0-9]+(?=([~}\s)])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/]; - lexer.conditions = {"mu":{"rules":[4,5,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],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"INITIAL":{"rules":[0,1,32],"inclusive":true}}; + 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)])))/,/^(?:([^\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],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[5],"inclusive":false},"raw":{"rules":[3,4],"inclusive":false},"INITIAL":{"rules":[0,1,38],"inclusive":true}}; return lexer;})() parser.lexer = lexer; function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; @@ -1255,32 +1382,234 @@ var __module9__ = (function() { return __exports__; })(); +// handlebars/compiler/helpers.js +var __module10__ = (function(__dependency1__) { + "use strict"; + var __exports__ = {}; + var Exception = __dependency1__; + + function stripFlags(open, close) { + return { + left: open.charAt(2) === '~', + right: close.charAt(close.length-3) === '~' + }; + } + + __exports__.stripFlags = stripFlags; + function prepareBlock(mustache, program, inverseAndProgram, close, inverted, locInfo) { + /*jshint -W040 */ + if (mustache.sexpr.id.original !== close.path.original) { + throw new Exception(mustache.sexpr.id.original + ' doesn\'t match ' + close.path.original, mustache); + } + + var inverse = inverseAndProgram && inverseAndProgram.program; + + var strip = { + left: mustache.strip.left, + right: close.strip.right, + + // 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.statements), + closeStandalone: isPrevWhitespace((inverse || program).statements) + }; + + if (mustache.strip.right) { + omitRight(program.statements, null, true); + } + + if (inverse) { + var inverseStrip = inverseAndProgram.strip; + + if (inverseStrip.left) { + omitLeft(program.statements, null, true); + } + if (inverseStrip.right) { + omitRight(inverse.statements, null, true); + } + if (close.strip.left) { + omitLeft(inverse.statements, null, true); + } + + // Find standalone else statments + if (isPrevWhitespace(program.statements) + && isNextWhitespace(inverse.statements)) { + + omitLeft(program.statements); + omitRight(inverse.statements); + } + } else { + if (close.strip.left) { + omitLeft(program.statements, null, true); + } + } + + if (inverted) { + return new this.BlockNode(mustache, inverse, program, strip, locInfo); + } else { + return new this.BlockNode(mustache, program, inverse, strip, locInfo); + } + } + + __exports__.prepareBlock = prepareBlock; + function prepareProgram(statements, isRoot) { + for (var i = 0, l = statements.length; i < l; i++) { + var current = statements[i], + strip = current.strip; + + if (!strip) { + continue; + } + + var _isPrevWhitespace = isPrevWhitespace(statements, i, isRoot, current.type === 'partial'), + _isNextWhitespace = isNextWhitespace(statements, i, isRoot), + + openStandalone = strip.openStandalone && _isPrevWhitespace, + closeStandalone = strip.closeStandalone && _isNextWhitespace, + inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace; + + if (strip.right) { + omitRight(statements, i, true); + } + if (strip.left) { + omitLeft(statements, i, true); + } + + if (inlineStandalone) { + omitRight(statements, i); + + if (omitLeft(statements, i)) { + // If we are on a standalone node, save the indent info for partials + if (current.type === 'partial') { + current.indent = (/([ \t]+$)/).exec(statements[i-1].original) ? RegExp.$1 : ''; + } + } + } + if (openStandalone) { + omitRight((current.program || current.inverse).statements); + + // Strip out the previous content node if it's whitespace only + omitLeft(statements, i); + } + if (closeStandalone) { + // Always strip the next node + omitRight(statements, i); + + omitLeft((current.inverse || current.program).statements); + } + } + + return statements; + } + + __exports__.prepareProgram = prepareProgram;function isPrevWhitespace(statements, i, isRoot) { + if (i === undefined) { + i = statements.length; + } + + // Nodes that end with newlines are considered whitespace (but are special + // cased for strip operations) + var prev = statements[i-1], + sibling = statements[i-2]; + if (!prev) { + return isRoot; + } + + if (prev.type === 'content') { + return (sibling || !isRoot ? (/\r?\n\s*?$/) : (/(^|\r?\n)\s*?$/)).test(prev.original); + } + } + function isNextWhitespace(statements, i, isRoot) { + if (i === undefined) { + i = -1; + } + + var next = statements[i+1], + sibling = statements[i+2]; + if (!next) { + return isRoot; + } + + if (next.type === 'content') { + return (sibling || !isRoot ? (/^\s*?\r?\n/) : (/^\s*?(\r?\n|$)/)).test(next.original); + } + } + + // 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(statements, i, multiple) { + var current = statements[i == null ? 0 : i + 1]; + if (!current || current.type !== 'content' || (!multiple && current.rightStripped)) { + return; + } + + var original = current.string; + current.string = current.string.replace(multiple ? (/^\s+/) : (/^[ \t]*\r?\n?/), ''); + current.rightStripped = current.string !== original; + } + + // 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(statements, i, multiple) { + var current = statements[i == null ? statements.length - 1 : i - 1]; + if (!current || current.type !== 'content' || (!multiple && current.leftStripped)) { + return; + } + + // We omit the last node if it's whitespace only and not preceeded by a non-content node. + var original = current.string; + current.string = current.string.replace(multiple ? (/\s+$/) : (/[ \t]+$/), ''); + current.leftStripped = current.string !== original; + return current.leftStripped; + } + return __exports__; +})(__module5__); + // handlebars/compiler/base.js -var __module8__ = (function(__dependency1__, __dependency2__) { +var __module8__ = (function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { "use strict"; var __exports__ = {}; var parser = __dependency1__; var AST = __dependency2__; + var Helpers = __dependency3__; + var extend = __dependency4__.extend; __exports__.parser = parser; + var yy = {}; + extend(yy, Helpers, AST); + function parse(input) { // Just return if an already-compile AST was passed in. - if(input.constructor === AST.ProgramNode) { return input; } + if (input.constructor === AST.ProgramNode) { return input; } + + parser.yy = yy; - parser.yy = AST; return parser.parse(input); } __exports__.parse = parse; return __exports__; -})(__module9__, __module7__); +})(__module9__, __module7__, __module10__, __module3__); // handlebars/compiler/compiler.js -var __module10__ = (function(__dependency1__) { +var __module11__ = (function(__dependency1__, __dependency2__) { "use strict"; var __exports__ = {}; var Exception = __dependency1__; + var isArray = __dependency2__.isArray; + + var slice = [].slice; function Compiler() {} @@ -1292,30 +1621,6 @@ var __module10__ = (function(__dependency1__) { Compiler.prototype = { compiler: Compiler, - disassemble: function() { - var opcodes = this.opcodes, opcode, out = [], params, param; - - for (var i=0, l=opcodes.length; i 0) { - this.source[1] = this.source[1] + ", " + locals.join(", "); + varDeclarations += ", " + locals.join(", "); } // Generate minimizer alias mappings - if (!this.isChild) { - for (var alias in this.context.aliases) { - if (this.context.aliases.hasOwnProperty(alias)) { - this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias]; - } + for (var alias in this.aliases) { + if (this.aliases.hasOwnProperty(alias)) { + varDeclarations += ', ' + alias + '=' + this.aliases[alias]; } } - if (this.source[1]) { - this.source[1] = "var " + this.source[1].substring(2) + ";"; - } - - // Merge children - if (!this.isChild) { - this.source[1] += '\n' + this.context.programs.join('\n') + '\n'; - } + var params = ["depth0", "helpers", "partials", "data"]; - if (!this.environment.isSimple) { - this.pushSource("return buffer;"); - } - - var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"]; - - for(var i=0, l=this.environment.depths.list.length; i= 0; + // adding 1 corrects loss of precision from parseFloat (#15100) + return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0; }, isPlainObject: function( obj ) { @@ -303,7 +301,7 @@ jQuery.extend({ if ( obj == null ) { return obj + ""; } - // Support: Android < 4.0, iOS < 6 (functionish RegExp) + // Support: Android<4.0, iOS<6 (functionish RegExp) return typeof obj === "object" || typeof obj === "function" ? class2type[ toString.call(obj) ] || "object" : typeof obj; @@ -333,6 +331,7 @@ jQuery.extend({ }, // Convert dashed to camelCase; used by the css and data modules + // Support: IE9-11+ // Microsoft forgot to hump their vendor prefix (#9572) camelCase: function( string ) { return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); @@ -548,14 +547,14 @@ function isArraylike( obj ) { } var Sizzle = /*! - * Sizzle CSS Selector Engine v1.10.19 + * Sizzle CSS Selector Engine v2.2.0-pre * http://sizzlejs.com/ * - * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors * Released under the MIT license * http://jquery.org/license * - * Date: 2014-04-18 + * Date: 2014-12-16 */ (function( window ) { @@ -582,7 +581,7 @@ var i, contains, // Instance-specific data - expando = "sizzle" + -(new Date()), + expando = "sizzle" + 1 * new Date(), preferredDoc = window.document, dirruns = 0, done = 0, @@ -597,7 +596,6 @@ var i, }, // General-purpose constants - strundefined = typeof undefined, MAX_NEGATIVE = 1 << 31, // Instance methods @@ -607,12 +605,13 @@ var i, push_native = arr.push, push = arr.push, slice = arr.slice, - // Use a stripped-down indexOf if we can't use a native one - indexOf = arr.indexOf || function( elem ) { + // Use a stripped-down indexOf as it's faster than native + // http://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { var i = 0, - len = this.length; + len = list.length; for ( ; i < len; i++ ) { - if ( this[i] === elem ) { + if ( list[i] === elem ) { return i; } } @@ -652,6 +651,7 @@ var i, ")\\)|)", // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), @@ -703,6 +703,14 @@ var i, String.fromCharCode( high + 0x10000 ) : // Supplemental Plane codepoint (surrogate pair) String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); }; // Optimize for push.apply( _, NodeList ) @@ -745,19 +753,18 @@ function Sizzle( selector, context, results, seed ) { context = context || document; results = results || []; + nodeType = context.nodeType; - if ( !selector || typeof selector !== "string" ) { - return results; - } + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { - if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { - return []; + return results; } - if ( documentIsHTML && !seed ) { + if ( !seed && documentIsHTML ) { - // Shortcuts - if ( (match = rquickExpr.exec( selector )) ) { + // Try to shortcut find operations when possible (e.g., not under DocumentFragment) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { // Speed-up: Sizzle("#ID") if ( (m = match[1]) ) { if ( nodeType === 9 ) { @@ -789,7 +796,7 @@ function Sizzle( selector, context, results, seed ) { return results; // Speed-up: Sizzle(".CLASS") - } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { + } else if ( (m = match[3]) && support.getElementsByClassName ) { push.apply( results, context.getElementsByClassName( m ) ); return results; } @@ -799,7 +806,7 @@ function Sizzle( selector, context, results, seed ) { if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { nid = old = expando; newContext = context; - newSelector = nodeType === 9 && selector; + newSelector = nodeType !== 1 && selector; // qSA works strangely on Element-rooted queries // We can work around this by specifying an extra ID on the root @@ -986,7 +993,7 @@ function createPositionalPseudo( fn ) { * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value */ function testContext( context ) { - return context && typeof context.getElementsByTagName !== strundefined && context; + return context && typeof context.getElementsByTagName !== "undefined" && context; } // Expose support vars for convenience @@ -1010,9 +1017,8 @@ isXML = Sizzle.isXML = function( elem ) { * @returns {Object} Returns the current document */ setDocument = Sizzle.setDocument = function( node ) { - var hasCompare, - doc = node ? node.ownerDocument || node : preferredDoc, - parent = doc.defaultView; + var hasCompare, parent, + doc = node ? node.ownerDocument || node : preferredDoc; // If no document and documentElement is available, return if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { @@ -1022,9 +1028,7 @@ setDocument = Sizzle.setDocument = function( node ) { // Set our document document = doc; docElem = doc.documentElement; - - // Support tests - documentIsHTML = !isXML( doc ); + parent = doc.defaultView; // Support: IE>8 // If iframe document is assigned to "document" variable and if iframe has been reloaded, @@ -1033,21 +1037,22 @@ setDocument = Sizzle.setDocument = function( node ) { if ( parent && parent !== parent.top ) { // IE11 does not have attachEvent, so all must suffer if ( parent.addEventListener ) { - parent.addEventListener( "unload", function() { - setDocument(); - }, false ); + parent.addEventListener( "unload", unloadHandler, false ); } else if ( parent.attachEvent ) { - parent.attachEvent( "onunload", function() { - setDocument(); - }); + parent.attachEvent( "onunload", unloadHandler ); } } + /* Support tests + ---------------------------------------------------------------------- */ + documentIsHTML = !isXML( doc ); + /* Attributes ---------------------------------------------------------------------- */ // Support: IE<8 - // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) support.attributes = assert(function( div ) { div.className = "i"; return !div.getAttribute("className"); @@ -1062,17 +1067,8 @@ setDocument = Sizzle.setDocument = function( node ) { return !div.getElementsByTagName("*").length; }); - // Check if getElementsByClassName can be trusted - support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { - div.innerHTML = "
    "; - - // Support: Safari<4 - // Catch class over-caching - div.firstChild.className = "i"; - // Support: Opera<10 - // Catch gEBCN failure to find non-leading classes - return div.getElementsByClassName("i").length === 2; - }); + // Support: IE<9 + support.getElementsByClassName = rnative.test( doc.getElementsByClassName ); // Support: IE<10 // Check if getElementById returns elements by name @@ -1086,7 +1082,7 @@ setDocument = Sizzle.setDocument = function( node ) { // ID find and filter if ( support.getById ) { Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== strundefined && documentIsHTML ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { var m = context.getElementById( id ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 @@ -1107,7 +1103,7 @@ setDocument = Sizzle.setDocument = function( node ) { Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { - var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); return node && node.value === attrId; }; }; @@ -1116,14 +1112,20 @@ setDocument = Sizzle.setDocument = function( node ) { // Tag Expr.find["TAG"] = support.getElementsByTagName ? function( tag, context ) { - if ( typeof context.getElementsByTagName !== strundefined ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); } } : + function( tag, context ) { var elem, tmp = [], i = 0, + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too results = context.getElementsByTagName( tag ); // Filter out possible comments @@ -1141,7 +1143,7 @@ setDocument = Sizzle.setDocument = function( node ) { // Class Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { - if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { + if ( documentIsHTML ) { return context.getElementsByClassName( className ); } }; @@ -1170,13 +1172,15 @@ setDocument = Sizzle.setDocument = function( node ) { // setting a boolean content attribute, // since its presence should be enough // http://bugs.jquery.com/ticket/12359 - div.innerHTML = ""; + docElem.appendChild( div ).innerHTML = "" + + ""; // Support: IE8, Opera 11-12.16 // Nothing should be selected when empty strings follow ^= or $= or *= // The test attribute must be unknown in Opera but "safe" for WinRT // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section - if ( div.querySelectorAll("[msallowclip^='']").length ) { + if ( div.querySelectorAll("[msallowcapture^='']").length ) { rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); } @@ -1186,12 +1190,24 @@ setDocument = Sizzle.setDocument = function( node ) { rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); } + // Support: Chrome<29, Android<4.2+, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.7+ + if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push("~="); + } + // Webkit/Opera - :checked should return selected option elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked // IE8 throws error here and will not see later tests if ( !div.querySelectorAll(":checked").length ) { rbuggyQSA.push(":checked"); } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibing-combinator selector` fails + if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push(".#.+[+~]"); + } }); assert(function( div ) { @@ -1308,7 +1324,7 @@ setDocument = Sizzle.setDocument = function( node ) { // Maintain original order return sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : 0; } @@ -1335,7 +1351,7 @@ setDocument = Sizzle.setDocument = function( node ) { aup ? -1 : bup ? 1 : sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : 0; // If the nodes are siblings, we can do a quick check @@ -1398,7 +1414,7 @@ Sizzle.matchesSelector = function( elem, expr ) { elem.document && elem.document.nodeType !== 11 ) { return ret; } - } catch(e) {} + } catch (e) {} } return Sizzle( expr, document, null, [ elem ] ).length > 0; @@ -1617,7 +1633,7 @@ Expr = Sizzle.selectors = { return pattern || (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && classCache( className, function( elem ) { - return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); }); }, @@ -1639,7 +1655,7 @@ Expr = Sizzle.selectors = { operator === "^=" ? check && result.indexOf( check ) === 0 : operator === "*=" ? check && result.indexOf( check ) > -1 : operator === "$=" ? check && result.slice( -check.length ) === check : - operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : false; }; @@ -1759,7 +1775,7 @@ Expr = Sizzle.selectors = { matched = fn( seed, argument ), i = matched.length; while ( i-- ) { - idx = indexOf.call( seed, matched[i] ); + idx = indexOf( seed, matched[i] ); seed[ idx ] = !( matches[ idx ] = matched[i] ); } }) : @@ -1798,6 +1814,8 @@ Expr = Sizzle.selectors = { function( elem, context, xml ) { input[0] = elem; matcher( input, null, xml, results ); + // Don't keep the element (issue #299) + input[0] = null; return !results.pop(); }; }), @@ -1809,6 +1827,7 @@ Expr = Sizzle.selectors = { }), "contains": markFunction(function( text ) { + text = text.replace( runescape, funescape ); return function( elem ) { return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; }; @@ -2230,7 +2249,7 @@ function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postS i = matcherOut.length; while ( i-- ) { if ( (elem = matcherOut[i]) && - (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { seed[temp] = !(results[temp] = elem); } @@ -2265,13 +2284,16 @@ function matcherFromTokens( tokens ) { return elem === checkContext; }, implicitRelative, true ), matchAnyContext = addCombinator( function( elem ) { - return indexOf.call( checkContext, elem ) > -1; + return indexOf( checkContext, elem ) > -1; }, implicitRelative, true ), matchers = [ function( elem, context, xml ) { - return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( (checkContext = context).nodeType ? matchContext( elem, context, xml ) : matchAnyContext( elem, context, xml ) ); + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; } ]; for ( ; i < len; i++ ) { @@ -2521,7 +2543,7 @@ select = Sizzle.select = function( selector, context, results, seed ) { // Sort stability support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; -// Support: Chrome<14 +// Support: Chrome 14-35+ // Always assume duplicates if they aren't passed to the comparison function support.detectDuplicates = !!hasDuplicate; @@ -2730,7 +2752,7 @@ var rootjQuery, if ( match[1] ) { context = context instanceof jQuery ? context[0] : context; - // scripts is true for back-compat + // Option to run scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge( this, jQuery.parseHTML( match[1], @@ -2758,8 +2780,8 @@ var rootjQuery, } else { elem = document.getElementById( match[2] ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 + // Support: Blackberry 4.6 + // gEBID returns nodes no longer in the document (#6963) if ( elem && elem.parentNode ) { // Inject the element directly into the jQuery object this.length = 1; @@ -2812,7 +2834,7 @@ rootjQuery = jQuery( document ); var rparentsprev = /^(?:parents|prev(?:Until|All))/, - // methods guaranteed to produce a unique set when starting from a unique set + // Methods guaranteed to produce a unique set when starting from a unique set guaranteedUnique = { children: true, contents: true, @@ -2892,8 +2914,7 @@ jQuery.fn.extend({ return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); }, - // Determine the position of an element within - // the matched set of elements + // Determine the position of an element within the set index: function( elem ) { // No argument, return index in parent @@ -2901,7 +2922,7 @@ jQuery.fn.extend({ return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; } - // index in selector + // Index in selector if ( typeof elem === "string" ) { return indexOf.call( jQuery( elem ), this[ 0 ] ); } @@ -3317,7 +3338,7 @@ jQuery.extend({ progressValues, progressContexts, resolveContexts; - // add listeners to Deferred subordinates; treat others as resolved + // Add listeners to Deferred subordinates; treat others as resolved if ( length > 1 ) { progressValues = new Array( length ); progressContexts = new Array( length ); @@ -3334,7 +3355,7 @@ jQuery.extend({ } } - // if we're not waiting on anything, resolve the master + // If we're not waiting on anything, resolve the master if ( !remaining ) { deferred.resolveWith( resolveContexts, resolveValues ); } @@ -3413,7 +3434,7 @@ jQuery.ready.promise = function( obj ) { readyList = jQuery.Deferred(); // Catch cases where $(document).ready() is called after the browser event has already occurred. - // we once tried to use readyState "interactive" here, but it caused issues like the one + // We once tried to use readyState "interactive" here, but it caused issues like the one // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 if ( document.readyState === "complete" ) { // Handle it asynchronously to allow scripts the opportunity to delay ready @@ -3507,7 +3528,7 @@ jQuery.acceptData = function( owner ) { function Data() { - // Support: Android < 4, + // Support: Android<4, // Old WebKit does not have Object.preventExtensions/freeze method, // return new empty object instead with no [[set]] accessor Object.defineProperty( this.cache = {}, 0, { @@ -3516,7 +3537,7 @@ function Data() { } }); - this.expando = jQuery.expando + Math.random(); + this.expando = jQuery.expando + Data.uid++; } Data.uid = 1; @@ -3544,7 +3565,7 @@ Data.prototype = { descriptor[ this.expando ] = { value: unlock }; Object.defineProperties( owner, descriptor ); - // Support: Android < 4 + // Support: Android<4 // Fallback to a less secure definition } catch ( e ) { descriptor[ this.expando ] = unlock; @@ -3684,17 +3705,16 @@ var data_user = new Data(); -/* - Implementation Summary - - 1. Enforce API surface and semantic compatibility with 1.9.x branch - 2. Improve the module's maintainability by reducing the storage - paths to a single mechanism. - 3. Use the same single mechanism to support "private" and "user" data. - 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) - 5. Avoid exposing implementation details on user objects (eg. expando properties) - 6. Provide a clear path for implementation upgrade to WeakMap in 2014 -*/ +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, rmultiDash = /([A-Z])/g; @@ -3899,7 +3919,7 @@ jQuery.extend({ queue.unshift( "inprogress" ); } - // clear up the last queue stop function + // Clear up the last queue stop function delete hooks.stop; fn.call( elem, next, hooks ); } @@ -3909,7 +3929,7 @@ jQuery.extend({ } }, - // not intended for public consumption - generates a queueHooks object, or returns the current one + // Not public - generate a queueHooks object, or return the current one _queueHooks: function( elem, type ) { var key = type + "queueHooks"; return data_priv.get( elem, key ) || data_priv.access( elem, key, { @@ -3939,7 +3959,7 @@ jQuery.fn.extend({ this.each(function() { var queue = jQuery.queue( this, type, data ); - // ensure a hooks for this queue + // Ensure a hooks for this queue jQuery._queueHooks( this, type ); if ( type === "fx" && queue[0] !== "inprogress" ) { @@ -4006,21 +4026,22 @@ var rcheckableType = (/^(?:checkbox|radio)$/i); div = fragment.appendChild( document.createElement( "div" ) ), input = document.createElement( "input" ); - // #11217 - WebKit loses check when the name is after the checked attribute + // Support: Safari<=5.1 + // Check state lost if the name is set (#11217) // Support: Windows Web Apps (WWA) - // `name` and `type` need .setAttribute for WWA + // `name` and `type` must use .setAttribute for WWA (#14901) input.setAttribute( "type", "radio" ); input.setAttribute( "checked", "checked" ); input.setAttribute( "name", "t" ); div.appendChild( input ); - // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 - // old WebKit doesn't clone checked state correctly in fragments + // Support: Safari<=5.1, Android<4.2 + // Older WebKit doesn't clone checked state correctly in fragments support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + // Support: IE<=11+ // Make sure textarea (and checkbox) defaultValue is properly cloned - // Support: IE9-IE11+ div.innerHTML = ""; support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; })(); @@ -4398,8 +4419,8 @@ jQuery.event = { j = 0; while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { - // Triggered event must either 1) have no namespace, or - // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + // Triggered event must either 1) have no namespace, or 2) have namespace(s) + // a subset or equal to those in the bound event (both can have no namespace). if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { event.handleObj = handleObj; @@ -4549,7 +4570,7 @@ jQuery.event = { event.target = document; } - // Support: Safari 6.0+, Chrome < 28 + // Support: Safari 6.0+, Chrome<28 // Target should not be a text node (#504, #13143) if ( event.target.nodeType === 3 ) { event.target = event.target.parentNode; @@ -4654,7 +4675,7 @@ jQuery.Event = function( src, props ) { // by a handler lower down the tree; reflect the correct value. this.isDefaultPrevented = src.defaultPrevented || src.defaultPrevented === undefined && - // Support: Android < 4.0 + // Support: Android<4.0 src.returnValue === false ? returnTrue : returnFalse; @@ -4744,8 +4765,8 @@ jQuery.each({ }; }); -// Create "bubbling" focus and blur events // Support: Firefox, Chrome, Safari +// Create "bubbling" focus and blur events if ( !support.focusinBubbles ) { jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { @@ -4898,7 +4919,7 @@ var // We have to close these tags to support XHTML (#13200) wrapMap = { - // Support: IE 9 + // Support: IE9 option: [ 1, "" ], thead: [ 1, "
    ", "
    " ], @@ -4909,7 +4930,7 @@ var _default: [ 0, "", "" ] }; -// Support: IE 9 +// Support: IE9 wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; @@ -4999,7 +5020,7 @@ function getAll( context, tag ) { ret; } -// Support: IE >= 9 +// Fix IE bugs, see support tests function fixInput( src, dest ) { var nodeName = dest.nodeName.toLowerCase(); @@ -5019,8 +5040,7 @@ jQuery.extend({ clone = elem.cloneNode( true ), inPage = jQuery.contains( elem.ownerDocument, elem ); - // Support: IE >= 9 - // Fix Cloning issues + // Fix IE cloning issues if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) { @@ -5071,8 +5091,8 @@ jQuery.extend({ // Add nodes directly if ( jQuery.type( elem ) === "object" ) { - // Support: QtWebKit - // jQuery.merge because push.apply(_, arraylike) throws + // Support: QtWebKit, PhantomJS + // push.apply(_, arraylike) throws on ancient WebKit jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); // Convert non-html into a text node @@ -5094,15 +5114,14 @@ jQuery.extend({ tmp = tmp.lastChild; } - // Support: QtWebKit - // jQuery.merge because push.apply(_, arraylike) throws + // Support: QtWebKit, PhantomJS + // push.apply(_, arraylike) throws on ancient WebKit jQuery.merge( nodes, tmp.childNodes ); // Remember the top-level container tmp = fragment.firstChild; - // Fixes #12346 - // Support: Webkit, IE + // Ensure the created nodes are orphaned (#12392) tmp.textContent = ""; } } @@ -5464,7 +5483,7 @@ function actualDisplay( name, doc ) { // getDefaultComputedStyle might be reliably used only on attached element display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? - // Use of this method is a temporary fix (more like optmization) until something better comes along, + // Use of this method is a temporary fix (more like optimization) until something better comes along, // since it was removed from specification and supported only in FF style.display : jQuery.css( elem[ 0 ], "display" ); @@ -5514,7 +5533,14 @@ var rmargin = (/^margin/); var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); var getStyles = function( elem ) { - return elem.ownerDocument.defaultView.getComputedStyle( elem, null ); + // Support: IE<=11+, Firefox<=30+ (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + if ( elem.ownerDocument.defaultView.opener ) { + return elem.ownerDocument.defaultView.getComputedStyle( elem, null ); + } + + return window.getComputedStyle( elem, null ); }; @@ -5526,7 +5552,7 @@ function curCSS( elem, name, computed ) { computed = computed || getStyles( elem ); // Support: IE9 - // getPropertyValue is only needed for .css('filter') in IE9, see #12537 + // getPropertyValue is only needed for .css('filter') (#12537) if ( computed ) { ret = computed.getPropertyValue( name ) || computed[ name ]; } @@ -5572,15 +5598,13 @@ function addGetHookIf( conditionFn, hookFn ) { return { get: function() { if ( conditionFn() ) { - // Hook not needed (or it's not possible to use it due to missing dependency), - // remove it. - // Since there are no other hooks for marginRight, remove the whole object. + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. delete this.get; return; } // Hook needed; redefine it so that the support test is not executed again. - return (this.get = hookFn).apply( this, arguments ); } }; @@ -5597,6 +5621,8 @@ function addGetHookIf( conditionFn, hookFn ) { return; } + // Support: IE9-11+ + // Style of cloned element affects source element cloned (#8908) div.style.backgroundClip = "content-box"; div.cloneNode( true ).style.backgroundClip = ""; support.clearCloneStyle = div.style.backgroundClip === "content-box"; @@ -5629,6 +5655,7 @@ function addGetHookIf( conditionFn, hookFn ) { if ( window.getComputedStyle ) { jQuery.extend( support, { pixelPosition: function() { + // This test is executed only once but we still do memoizing // since we can use the boxSizingReliable pre-computing. // No need to check if the test was already performed, though. @@ -5642,6 +5669,7 @@ function addGetHookIf( conditionFn, hookFn ) { return boxSizingReliableVal; }, reliableMarginRight: function() { + // Support: Android 2.3 // Check if div with explicit width and no margin-right incorrectly // gets computed margin-right based on width of container. (#3333) @@ -5663,6 +5691,7 @@ function addGetHookIf( conditionFn, hookFn ) { ret = !parseFloat( window.getComputedStyle( marginDiv, null ).marginRight ); docElem.removeChild( container ); + div.removeChild( marginDiv ); return ret; } @@ -5694,8 +5723,8 @@ jQuery.swap = function( elem, options, callback, args ) { var - // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" - // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + // Swappable if display is none or starts with table except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display rdisplayswap = /^(none|table(?!-c[ea]).+)/, rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ), rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ), @@ -5708,15 +5737,15 @@ var cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; -// return a css property mapped to a potentially vendor prefixed property +// Return a css property mapped to a potentially vendor prefixed property function vendorPropName( style, name ) { - // shortcut for names that are not vendor prefixed + // Shortcut for names that are not vendor prefixed if ( name in style ) { return name; } - // check for vendor prefixed names + // Check for vendor prefixed names var capName = name[0].toUpperCase() + name.slice(1), origName = name, i = cssPrefixes.length; @@ -5749,7 +5778,7 @@ function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { val = 0; for ( ; i < 4; i += 2 ) { - // both box models exclude margin, so add it if we want it + // Both box models exclude margin, so add it if we want it if ( extra === "margin" ) { val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); } @@ -5760,15 +5789,15 @@ function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); } - // at this point, extra isn't border nor margin, so remove border + // At this point, extra isn't border nor margin, so remove border if ( extra !== "margin" ) { val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); } } else { - // at this point, extra isn't content, so add padding + // At this point, extra isn't content, so add padding val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - // at this point, extra isn't content nor padding, so add border + // At this point, extra isn't content nor padding, so add border if ( extra !== "padding" ) { val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); } @@ -5786,7 +5815,7 @@ function getWidthOrHeight( elem, name, extra ) { styles = getStyles( elem ), isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; - // some non-html elements return undefined for offsetWidth, so check for null/undefined + // Some non-html elements return undefined for offsetWidth, so check for null/undefined // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 if ( val <= 0 || val == null ) { @@ -5801,7 +5830,7 @@ function getWidthOrHeight( elem, name, extra ) { return val; } - // we need the check for style in case a browser which returns unreliable values + // Check for style in case a browser which returns unreliable values // for getComputedStyle silently falls back to the reliable elem.style valueIsBorderBox = isBorderBox && ( support.boxSizingReliable() || val === elem.style[ name ] ); @@ -5810,7 +5839,7 @@ function getWidthOrHeight( elem, name, extra ) { val = parseFloat( val ) || 0; } - // use the active box-sizing model to add/subtract irrelevant styles + // Use the active box-sizing model to add/subtract irrelevant styles return ( val + augmentWidthOrHeight( elem, @@ -5874,12 +5903,14 @@ function showHide( elements, show ) { } jQuery.extend({ + // Add in style property hooks for overriding the default // behavior of getting and setting a style property cssHooks: { opacity: { get: function( elem, computed ) { if ( computed ) { + // We should always get a number back from opacity var ret = curCSS( elem, "opacity" ); return ret === "" ? "1" : ret; @@ -5907,12 +5938,12 @@ jQuery.extend({ // Add in properties whose names you wish to fix before // setting or getting the value cssProps: { - // normalize float css property "float": "cssFloat" }, // Get and set the style property on a DOM Node style: function( elem, name, value, extra ) { + // Don't set styles on text and comment nodes if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { return; @@ -5925,33 +5956,32 @@ jQuery.extend({ name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); - // gets hook for the prefixed version - // followed by the unprefixed version + // Gets hook for the prefixed version, then unprefixed version hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; // Check if we're setting a value if ( value !== undefined ) { type = typeof value; - // convert relative number strings (+= or -=) to relative numbers. #7345 + // Convert "+=" or "-=" to relative numbers (#7345) if ( type === "string" && (ret = rrelNum.exec( value )) ) { value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); // Fixes bug #9237 type = "number"; } - // Make sure that null and NaN values aren't set. See: #7116 + // Make sure that null and NaN values aren't set (#7116) if ( value == null || value !== value ) { return; } - // If a number was passed in, add 'px' to the (except for certain CSS properties) + // If a number, add 'px' to the (except for certain CSS properties) if ( type === "number" && !jQuery.cssNumber[ origName ] ) { value += "px"; } - // Fixes #8908, it can be done more correctly by specifying setters in cssHooks, - // but it would mean to define eight (for every problematic property) identical functions + // Support: IE9-11+ + // background-* props affect original clone's values if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { style[ name ] = "inherit"; } @@ -5979,8 +6009,7 @@ jQuery.extend({ // Make sure that we're working with the right name name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); - // gets hook for the prefixed version - // followed by the unprefixed version + // Try prefixed name followed by the unprefixed name hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; // If a hook was provided get the computed value from there @@ -5993,12 +6022,12 @@ jQuery.extend({ val = curCSS( elem, name, styles ); } - //convert "normal" to computed value + // Convert "normal" to computed value if ( val === "normal" && name in cssNormalTransform ) { val = cssNormalTransform[ name ]; } - // Return, converting to number if forced or a qualifier was provided and val looks numeric + // Make numeric if forced or a qualifier was provided and val looks numeric if ( extra === "" || extra ) { num = parseFloat( val ); return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; @@ -6011,8 +6040,9 @@ jQuery.each([ "height", "width" ], function( i, name ) { jQuery.cssHooks[ name ] = { get: function( elem, computed, extra ) { if ( computed ) { - // certain elements can have dimension info if we invisibly show them - // however, it must have a current display style that would benefit from this + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit return rdisplayswap.test( jQuery.css( elem, "display" ) ) && elem.offsetWidth === 0 ? jQuery.swap( elem, cssShow, function() { return getWidthOrHeight( elem, name, extra ); @@ -6040,8 +6070,6 @@ jQuery.each([ "height", "width" ], function( i, name ) { jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight, function( elem, computed ) { if ( computed ) { - // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right - // Work around by temporarily setting element display to inline-block return jQuery.swap( elem, { "display": "inline-block" }, curCSS, [ elem, "marginRight" ] ); } @@ -6059,7 +6087,7 @@ jQuery.each({ var i = 0, expanded = {}, - // assumes a single number if not a string + // Assumes a single number if not a string parts = typeof value === "string" ? value.split(" ") : [ value ]; for ( ; i < 4; i++ ) { @@ -6182,17 +6210,18 @@ Tween.propHooks = { return tween.elem[ tween.prop ]; } - // passing an empty string as a 3rd parameter to .css will automatically - // attempt a parseFloat and fallback to a string if the parse fails - // so, simple values such as "10px" are parsed to Float. - // complex values such as "rotate(1rad)" are returned as is. + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. result = jQuery.css( tween.elem, tween.prop, "" ); // Empty strings, null, undefined and "auto" are converted to 0. return !result || result === "auto" ? 0 : result; }, set: function( tween ) { - // use step hook for back compat - use cssHook if its there - use .style if its - // available and use plain properties where available + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. if ( jQuery.fx.step[ tween.prop ] ) { jQuery.fx.step[ tween.prop ]( tween ); } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) { @@ -6206,7 +6235,6 @@ Tween.propHooks = { // Support: IE9 // Panic based approach to setting things on disconnected nodes - Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { set: function( tween ) { if ( tween.elem.nodeType && tween.elem.parentNode ) { @@ -6262,16 +6290,16 @@ var start = +target || 1; do { - // If previous iteration zeroed out, double until we get *something* - // Use a string for doubling factor so we don't accidentally see scale as unchanged below + // If previous iteration zeroed out, double until we get *something*. + // Use string for doubling so we don't accidentally see scale as unchanged below scale = scale || ".5"; // Adjust and apply start = start / scale; jQuery.style( tween.elem, prop, start + unit ); - // Update scale, tolerating zero or NaN from tween.cur() - // And breaking the loop if scale is unchanged or perfect, or if we've just had enough + // Update scale, tolerating zero or NaN from tween.cur(), + // break the loop if scale is unchanged or perfect, or if we've just had enough } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations ); } @@ -6303,8 +6331,8 @@ function genFx( type, includeWidth ) { i = 0, attrs = { height: type }; - // if we include width, step value is 1 to do all cssExpand values, - // if we don't include width, step value is 2 to skip over Left and Right + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right includeWidth = includeWidth ? 1 : 0; for ( ; i < 4 ; i += 2 - includeWidth ) { which = cssExpand[ i ]; @@ -6326,7 +6354,7 @@ function createTween( value, prop, animation ) { for ( ; index < length; index++ ) { if ( (tween = collection[ index ].call( animation, prop, value )) ) { - // we're done with this property + // We're done with this property return tween; } } @@ -6341,7 +6369,7 @@ function defaultPrefilter( elem, props, opts ) { hidden = elem.nodeType && isHidden( elem ), dataShow = data_priv.get( elem, "fxshow" ); - // handle queue: false promises + // Handle queue: false promises if ( !opts.queue ) { hooks = jQuery._queueHooks( elem, "fx" ); if ( hooks.unqueued == null ) { @@ -6356,8 +6384,7 @@ function defaultPrefilter( elem, props, opts ) { hooks.unqueued++; anim.always(function() { - // doing this makes sure that the complete handler will be called - // before this completes + // Ensure the complete handler is called before this completes anim.always(function() { hooks.unqueued--; if ( !jQuery.queue( elem, "fx" ).length ) { @@ -6367,7 +6394,7 @@ function defaultPrefilter( elem, props, opts ) { }); } - // height/width overflow pass + // Height/width overflow pass if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) { // Make sure that nothing sneaks out // Record all 3 overflow attributes because IE9-10 do not @@ -6429,7 +6456,7 @@ function defaultPrefilter( elem, props, opts ) { dataShow = data_priv.access( elem, "fxshow", {} ); } - // store state if its toggle - enables .stop().toggle() to "reverse" + // Store state if its toggle - enables .stop().toggle() to "reverse" if ( toggle ) { dataShow.hidden = !hidden; } @@ -6489,8 +6516,8 @@ function propFilter( props, specialEasing ) { value = hooks.expand( value ); delete props[ name ]; - // not quite $.extend, this wont overwrite keys already present. - // also - reusing 'index' from above because we have the correct "name" + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" for ( index in value ) { if ( !( index in props ) ) { props[ index ] = value[ index ]; @@ -6509,7 +6536,7 @@ function Animation( elem, properties, options ) { index = 0, length = animationPrefilters.length, deferred = jQuery.Deferred().always( function() { - // don't match elem in the :animated selector + // Don't match elem in the :animated selector delete tick.elem; }), tick = function() { @@ -6518,7 +6545,8 @@ function Animation( elem, properties, options ) { } var currentTime = fxNow || createFxNow(), remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), - // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497) + // Support: Android 2.3 + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) temp = remaining / animation.duration || 0, percent = 1 - temp, index = 0, @@ -6554,7 +6582,7 @@ function Animation( elem, properties, options ) { }, stop: function( gotoEnd ) { var index = 0, - // if we are going to the end, we want to run all the tweens + // If we are going to the end, we want to run all the tweens // otherwise we skip this part length = gotoEnd ? animation.tweens.length : 0; if ( stopped ) { @@ -6565,8 +6593,7 @@ function Animation( elem, properties, options ) { animation.tweens[ index ].run( 1 ); } - // resolve when we played the last frame - // otherwise, reject + // Resolve when we played the last frame; otherwise, reject if ( gotoEnd ) { deferred.resolveWith( elem, [ animation, gotoEnd ] ); } else { @@ -6648,7 +6675,7 @@ jQuery.speed = function( speed, easing, fn ) { opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default; - // normalize opt.queue - true/undefined/null -> "fx" + // Normalize opt.queue - true/undefined/null -> "fx" if ( opt.queue == null || opt.queue === true ) { opt.queue = "fx"; } @@ -6672,10 +6699,10 @@ jQuery.speed = function( speed, easing, fn ) { jQuery.fn.extend({ fadeTo: function( speed, to, easing, callback ) { - // show any hidden elements after setting opacity to 0 + // Show any hidden elements after setting opacity to 0 return this.filter( isHidden ).css( "opacity", 0 ).show() - // animate to the value specified + // Animate to the value specified .end().animate({ opacity: to }, speed, easing, callback ); }, animate: function( prop, speed, easing, callback ) { @@ -6738,9 +6765,9 @@ jQuery.fn.extend({ } } - // start the next in the queue if the last step wasn't forced - // timers currently will call their complete callbacks, which will dequeue - // but only if they were gotoEnd + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. if ( dequeue || !gotoEnd ) { jQuery.dequeue( this, type ); } @@ -6758,17 +6785,17 @@ jQuery.fn.extend({ timers = jQuery.timers, length = queue ? queue.length : 0; - // enable finishing flag on private data + // Enable finishing flag on private data data.finish = true; - // empty the queue first + // Empty the queue first jQuery.queue( this, type, [] ); if ( hooks && hooks.stop ) { hooks.stop.call( this, true ); } - // look for any active animations, and finish them + // Look for any active animations, and finish them for ( index = timers.length; index--; ) { if ( timers[ index ].elem === this && timers[ index ].queue === type ) { timers[ index ].anim.stop( true ); @@ -6776,14 +6803,14 @@ jQuery.fn.extend({ } } - // look for any animations in the old queue and finish them + // Look for any animations in the old queue and finish them for ( index = 0; index < length; index++ ) { if ( queue[ index ] && queue[ index ].finish ) { queue[ index ].finish.call( this ); } } - // turn off finishing flag + // Turn off finishing flag delete data.finish; }); } @@ -6886,21 +6913,21 @@ jQuery.fn.delay = function( time, type ) { input.type = "checkbox"; - // Support: iOS 5.1, Android 4.x, Android 2.3 - // Check the default checkbox/radio value ("" on old WebKit; "on" elsewhere) + // Support: iOS<=5.1, Android<=4.2+ + // Default value for a checkbox should be "on" support.checkOn = input.value !== ""; - // Must access the parent to make an option select properly - // Support: IE9, IE10 + // Support: IE<=11+ + // Must access selectedIndex to make default options select support.optSelected = opt.selected; - // Make sure that the options inside disabled selects aren't marked as disabled - // (WebKit marks them as disabled) + // Support: Android<=2.3 + // Options inside disabled selects are incorrectly marked as disabled select.disabled = true; support.optDisabled = !opt.disabled; - // Check if an input maintains its value after becoming a radio - // Support: IE9, IE10 + // Support: IE<=11+ + // An input loses its value after becoming a radio input = document.createElement( "input" ); input.value = "t"; input.type = "radio"; @@ -6997,8 +7024,6 @@ jQuery.extend({ set: function( elem, value ) { if ( !support.radioValue && value === "radio" && jQuery.nodeName( elem, "input" ) ) { - // Setting the type on a radio button after the value resets the value in IE6-9 - // Reset value to default in case type is set after value during creation var val = elem.value; elem.setAttribute( "type", value ); if ( val ) { @@ -7068,7 +7093,7 @@ jQuery.extend({ var ret, hooks, notxml, nType = elem.nodeType; - // don't get/set properties on text, comment and attribute nodes + // Don't get/set properties on text, comment and attribute nodes if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return; } @@ -7104,8 +7129,6 @@ jQuery.extend({ } }); -// Support: IE9+ -// Selectedness for an option in an optgroup can be inaccurate if ( !support.optSelected ) { jQuery.propHooks.selected = { get: function( elem ) { @@ -7213,7 +7236,7 @@ jQuery.fn.extend({ } } - // only assign if different to avoid unneeded rendering. + // Only assign if different to avoid unneeded rendering. finalValue = value ? jQuery.trim( cur ) : ""; if ( elem.className !== finalValue ) { elem.className = finalValue; @@ -7240,14 +7263,14 @@ jQuery.fn.extend({ return this.each(function() { if ( type === "string" ) { - // toggle individual class names + // Toggle individual class names var className, i = 0, self = jQuery( this ), classNames = value.match( rnotwhite ) || []; while ( (className = classNames[ i++ ]) ) { - // check each className given, space separated list + // Check each className given, space separated list if ( self.hasClass( className ) ) { self.removeClass( className ); } else { @@ -7262,7 +7285,7 @@ jQuery.fn.extend({ data_priv.set( this, "__className__", this.className ); } - // If the element has a class name or if we're passed "false", + // If the element has a class name or if we're passed `false`, // then remove the whole classname (if there was one, the above saved it). // Otherwise bring back whatever was previously saved (if anything), // falling back to the empty string if nothing was stored. @@ -7306,9 +7329,9 @@ jQuery.fn.extend({ ret = elem.value; return typeof ret === "string" ? - // handle most common string cases + // Handle most common string cases ret.replace(rreturn, "") : - // handle cases where value is null/undef or number + // Handle cases where value is null/undef or number ret == null ? "" : ret; } @@ -7416,7 +7439,7 @@ jQuery.extend({ } } - // force browsers to behave consistently when non-matching value is set + // Force browsers to behave consistently when non-matching value is set if ( !optionSet ) { elem.selectedIndex = -1; } @@ -7437,8 +7460,6 @@ jQuery.each([ "radio", "checkbox" ], function() { }; if ( !support.checkOn ) { jQuery.valHooks[ this ].get = function( elem ) { - // Support: Webkit - // "" is returned instead of "on" if a value isn't specified return elem.getAttribute("value") === null ? "on" : elem.value; }; } @@ -7520,10 +7541,6 @@ jQuery.parseXML = function( data ) { var - // Document location - ajaxLocParts, - ajaxLocation, - rhash = /#.*$/, rts = /([?&])_=[^&]*/, rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, @@ -7552,22 +7569,13 @@ var transports = {}, // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression - allTypes = "*/".concat("*"); + allTypes = "*/".concat( "*" ), -// #8138, IE may throw an exception when accessing -// a field from window.location if document.domain has been set -try { - ajaxLocation = location.href; -} catch( e ) { - // Use the href attribute of an A element - // since IE will modify it given document.location - ajaxLocation = document.createElement( "a" ); - ajaxLocation.href = ""; - ajaxLocation = ajaxLocation.href; -} + // Document location + ajaxLocation = window.location.href, -// Segment location into parts -ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; + // Segment location into parts + ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport function addToPrefiltersOrTransports( structure ) { @@ -8046,7 +8054,8 @@ jQuery.extend({ } // We can fire global events as of now if asked to - fireGlobals = s.global; + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; // Watch for a new set of requests if ( fireGlobals && jQuery.active++ === 0 ) { @@ -8119,7 +8128,7 @@ jQuery.extend({ return jqXHR.abort(); } - // aborting is no longer a cancellation + // Aborting is no longer a cancellation strAbort = "abort"; // Install callbacks on deferreds @@ -8231,8 +8240,7 @@ jQuery.extend({ isSuccess = !error; } } else { - // We extract error from statusText - // then normalize statusText and status for non-aborts + // Extract error from statusText and normalize for non-aborts error = statusText; if ( status || !statusText ) { statusText = "error"; @@ -8288,7 +8296,7 @@ jQuery.extend({ jQuery.each( [ "get", "post" ], function( i, method ) { jQuery[ method ] = function( url, data, callback, type ) { - // shift arguments if data argument was omitted + // Shift arguments if data argument was omitted if ( jQuery.isFunction( data ) ) { type = type || callback; callback = data; @@ -8305,13 +8313,6 @@ jQuery.each( [ "get", "post" ], function( i, method ) { }; }); -// Attach a bunch of functions for handling common AJAX events -jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) { - jQuery.fn[ type ] = function( fn ) { - return this.on( type, fn ); - }; -}); - jQuery._evalUrl = function( url ) { return jQuery.ajax({ @@ -8529,8 +8530,9 @@ var xhrId = 0, // Support: IE9 // Open requests must be manually aborted on unload (#5280) -if ( window.ActiveXObject ) { - jQuery( window ).on( "unload", function() { +// See https://support.microsoft.com/kb/2856746 for more info +if ( window.attachEvent ) { + window.attachEvent( "onunload", function() { for ( var key in xhrCallbacks ) { xhrCallbacks[ key ](); } @@ -8883,6 +8885,16 @@ jQuery.fn.load = function( url, params, callback ) { +// Attach a bunch of functions for handling common AJAX events +jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) { + jQuery.fn[ type ] = function( fn ) { + return this.on( type, fn ); + }; +}); + + + + jQuery.expr.filters.animated = function( elem ) { return jQuery.grep(jQuery.timers, function( fn ) { return elem === fn.elem; @@ -8919,7 +8931,8 @@ jQuery.offset = { calculatePosition = ( position === "absolute" || position === "fixed" ) && ( curCSSTop + curCSSLeft ).indexOf("auto") > -1; - // Need to be able to calculate position if either top or left is auto and position is either absolute or fixed + // Need to be able to calculate position if either + // top or left is auto and position is either absolute or fixed if ( calculatePosition ) { curPosition = curElem.position(); curTop = curPosition.top; @@ -8976,8 +8989,8 @@ jQuery.fn.extend({ return box; } + // Support: BlackBerry 5, iOS 3 (original iPhone) // If we don't have gBCR, just use 0,0 rather than error - // BlackBerry 5, iOS 3 (original iPhone) if ( typeof elem.getBoundingClientRect !== strundefined ) { box = elem.getBoundingClientRect(); } @@ -8999,7 +9012,7 @@ jQuery.fn.extend({ // Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only offset parent if ( jQuery.css( elem, "position" ) === "fixed" ) { - // We assume that getBoundingClientRect is available when computed position is fixed + // Assume getBoundingClientRect is there when computed position is fixed offset = elem.getBoundingClientRect(); } else { @@ -9062,16 +9075,18 @@ jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( }; }); +// Support: Safari<7+, Chrome<37+ // Add the top/left cssHooks using jQuery.fn.position // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 -// getComputedStyle returns percent when specified for top/left/bottom/right -// rather than make the css module depend on the offset module, we just check for it here +// Blink bug: https://code.google.com/p/chromium/issues/detail?id=229280 +// getComputedStyle returns percent when specified for top/left/bottom/right; +// rather than make the css module depend on the offset module, just check for it here jQuery.each( [ "top", "left" ], function( i, prop ) { jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition, function( elem, computed ) { if ( computed ) { computed = curCSS( elem, prop ); - // if curCSS returns percentage, fallback to offset + // If curCSS returns percentage, fallback to offset return rnumnonpx.test( computed ) ? jQuery( elem ).position()[ prop ] + "px" : computed; @@ -9084,7 +9099,7 @@ jQuery.each( [ "top", "left" ], function( i, prop ) { // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) { - // margin is only for outerHeight, outerWidth + // Margin is only for outerHeight, outerWidth jQuery.fn[ funcName ] = function( margin, value ) { var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); @@ -9175,8 +9190,8 @@ jQuery.noConflict = function( deep ) { return jQuery; }; -// Expose jQuery and $ identifiers, even in -// AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557) +// Expose jQuery and $ identifiers, even in AMD +// (#7102#comment:10, https://github.com/jquery/jquery/pull/557) // and CommonJS for browser emulators (#13566) if ( typeof noGlobal === strundefined ) { window.jQuery = window.$ = jQuery; diff --git a/examples/emberjs/node_modules/todomvc-app-css/index.css b/examples/emberjs/node_modules/todomvc-app-css/index.css new file mode 100644 index 0000000000..4308848800 --- /dev/null +++ b/examples/emberjs/node_modules/todomvc-app-css/index.css @@ -0,0 +1,394 @@ +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 new file mode 100644 index 0000000000..da65968a73 --- /dev/null +++ b/examples/emberjs/node_modules/todomvc-common/base.css @@ -0,0 +1,141 @@ +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/bower_components/todomvc-common/base.js b/examples/emberjs/node_modules/todomvc-common/base.js similarity index 87% rename from examples/emberjs/bower_components/todomvc-common/base.js rename to examples/emberjs/node_modules/todomvc-common/base.js index d3a15127d8..44fb50c613 100644 --- a/examples/emberjs/bower_components/todomvc-common/base.js +++ b/examples/emberjs/node_modules/todomvc-common/base.js @@ -1,6 +1,8 @@ +/* global _ */ (function () { 'use strict'; + /* jshint ignore:start */ // Underscore's Template Module // Courtesy of underscorejs.org var _ = (function (_) { @@ -114,6 +116,7 @@ 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') { @@ -175,13 +178,17 @@ if (learnJSON.backend) { this.frameworkJSON = learnJSON.backend; + this.frameworkJSON.issueLabel = framework; this.append({ backend: true }); } else if (learnJSON[framework]) { this.frameworkJSON = learnJSON[framework]; + this.frameworkJSON.issueLabel = framework; this.append(); } + + this.fetchIssueCount(); } Learn.prototype.append = function (opts) { @@ -212,6 +219,26 @@ document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); }; + Learn.prototype.fetchIssueCount = function () { + var issueLink = document.getElementById('issue-count-link'); + if (issueLink) { + var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos'); + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.onload = function (e) { + var parsedResponse = JSON.parse(e.target.responseText); + if (parsedResponse instanceof Array) { + var count = parsedResponse.length + if (count !== 0) { + issueLink.innerHTML = 'This app has ' + count + ' open issues'; + document.getElementById('issue-count').style.display = 'inline'; + } + } + }; + xhr.send(); + } + }; + redirect(); getFile('learn.json', Learn); })(); diff --git a/examples/emberjs/package.json b/examples/emberjs/package.json new file mode 100644 index 0000000000..06df428c91 --- /dev/null +++ b/examples/emberjs/package.json @@ -0,0 +1,11 @@ +{ + "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" + } +}