From 65a08e01892d976541733def94966a87c992e6c8 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Sat, 7 Feb 2015 21:22:20 -0500 Subject: [PATCH] Updated to 1.10.0. --- index.html | 4 +- .../{ember-1.9.1.js => ember-1.10.0.debug.js} | 60625 ++++++++-------- js/libs/ember-template-compiler-1.10.0.js | 7193 ++ js/libs/handlebars-v2.0.0.js | 3079 - 4 files changed, 38737 insertions(+), 32164 deletions(-) rename js/libs/{ember-1.9.1.js => ember-1.10.0.debug.js} (89%) create mode 100644 js/libs/ember-template-compiler-1.10.0.js delete mode 100644 js/libs/handlebars-v2.0.0.js diff --git a/index.html b/index.html index b063a65..8a6f1bf 100644 --- a/index.html +++ b/index.html @@ -22,8 +22,8 @@

Welcome to Ember.js

- - + + diff --git a/js/libs/ember-1.9.1.js b/js/libs/ember-1.10.0.debug.js similarity index 89% rename from js/libs/ember-1.9.1.js rename to js/libs/ember-1.10.0.debug.js index e57aab1..885adb1 100644 --- a/js/libs/ember-1.9.1.js +++ b/js/libs/ember-1.10.0.debug.js @@ -5,7 +5,7 @@ * 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.9.1 + * @version 1.10.0 */ (function() { @@ -32,7 +32,7 @@ var enifed, requireModule, eriuqer, requirejs, Ember; 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]; @@ -56,9 +56,11 @@ var enifed, requireModule, eriuqer, requirejs, Ember; }; function resolve(child, name) { - if (child.charAt(0) !== '.') { return child; } - var parts = child.split("/"); - var parentBase = name.split("/").slice(0, -1); + if (child.charAt(0) !== '.') { + return child; + } + var parts = child.split('/'); + var parentBase = name.split('/').slice(0, -1); for (var i=0, l=parts.length; i 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); + } + } + } - // 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")]); + return lazyValue; } - mustache = new Handlebars.AST.MustacheNode([id].concat([mustache.id]), mustache.hash, !mustache.escaped); } - return Handlebars.Compiler.prototype.mustache.call(this, mustache); - }; - + return new Helper(helperFunc); + } + }); +enifed("ember-htmlbars/compat/register-bound-helper", + ["ember-htmlbars/helpers","ember-htmlbars/compat/make-bound-helper","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; /** - Used for precompilation of Ember Handlebars templates. This will not be used - during normal app execution. - - @method precompile - @for Ember.Handlebars - @static - @param {String|Object} value The template to precompile or an Handlebars AST - @param {Boolean} asObject optional parameter, defaulting to true, of whether or not the - compiled template should be returned as an Object or a String + @module ember + @submodule ember-htmlbars */ - EmberHandlebars.precompile = function(value, asObject) { - var ast = Handlebars.parse(value); - - 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); - }; + var helpers = __dependency1__["default"]; + var makeBoundHelper = __dependency2__["default"]; - // 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. + var slice = [].slice; - @method compile - @for Ember.Handlebars - @static - @param {String} string The template to compile - @return {Function} - */ - EmberHandlebars.compile = function(string) { - var ast = Handlebars.parse(string); - var options = { data: true, stringParams: true }; - var environment = new EmberHandlebars.Compiler().compile(ast, options); - var templateSpec = new EmberHandlebars.JavaScriptCompiler().compile(environment, options, undefined, true); + /** + 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. - var template = EmberHandlebars.template(templateSpec); - template.isMethod = false; //Make sure we don't wrap templates with ._super + ## Simple example - return template; - }; - } + ```javascript + Ember.Handlebars.registerBoundHelper('capitalize', function(value) { + return Ember.String.capitalize(value); + }); + ``` - __exports__["default"] = EmberHandlebars; - }); -enifed("ember-handlebars", - ["ember-handlebars-compiler","ember-metal/core","ember-runtime/system/lazy_load","ember-handlebars/loader","ember-handlebars/ext","ember-handlebars/string","ember-handlebars/helpers/binding","ember-handlebars/helpers/if_unless","ember-handlebars/helpers/with","ember-handlebars/helpers/bind_attr","ember-handlebars/helpers/collection","ember-handlebars/helpers/view","ember-handlebars/helpers/unbound","ember-handlebars/helpers/debug","ember-handlebars/helpers/each","ember-handlebars/helpers/template","ember-handlebars/helpers/partial","ember-handlebars/helpers/yield","ember-handlebars/helpers/loc","ember-handlebars/controls/checkbox","ember-handlebars/controls/select","ember-handlebars/controls/text_area","ember-handlebars/controls/text_field","ember-handlebars/controls/text_support","ember-handlebars/controls","ember-handlebars/component_lookup","ember-handlebars/views/handlebars_bound_view","ember-handlebars/views/metamorph_view","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"; - var EmberHandlebars = __dependency1__["default"]; - var Ember = __dependency2__["default"]; - // to add to globals + The above bound helper can be used inside of templates as follows: - var runLoadHooks = __dependency3__.runLoadHooks; - var bootstrap = __dependency4__["default"]; + ```handlebars + {{capitalize name}} + ``` - var makeBoundHelper = __dependency5__.makeBoundHelper; - var registerBoundHelper = __dependency5__.registerBoundHelper; - var helperMissingHelper = __dependency5__.helperMissingHelper; - var blockHelperMissingHelper = __dependency5__.blockHelperMissingHelper; - var handlebarsGet = __dependency5__.handlebarsGet; + 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. + ## Example with options - // side effect of extending StringUtils of htmlSafe + Like normal handlebars helpers, bound helpers have access to the options + passed into the helper call. - var bind = __dependency7__.bind; - var _triageMustacheHelper = __dependency7__._triageMustacheHelper; - var resolveHelper = __dependency7__.resolveHelper; - var bindHelper = __dependency7__.bindHelper; + ```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(''); + }); + ``` - var ifHelper = __dependency8__.ifHelper; - var boundIfHelper = __dependency8__.boundIfHelper; - var unboundIfHelper = __dependency8__.unboundIfHelper; - var unlessHelper = __dependency8__.unlessHelper; + This helper could be used in a template as follows: - var withHelper = __dependency9__["default"]; + ```handlebars + {{repeat text count=3}} + ``` - var bindAttrHelper = __dependency10__.bindAttrHelper; - var bindAttrHelperDeprecated = __dependency10__.bindAttrHelperDeprecated; - var bindClasses = __dependency10__.bindClasses; + ## Example with bound options - var collectionHelper = __dependency11__["default"]; - var ViewHelper = __dependency12__.ViewHelper; - var viewHelper = __dependency12__.viewHelper; - var unboundHelper = __dependency13__["default"]; - var logHelper = __dependency14__.logHelper; - var debuggerHelper = __dependency14__.debuggerHelper; - var EachView = __dependency15__.EachView; - var eachHelper = __dependency15__.eachHelper; - var templateHelper = __dependency16__["default"]; - var partialHelper = __dependency17__["default"]; - var yieldHelper = __dependency18__["default"]; - var locHelper = __dependency19__["default"]; + Bound hash options are also supported. Example: + ```handlebars + {{repeat text count=numRepeats}} + ``` - var Checkbox = __dependency20__["default"]; - var Select = __dependency21__.Select; - var SelectOption = __dependency21__.SelectOption; - var SelectOptgroup = __dependency21__.SelectOptgroup; - var TextArea = __dependency22__["default"]; - var TextField = __dependency23__["default"]; - var TextSupport = __dependency24__["default"]; - var inputHelper = __dependency25__.inputHelper; - var textareaHelper = __dependency25__.textareaHelper; + 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 ComponentLookup = __dependency26__["default"]; - var _HandlebarsBoundView = __dependency27__._HandlebarsBoundView; - var SimpleHandlebarsView = __dependency27__.SimpleHandlebarsView; - var _MetamorphView = __dependency28__["default"]; - var _SimpleMetamorphView = __dependency28__._SimpleMetamorphView; - var _Metamorph = __dependency28__._Metamorph; + ## Example with extra dependencies + 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. - /** - Ember Handlebars + ```javascript + Ember.Handlebars.registerBoundHelper('capitalizeName', function(value) { + return value.get('name').toUpperCase(); + }, 'name'); + ``` - @module ember - @submodule ember-handlebars - @requires ember-views - */ + ## Example with multiple bound properties - // Ember.Handlebars.Globals - EmberHandlebars.bootstrap = bootstrap; - EmberHandlebars.makeBoundHelper = makeBoundHelper; - EmberHandlebars.registerBoundHelper = registerBoundHelper; - EmberHandlebars.resolveHelper = resolveHelper; - EmberHandlebars.bind = bind; - EmberHandlebars.bindClasses = bindClasses; - EmberHandlebars.EachView = EachView; - EmberHandlebars.ViewHelper = ViewHelper; + `Ember.Handlebars.registerBoundHelper` supports binding to + multiple properties, e.g.: + ```javascript + Ember.Handlebars.registerBoundHelper('concatenate', function() { + var values = Array.prototype.slice.call(arguments, 0, -1); + return values.join('||'); + }); + ``` - // Ember Globals - Ember.Handlebars = EmberHandlebars; - EmberHandlebars.get = handlebarsGet; - Ember.ComponentLookup = ComponentLookup; - Ember._SimpleHandlebarsView = SimpleHandlebarsView; - Ember._HandlebarsBoundView = _HandlebarsBoundView; - Ember._SimpleMetamorphView = _SimpleMetamorphView; - Ember._MetamorphView = _MetamorphView; - Ember._Metamorph = _Metamorph; - Ember.TextSupport = TextSupport; - Ember.Checkbox = Checkbox; - Ember.Select = Select; - Ember.SelectOption = SelectOption; - Ember.SelectOptgroup = SelectOptgroup; - Ember.TextArea = TextArea; - Ember.TextField = TextField; - Ember.TextSupport = TextSupport; + 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. - // register helpers - EmberHandlebars.registerHelper('helperMissing', helperMissingHelper); - EmberHandlebars.registerHelper('blockHelperMissing', blockHelperMissingHelper); - EmberHandlebars.registerHelper('bind', bindHelper); - EmberHandlebars.registerHelper('boundIf', boundIfHelper); - EmberHandlebars.registerHelper('_triageMustache', _triageMustacheHelper); - EmberHandlebars.registerHelper('unboundIf', unboundIfHelper); - EmberHandlebars.registerHelper('with', withHelper); - EmberHandlebars.registerHelper('if', ifHelper); - EmberHandlebars.registerHelper('unless', unlessHelper); - EmberHandlebars.registerHelper('bind-attr', bindAttrHelper); - EmberHandlebars.registerHelper('bindAttr', bindAttrHelperDeprecated); - EmberHandlebars.registerHelper('collection', collectionHelper); - EmberHandlebars.registerHelper("log", logHelper); - EmberHandlebars.registerHelper("debugger", debuggerHelper); - EmberHandlebars.registerHelper("each", eachHelper); - EmberHandlebars.registerHelper("loc", locHelper); - EmberHandlebars.registerHelper("partial", partialHelper); - EmberHandlebars.registerHelper("template", templateHelper); - EmberHandlebars.registerHelper("yield", yieldHelper); - EmberHandlebars.registerHelper("view", viewHelper); - EmberHandlebars.registerHelper("unbound", unboundHelper); - EmberHandlebars.registerHelper("input", inputHelper); - EmberHandlebars.registerHelper("textarea", textareaHelper); - - // run load hooks - runLoadHooks('Ember.Handlebars', EmberHandlebars); + ## Use with unbound helper - __exports__["default"] = EmberHandlebars; - }); -enifed("ember-handlebars/component_lookup", - ["ember-runtime/system/object","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var EmberObject = __dependency1__["default"]; + The `{{unbound}}` helper can be used with bound helper invocations + to render them in their unbound form, e.g. - __exports__["default"] = EmberObject.extend({ - lookupFactory: function(name, container) { + ```handlebars + {{unbound capitalize name}} + ``` - container = container || this.container; + In this example, if the name property changes, the helper + will not re-render. - var fullName = 'component:' + name; - var templateFullName = 'template:components/' + name; - var templateRegistered = container && container.has(templateFullName); + ## Use with blocks not supported - if (templateRegistered) { - container.injection(fullName, 'layout', templateFullName); - } + Bound helpers do not support use with Handlebars blocks or + the addition of child views of any kind. - var Component = container.lookupFactory(fullName); + @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); - // 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; - } - } - }); + helpers[name] = boundFn; + } }); -enifed("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__) { +enifed("ember-htmlbars/helpers", + ["ember-metal/platform","ember-htmlbars/system/helper","exports"], + function(__dependency1__, __dependency2__, __exports__) { "use strict"; - var Checkbox = __dependency1__["default"]; - var TextField = __dependency2__["default"]; - var TextArea = __dependency3__["default"]; + /** + @module ember + @submodule ember-htmlbars + */ - var Ember = __dependency4__["default"]; - // Ember.assert - // var emberAssert = Ember.assert; + var o_create = __dependency1__.create; - var EmberHandlebars = __dependency5__["default"]; + /** + @private + @property helpers + */ + var helpers = o_create(null); /** @module ember - @submodule ember-handlebars-compiler + @submodule ember-htmlbars */ + var Helper = __dependency2__["default"]; + /** + @private + @method _registerHelper + @for Ember.HTMLBars + @param {String} name + @param {Object|Function} helperFunc the helper function to add + */ + function registerHelper(name, helperFunc) { + var helper; - 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. + if (helperFunc && helperFunc.isHelper) { + helper = helperFunc; + } else { + helper = new Helper(helperFunc); + } - ## 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: + helpers[name] = helper; + } - - - - - - - - - - - -
`readonly``required``autofocus`
`value``placeholder``disabled`
`size``tabindex``maxlength`
`name``min``max`
`pattern``accept``autocomplete`
`autosave``formaction``formenctype`
`formmethod``formnovalidate``formtarget`
`height``inputmode``multiple`
`step``width``form`
`selectionDirection``spellcheck` 
+ __exports__.registerHelper = registerHelper;__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"; + /** + @module ember + @submodule ember-htmlbars + */ + var Ember = __dependency1__["default"]; + // Ember.assert - 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). + 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; - ## Unbound: + /** + `bind-attr` allows you to create a binding between DOM element attributes and + Ember objects. For example: ```handlebars - {{input value="http://www.facebook.com"}} + imageTitle}} ``` + 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`. - ```html - - ``` - - ## Bound: + If the rendering context of this template is the following object: ```javascript - App.ApplicationController = Ember.Controller.extend({ - firstName: "Stanley", - entryNotAllowed: true - }); + { + imageUrl: 'http://lolcats.info/haz-a-funny', + imageTitle: 'A humorous image of a cat' + } ``` + The resulting HTML output will be: - ```handlebars - {{input type="text" value=firstName disabled=entryNotAllowed size="50"}} + ```html + A humorous image of a cat ``` - - ```html - - ``` - - ## Actions - - The helper can send multiple actions based on user events. - - The action property defines the action which is sent when - the user presses the return key. - + `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: ```handlebars - {{input action="submit"}} + imageTitle}} ``` + ### `bind-attr` and the `class` attribute - The helper allows some user events to send actions. - - * `enter` - * `insert-newline` - * `escape-press` - * `focus-in` - * `focus-out` - * `key-press` + `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: + * a string return value of an object's property. + * a boolean return value of an object's property + * a hard-coded value - 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. + 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: + ```javascript + AView = View.extend({ + someProperty: function() { + return "aValue"; + }.property() + }) + ``` ```handlebars - {{input focus-in="alertMessage"}} + ``` + Result in the following rendered output: - See more about [Text Support Actions](/api/classes/Ember.TextField.html) - - ## Extension + ```html + + ``` - 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: + 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`. + A class name is provided via the syntax + `somePropertyName:class-name-if-true`. ```javascript - Ember.TextField.reopen({ - attributeBindings: ['data-error'] - }); + AView = View.extend({ + someBool: true + }) ``` - 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. - - See more about [Ember components](/api/classes/Ember.Component.html) - - - ## Use as checkbox + ```handlebars + + ``` - An `{{input}}` with a `type` of `checkbox` will render an HTML checkbox input. - The following HTML attributes can be set via the helper: + Result in the following rendered output: - * `checked` - * `disabled` - * `tabindex` - * `indeterminate` - * `name` - * `autofocus` - * `form` + ```html + + ``` + 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: - 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). + ```handlebars + + ``` - ## Unbound: + A hard-coded value can be used by prepending `:` to the desired + class name: `:class-name-to-always-apply`. ```handlebars - {{input type="checkbox" name="isAdmin"}} + ``` + Results in the following rendered output: + ```html - + ``` - ## Bound: + All three strategies - string return value, boolean return value, and + hard-coded value – can be combined in a single declaration: - ```javascript - App.ApplicationController = Ember.Controller.extend({ - isAdmin: true - }); + ```handlebars + ``` + @method bind-attr + @for Ember.Handlebars.helpers + @param {Hash} options + @return {String} HTML string + */ + function bindAttrHelper(params, hash, options, env) { + var element = options.element; - ```handlebars - {{input type="checkbox" checked=isAdmin }} - ``` + Ember.assert("You must specify at least one hash argument to bind-attr", !!keys(hash).length); + var view = this; - ```html - - ``` + // 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); + } - ## Extension + var classView = new AttrNode('class', classNameBindings); + classView._morph = env.dom.createAttrMorph(element, 'class'); - 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: + Ember.assert( + 'You cannot set `class` manually and via `{{bind-attr}}` helper on the same element. ' + + 'Please use `{{bind-attr}}`\'s `:static-class` syntax instead.', + !element.getAttribute('class') + ); + view.appendChild(classView); + } - ```javascript - Ember.Checkbox.reopen({ - classNames: ['my-app-checkbox'] + var attrKeys = keys(hash); + + var attr, path, lazyValue, attrView; + for (var i=0, l=attrKeys.length;i` tag into the template. - The attributes of `{{textarea}}` match those of the native HTML tags as - closely as possible. + // Set up observers for observable objects + var viewClass = _viewClass || BoundView; + var viewOptions = { + _morph: options.morph, + preserveContext: preserveContext, + shouldDisplayFunc: shouldDisplay, + valueNormalizerFunc: valueNormalizer, + displayTemplate: options.template, + inverseTemplate: options.inverse, + lazyValue: lazyValue, + previousContext: get(this, 'context'), + isEscaped: !hash.unescaped, + templateHash: hash, + helperName: options.helperName + }; - The following HTML attributes can be set: + if (options.keywords) { + viewOptions._keywords = options.keywords; + } - * `value` - * `name` - * `rows` - * `cols` - * `placeholder` - * `disabled` - * `maxlength` - * `tabindex` - * `selectionEnd` - * `selectionStart` - * `selectionDirection` - * `wrap` - * `readonly` - * `autofocus` - * `form` - * `spellcheck` - * `required` + // 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); - 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). + this.appendChild(bindView); - Unbound: + lazyValue.subscribe(this._wrapAsScheduled(function() { + run.scheduleOnce('render', bindView, 'rerenderIfNeeded'); + })); + } + + /** + `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`: ```handlebars - {{textarea value="Lots of static text that ISN'T bound"}} + {{bind "content.title"}} ``` - Would result in the following HTML: + 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. - ```html - - ``` + @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); - Bound: + var property = params[0]; - 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. + if (typeof property === 'string') { + property = this.getStream(property); + } - ```javascript - App.ApplicationController = Ember.Controller.extend({ - writtenWords: "Lots of text that IS bound" - }); - ``` + 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."); - ```handlebars - {{textarea value=writtenWords}} - ``` + return property; + } + } - Would result in the following HTML: + __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"; + /** + @module ember + @submodule ember-htmlbars + */ - ```html - + var Ember = __dependency1__["default"]; + // 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; + + /** + `{{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. + + The provided block will be applied as the template for each item's view. + + Given an empty `` the following template: + + ```handlebars + {{! application.hbs }} + {{#collection content=model}} + Hi {{view.content.name}} + {{/collection}} ``` - 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`: + And the following application code ```javascript - App.ApplicationController = Ember.Controller.extend({ - writtenWords: "Lots of text that IS bound", - outputWrittenWords: Ember.computed.oneWay("writtenWords") + App = Ember.Application.create(); + App.ApplicationRoute = Ember.Route.extend({ + model: function(){ + return [{name: 'Yehuda'},{name: 'Tom'},{name: 'Peter'}]; + } }); ``` - ```handlebars - {{textarea value=writtenWords}} + The following HTML will result: -
- {{outputWrittenWords}} + ```html +
+
Hi Yehuda
+
Hi Tom
+
Hi Peter
``` - Would result in the following HTML: + ### Non-block version of collection - ```html - + If you provide an `itemViewClass` option that has its own `template` you may + omit the block. - <-- the following div will be updated in real time as you type --> + The following template: -
- Lots of text that IS bound -
+ ```handlebars + {{! application.hbs }} + {{collection content=model itemViewClass="an-item"}} ``` - 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: + And application code ```javascript - App.ApplicationController = Ember.Controller.extend({ - writtenWords: "Lots of text that IS bound", - twoWayWrittenWords: Ember.computed.alias("writtenWords") + App = Ember.Application.create(); + App.ApplicationRoute = Ember.Route.extend({ + model: function(){ + return [{name: 'Yehuda'},{name: 'Tom'},{name: 'Peter'}]; + } }); - ``` - ```handlebars - {{textarea value=writtenWords}} - {{textarea value=twoWayWrittenWords}} + App.AnItemView = Ember.View.extend({ + template: Ember.Handlebars.compile("Greetings {{view.content.name}}") + }); ``` - ```html - - - <-- both updated in real time --> + Will result in the HTML structure below - + ```html +
+
Greetings Yehuda
+
Greetings Tom
+
Greetings Peter
+
``` - ## Actions - - The helper can send multiple actions based on user events. + ### Specifying a CollectionView subclass - The action property defines the action which is send when - the user presses the return key. + 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: ```handlebars - {{input action="submit"}} + {{#collection "my-custom-collection" content=model}} + Hi {{view.content.name}} + {{/collection}} ``` - The helper allows some user events to send actions. + This example would look for the class `App.MyCustomCollection`. - * `enter` - * `insert-newline` - * `escape-press` - * `focus-in` - * `focus-out` - * `key-press` + ### Forwarded `item.*`-named Options - 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. + 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 - {{textarea focus-in="alertMessage"}} + {{#collection content=model + itemTagName="p" + itemClassNames="greeting"}} + Howdy {{view.content.name}} + {{/collection}} ``` - See more about [Text Support Actions](/api/classes/Ember.TextArea.html) - - ## Extension - - 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: + Will result in the following HTML structure: - ```javascript - Ember.TextArea.reopen({ - attributeBindings: ['data-error'] - }); + ```html +
+

Howdy Yehuda

+

Howdy Tom

+

Howdy Peter

+
``` - 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. - - See more about [Ember components](/api/classes/Ember.Component.html) - - @method textarea + @method collection @for Ember.Handlebars.helpers + @param {String} path @param {Hash} options + @return {String} HTML string + @deprecated Use `{{each}}` helper instead. */ - function textareaHelper(options) { - Ember.assert('You can only pass attributes to the `textarea` helper, not arguments', arguments.length < 2); + function collectionHelper(params, hash, options, env) { + var path = params[0]; - return EmberHandlebars.helpers.view.call(this, TextArea, 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'); - __exports__.textareaHelper = textareaHelper; - }); -enifed("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__["default"]; + Ember.assert("You cannot pass more than one argument to the collection helper", params.length <= 1); - /** - @module ember - @submodule ember-handlebars - */ + var data = env.data; + var template = options.template; + var inverse = options.inverse; + var view = data.view; - /** - The internal class used to create text inputs when the `{{input}}` - helper is used with `type` of `checkbox`. + // 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); - See [handlebars.helpers.input](/api/classes/Ember.Handlebars.helpers.html#method_input) for usage details. + // 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; + } - ## Direct manipulation of `checked` + var itemHash = {}; + var match; - 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. - - @class Checkbox - @namespace Ember - @extends Ember.View - */ - __exports__["default"] = View.extend({ - instrumentDisplay: '{{input type="checkbox"}}', - - classNames: ['ember-checkbox'], + // Extract item view class if provided else default to the standard class + var collectionPrototype = collectionClass.proto(); + var itemViewClass; - tagName: 'input', + if (hash.itemView) { + itemViewClass = readViewFactory(hash.itemView, container); + } else if (hash.itemViewClass) { + itemViewClass = readViewFactory(hash.itemViewClass, container); + } else { + itemViewClass = collectionPrototype.itemViewClass; + } - attributeBindings: [ - 'type', - 'checked', - 'indeterminate', - 'disabled', - 'tabindex', - 'name', - 'autofocus', - 'required', - 'form' - ], + if (typeof itemViewClass === 'string') { + itemViewClass = container.lookupFactory('view:'+itemViewClass); + } - type: 'checkbox', - checked: false, - disabled: false, - indeterminate: false, + Ember.assert(fmt("%@ #collection: Could not find itemViewClass %@", [data.view, itemViewClass]), !!itemViewClass); - init: function() { - this._super(); - this.on('change', this, this._updateElementValue); - }, + delete hash.itemViewClass; + delete hash.itemView; - didInsertElement: function() { - this._super(); - get(this, 'element').indeterminate = !!get(this, 'indeterminate'); - }, + // 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]; - _updateElementValue: function() { - set(this, 'checked', this.$().prop('checked')); + if (IS_BINDING.test(prop)) { + itemHash[childProp] = view._getBindingForStream(hash[prop]); + } else { + itemHash[childProp] = hash[prop]; + } + delete hash[prop]; + } + } } - }); - }); -enifed("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"; - /** - @module ember - @submodule ember-handlebars - */ - var EmberHandlebars = __dependency1__["default"]; + if (template) { + itemHash.template = template; + delete options.template; + } - var forEach = __dependency2__.forEach; - var indexOf = __dependency2__.indexOf; - var indexesOf = __dependency2__.indexesOf; - var replace = __dependency2__.replace; + 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; } - var get = __dependency3__.get; - var set = __dependency4__.set; - var View = __dependency5__["default"]; - var CollectionView = __dependency6__["default"]; - var isArray = __dependency7__.isArray; - var isNone = __dependency8__["default"]; - var computed = __dependency9__.computed; - var emberA = __dependency10__.A; - var observer = __dependency11__.observer; - var defineProperty = __dependency12__.defineProperty; + if (hash.keyword) { + itemHash._contextBinding = Binding.oneWay('_parentView.context'); + } else { + itemHash._contextBinding = Binding.oneWay('content'); + } + var viewOptions = ViewHelper.propertiesFromHTMLOptions(itemHash, {}, { data: data }); - var SelectOption = View.extend({ - instrumentDisplay: 'Ember.SelectOption', + if (hash.itemClassBinding) { + var itemClassBindings = hash.itemClassBinding.split(' '); + viewOptions.classNameBindings = map(itemClassBindings, function(classBinding){ + return streamifyClassNameBinding(view, classBinding); + }); + } - tagName: 'option', - attributeBindings: ['value', 'selected'], + hash.itemViewClass = itemViewClass; + hash._itemViewProps = viewOptions; - defaultTemplate: function(context, options) { - options = { data: options.data, hash: {} }; - EmberHandlebars.helpers.bind.call(context, "view.label", options); - }, + options.helperName = options.helperName || 'collection'; - init: function() { - this.labelPathDidChange(); - this.valuePathDidChange(); + return env.helpers.view.helperFunction.call(this, [collectionClass], hash, options, env); + } - this._super(); - }, + __exports__.collectionHelper = collectionHelper; + }); +enifed("ember-htmlbars/helpers/debugger", + ["ember-metal/logger","exports"], + function(__dependency1__, __exports__) { + "use strict"; + /*jshint debug:true*/ - selected: computed(function() { - var content = get(this, 'content'); - var 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; // jshint ignore:line - } - }).property('content', 'parentView.selection'), + /** + @module ember + @submodule ember-htmlbars + */ + var Logger = __dependency1__["default"]; - labelPathDidChange: observer('parentView.optionLabelPath', function() { - var labelPath = get(this, 'parentView.optionLabelPath'); + /** + Execute the `debugger` statement in the current context. - if (!labelPath) { return; } + ```handlebars + {{debugger}} + ``` - defineProperty(this, 'label', computed(function() { - return get(this, labelPath); - }).property(labelPath)); - }), + 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: - valuePathDidChange: observer('parentView.optionValuePath', function() { - var valuePath = get(this, 'parentView.optionValuePath'); + - templateContext: this is most likely a controller + from which this template looks up / displays properties + - typeOfTemplateContext: a string description of + what the templateContext is - if (!valuePath) { return; } + 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`: - defineProperty(this, 'value', computed(function() { - return get(this, valuePath); - }).property(valuePath)); - }) - }); + ``` + > templateContext.get('foo') // -> "" + ``` - var SelectOptgroup = CollectionView.extend({ - instrumentDisplay: 'Ember.SelectOptgroup', + @method debugger + @for Ember.Handlebars.helpers + @param {String} property + */ + function debuggerHelper() { - tagName: 'optgroup', - attributeBindings: ['label'], + // These are helpful values you can inspect while debugging. + /* jshint unused: false */ + var view = this; + Logger.info('Use `this` to access the view context.'); - selectionBinding: 'parentView.selection', - multipleBinding: 'parentView.multiple', - optionLabelPathBinding: 'parentView.optionLabelPath', - optionValuePathBinding: 'parentView.optionValuePath', + debugger; + } - itemViewClassBinding: 'parentView.optionView' - }); + __exports__.debuggerHelper = debuggerHelper; + }); +enifed("ember-htmlbars/helpers/each", + ["ember-metal/core","ember-views/views/each","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; /** - 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. - - The text and `value` property of each ` - - +
    +
  • Yehuda
  • +
  • Tom
  • +
  • Paul
  • +
``` - The `value` attribute of the selected `"); - return buffer; - },"3":function(depth0,helpers,partials,data) { - var stack1; - stack1 = helpers.each.call(depth0, "group", "in", "view.groupedContent", {"name":"each","hash":{},"hashTypes":{},"hashContexts":{},"fn":this.program(4, data),"inverse":this.noop,"types":["ID","ID","ID"],"contexts":[depth0,depth0,depth0],"data":data}); - if (stack1 != null) { data.buffer.push(stack1); } - else { data.buffer.push(''); } - },"4":function(depth0,helpers,partials,data) { - var escapeExpression=this.escapeExpression; - data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.groupView", {"name":"view","hash":{ - 'label': ("group.label"), - 'content': ("group.content") - },"hashTypes":{'label': "ID",'content': "ID"},"hashContexts":{'label': depth0,'content': depth0},"types":["ID"],"contexts":[depth0],"data":data}))); - },"6":function(depth0,helpers,partials,data) { - var stack1; - stack1 = helpers.each.call(depth0, "item", "in", "view.content", {"name":"each","hash":{},"hashTypes":{},"hashContexts":{},"fn":this.program(7, data),"inverse":this.noop,"types":["ID","ID","ID"],"contexts":[depth0,depth0,depth0],"data":data}); - if (stack1 != null) { data.buffer.push(stack1); } - else { data.buffer.push(''); } - },"7":function(depth0,helpers,partials,data) { - var escapeExpression=this.escapeExpression; - data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.optionView", {"name":"view","hash":{ - 'content': ("item") - },"hashTypes":{'content': "ID"},"hashContexts":{'content': depth0},"types":["ID"],"contexts":[depth0],"data":data}))); - },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) { - var stack1, buffer = ''; - stack1 = helpers['if'].call(depth0, "view.prompt", {"name":"if","hash":{},"hashTypes":{},"hashContexts":{},"fn":this.program(1, data),"inverse":this.noop,"types":["ID"],"contexts":[depth0],"data":data}); - if (stack1 != null) { data.buffer.push(stack1); } - stack1 = helpers['if'].call(depth0, "view.optionGroupPath", {"name":"if","hash":{},"hashTypes":{},"hashContexts":{},"fn":this.program(3, data),"inverse":this.program(6, data),"types":["ID"],"contexts":[depth0],"data":data}); - if (stack1 != null) { data.buffer.push(stack1); } - return buffer; - },"useData":true}), - attributeBindings: ['multiple', 'disabled', 'tabindex', 'name', 'required', 'autofocus', - 'form', 'size'], - - /** - The `multiple` attribute of the select element. Indicates whether multiple - options can be selected. + var Ember = __dependency1__["default"]; + // Ember.assert + var bind = __dependency2__.bind; - @property multiple - @type Boolean - @default false - */ - multiple: false, + var get = __dependency3__.get; + var isArray = __dependency4__.isArray; + var ConditionalStream = __dependency5__["default"]; + var isStream = __dependency6__.isStream; - /** - The `disabled` attribute of the select element. Indicates whether - the element is disabled from interactions. + function shouldDisplayIfHelperContent(result) { + var truthy = result && get(result, 'isTruthy'); + if (typeof truthy === 'boolean') { return truthy; } - @property disabled - @type Boolean - @default false - */ - disabled: false, + if (isArray(result)) { + return get(result, 'length') !== 0; + } else { + return !!result; + } + } - /** - The `required` attribute of the select element. Indicates whether - a selected option is required for form validation. + 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. - @property required - @type Boolean - @default false - @since 1.5.0 - */ - required: false, + ```handlebars + {{#boundIf "content.shouldDisplayTitle"}} + {{content.title}} + {{/boundIf}} + ``` - /** - The list of options. + @private + @method boundIf + @for Ember.Handlebars.helpers + @param {String} property Property to bind + @param {Function} fn Context to provide for rendering + @return {String} HTML string + */ + function boundIfHelper(params, hash, options, env) { + options.helperName = options.helperName || 'boundIf'; + return bind.call(this, params[0], hash, options, env, true, shouldDisplayIfHelperContent, shouldDisplayIfHelperContent, [ + 'isTruthy', + 'length' + ]); + } - If `optionLabelPath` and `optionValuePath` are not overridden, this should - be a list of strings, which will serve simultaneously as labels and values. + /** + @private - Otherwise, this should be a list of objects. For instance: + Use the `unboundIf` helper to create a conditional that evaluates once. - ```javascript - var App = Ember.Application.create(); - var App.MySelect = Ember.Select.extend({ - content: Ember.A([ - { id: 1, firstName: 'Yehuda' }, - { id: 2, firstName: 'Tom' } - ]), - optionLabelPath: 'content.firstName', - optionValuePath: 'content.id' - }); - ``` + ```handlebars + {{#unboundIf "content.shouldDisplayTitle"}} + {{content.title}} + {{/unboundIf}} + ``` - @property content - @type Array - @default null - */ - content: null, + @method unboundIf + @for Ember.Handlebars.helpers + @param {String} property Property to bind + @param {Function} fn Context to provide for rendering + @return {String} HTML string + @since 1.4.0 + */ + function unboundIfHelper(params, hash, options, env) { + var template = options.template; + var value = params[0]; - /** - When `multiple` is `false`, the element of `content` that is currently - selected, if any. + if (isStream(params[0])) { + value = params[0].value(); + } - When `multiple` is `true`, an array of such elements. + if (!shouldDisplayIfHelperContent(value)) { + template = options.inverse || EMPTY_TEMPLATE; + } - @property selection - @type Object or Array - @default null - */ - selection: null, + return template.render(this, env, options.morph.contextualElement); + } - /** - 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. + function _inlineIfAssertion(params) { + Ember.assert("If helper in inline form expects between two and three arguments", params.length === 2 || params.length === 3); + } - It is not currently supported in multiple selection mode. + /** + See [boundIf](/api/classes/Ember.Handlebars.helpers.html#method_boundIf) + and [unboundIf](/api/classes/Ember.Handlebars.helpers.html#method_unboundIf) - @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'), + @method if + @for Ember.Handlebars.helpers + @param {Function} context + @param {Hash} options + @return {String} HTML string + */ + 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; - /** - If given, a top-most dummy option will be rendered to serve as a user - prompt. + options.helperName = options.helperName || ('if '); - @property prompt - @type String - @default null - */ - prompt: null, + 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); + } + } - /** - The path of the option labels. See [content](/api/classes/Ember.Select.html#property_content). + /** + @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); - @property optionLabelPath - @type String - @default 'content' - */ - optionLabelPath: 'content', + var template = options.template; + var inverse = options.inverse || EMPTY_TEMPLATE; + var helperName = 'unless'; - /** - The path of the option values. See [content](/api/classes/Ember.Select.html#property_content). + options.template = inverse; + options.inverse = template; - @property optionValuePath - @type String - @default 'content' - */ - optionValuePath: 'content', + options.helperName = options.helperName || helperName; - /** - The path of the option group. - When this property is used, `content` should be sorted by `optionGroupPath`. + 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); + } + } - @property optionGroupPath - @type String - @default null - */ - optionGroupPath: null, + __exports__.ifHelper = ifHelper; + __exports__.boundIfHelper = boundIfHelper; + __exports__.unboundIfHelper = unboundIfHelper; + __exports__.unlessHelper = unlessHelper; + }); +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 Checkbox = __dependency1__["default"]; + var TextField = __dependency2__["default"]; + var read = __dependency3__.read; - /** - The view class for optgroup. + var Ember = __dependency4__["default"]; + // Ember.assert - @property groupView - @type Ember.View - @default Ember.SelectOptgroup - */ - groupView: SelectOptgroup, + /** + @module ember + @submodule ember-htmlbars + */ - groupedContent: computed(function() { - var groupPath = get(this, 'optionGroupPath'); - var groupedContent = emberA(); - var content = get(this, 'content') || []; + /** - forEach(content, function(item) { - var label = get(item, groupPath); + 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. - if (get(groupedContent, 'lastObject.label') !== label) { - groupedContent.pushObject({ - label: label, - content: emberA() - }); - } + ## 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: - get(groupedContent, 'lastObject.content').push(item); - }); + + + + + + + + + + + +
`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` 
- return groupedContent; - }).property('optionGroupPath', 'content.@each'), - /** - The view class for option. + 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). - @property optionView - @type Ember.View - @default Ember.SelectOption - */ - optionView: SelectOption, + ## Unbound: - _change: function() { - if (get(this, 'multiple')) { - this._changeMultiple(); - } else { - this._changeSingle(); - } - }, + ```handlebars + {{input value="http://www.facebook.com"}} + ``` - selectionDidChange: observer('selection.@each', function() { - var selection = get(this, 'selection'); - if (get(this, 'multiple')) { - if (!isArray(selection)) { - set(this, 'selection', emberA([selection])); - return; - } - this._selectionDidChangeMultiple(); - } else { - this._selectionDidChangeSingle(); - } - }), - valueDidChange: observer('value', function() { - var content = get(this, 'content'); - var value = get(this, 'value'); - var valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''); - var selectedValue = (valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection')); - var selection; + ```html + + ``` - if (value !== selectedValue) { - selection = content ? content.find(function(obj) { - return value === (valuePath ? get(obj, valuePath) : obj); - }) : null; + ## Bound: - this.set('selection', selection); - } - }), + ```javascript + App.ApplicationController = Ember.Controller.extend({ + firstName: "Stanley", + entryNotAllowed: true + }); + ``` - _triggerChange: function() { - var selection = get(this, 'selection'); - var value = get(this, 'value'); + ```handlebars + {{input type="text" value=firstName disabled=entryNotAllowed size="50"}} + ``` - if (!isNone(selection)) { this.selectionDidChange(); } - if (!isNone(value)) { this.valueDidChange(); } - this._change(); - }, + ```html + + ``` - _changeSingle: function() { - var selectedIndex = this.$()[0].selectedIndex; - var content = get(this, 'content'); - var prompt = get(this, 'prompt'); + ## Actions - if (!content || !get(content, 'length')) { return; } - if (prompt && selectedIndex === 0) { set(this, 'selection', null); return; } + The helper can send multiple actions based on user events. - if (prompt) { selectedIndex -= 1; } - set(this, 'selection', content.objectAt(selectedIndex)); - }, + The action property defines the action which is sent when + the user presses the return key. + ```handlebars + {{input action="submit"}} + ``` - _changeMultiple: function() { - var options = this.$('option:selected'); - var prompt = get(this, 'prompt'); - var offset = prompt ? 1 : 0; - var content = get(this, 'content'); - var selection = get(this, 'selection'); + The helper allows some user events to send actions. - if (!content) { return; } - if (options) { - var selectedIndexes = options.map(function() { - return this.index - offset; - }).toArray(); - var newSelection = content.objectsAt(selectedIndexes); + * `enter` + * `insert-newline` + * `escape-press` + * `focus-in` + * `focus-out` + * `key-press` - if (isArray(selection)) { - replace(selection, 0, get(selection, 'length'), newSelection); - } else { - set(this, 'selection', newSelection); - } - } - }, - _selectionDidChangeSingle: function() { - var el = this.get('element'); - if (!el) { return; } + 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. - var content = get(this, 'content'); - var selection = get(this, 'selection'); - var selectionIndex = content ? indexOf(content, selection) : -1; - var prompt = get(this, 'prompt'); + ```handlebars + {{input focus-in="alertMessage"}} + ``` - if (prompt) { selectionIndex += 1; } - if (el) { el.selectedIndex = selectionIndex; } - }, + See more about [Text Support Actions](/api/classes/Ember.TextField.html) - _selectionDidChangeMultiple: function() { - var content = get(this, 'content'); - var selection = get(this, 'selection'); - var selectedIndexes = content ? indexesOf(content, selection) : [-1]; - var prompt = get(this, 'prompt'); - var offset = prompt ? 1 : 0; - var options = this.$('option'); - var adjusted; + ## Extension - if (options) { - options.each(function() { - adjusted = this.index > -1 ? this.index - offset : -1; - this.selected = indexOf(selectedIndexes, adjusted) > -1; - }); - } - }, + 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: - init: function() { - this._super(); - this.on("didInsertElement", this, this._triggerChange); - this.on("change", this, this._change); - } - }); - __exports__["default"] = Select; - __exports__.Select = Select; - __exports__.SelectOption = SelectOption; - __exports__.SelectOptgroup = SelectOptgroup; - }); -enifed("ember-handlebars/controls/text_area", - ["ember-metal/property_get","ember-views/views/component","ember-handlebars/controls/text_support","ember-metal/mixin","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; + ```javascript + Ember.TextField.reopen({ + attributeBindings: ['data-error'] + }); + ``` - /** - @module ember - @submodule ember-handlebars - */ - var get = __dependency1__.get; - var Component = __dependency2__["default"]; - var TextSupport = __dependency3__["default"]; - var observer = __dependency4__.observer; + 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. - /** - The internal class used to create textarea element when the `{{textarea}}` - helper is used. + See more about [Ember components](/api/classes/Ember.Component.html) - See [handlebars.helpers.textarea](/api/classes/Ember.Handlebars.helpers.html#method_textarea) for usage details. - ## Layout and LayoutName properties + ## Use as checkbox - 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. + An `{{input}}` with a `type` of `checkbox` will render an HTML checkbox input. + The following HTML attributes can be set via the helper: - @class TextArea - @namespace Ember - @extends Ember.Component - @uses Ember.TextSupport + * `checked` + * `disabled` + * `tabindex` + * `indeterminate` + * `name` + * `autofocus` + * `form` + + + When set to a quoted string, these values will be directly applied to the HTML + element. When left unquoted, these values will be bound to a property on the + template's current rendering context (most typically a controller instance). + + ## Unbound: + + ```handlebars + {{input type="checkbox" name="isAdmin"}} + ``` + + ```html + + ``` + + ## Bound: + + ```javascript + App.ApplicationController = Ember.Controller.extend({ + isAdmin: true + }); + ``` + + + ```handlebars + {{input type="checkbox" checked=isAdmin }} + ``` + + + ```html + + ``` + + ## Extension + + Internally, `{{input type="checkbox"}}` creates an instance of `Ember.Checkbox`, passing + arguments from the helper to `Ember.Checkbox`'s `create` method. You can extend the + capablilties of checkbox inputs in your applications by reopening this class. For example, + if you wanted to add a css class to all checkboxes in your application: + + + ```javascript + Ember.Checkbox.reopen({ + classNames: ['my-app-checkbox'] + }); + ``` + + + @method input + @for Ember.Handlebars.helpers + @param {Hash} options */ - __exports__["default"] = Component.extend(TextSupport, { - instrumentDisplay: '{{textarea}}', + function inputHelper(params, hash, options, env) { + Ember.assert('You can only pass attributes to the `input` helper, not arguments', params.length === 0); - classNames: ['ember-text-area'], + var onEvent = hash.on; + var inputType; - tagName: "textarea", - attributeBindings: [ - 'rows', - 'cols', - 'name', - 'selectionEnd', - 'selectionStart', - 'wrap', - 'lang', - 'dir' - ], - rows: null, - cols: null, + inputType = read(hash.type); - _updateElementValue: observer('value', function() { - // We do this check so cursor position doesn't get affected in IE - var value = get(this, 'value'); - var $el = this.$(); - if ($el && value !== $el.val()) { - $el.val(value); - } - }), + if (inputType === 'checkbox') { + delete hash.type; - init: function() { - this._super(); - this.on("didInsertElement", this, this._updateElementValue); + Ember.assert("{{input type='checkbox'}} does not support setting `value=someBooleanValue`;" + + " you must use `checked=someBooleanValue` instead.", !hash.hasOwnProperty('value')); + + env.helpers.view.helperFunction.call(this, [Checkbox], hash, options, env); + } else { + delete hash.on; + + hash.onEvent = onEvent || 'enter'; + env.helpers.view.helperFunction.call(this, [TextField], hash, options, env); } - }); + } + + __exports__.inputHelper = inputHelper; }); -enifed("ember-handlebars/controls/text_field", - ["ember-views/views/component","ember-handlebars/controls/text_support","exports"], - function(__dependency1__, __dependency2__, __exports__) { +enifed("ember-htmlbars/helpers/loc", + ["ember-metal/core","ember-runtime/system/string","ember-metal/streams/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; + var Ember = __dependency1__["default"]; + var loc = __dependency2__.loc; + var isStream = __dependency3__.isStream; + /** @module ember - @submodule ember-handlebars + @submodule ember-htmlbars */ - var Component = __dependency1__["default"]; - var TextSupport = __dependency2__["default"]; /** + Calls [Ember.String.loc](/api/classes/Ember.String.html#method_loc) with the + provided string. - The internal class used to create text inputs when the `{{input}}` - helper is used with `type` of `text`. - - See [Handlebars.helpers.input](/api/classes/Ember.Handlebars.helpers.html#method_input) for usage details. - - ## 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. - - @class TextField - @namespace Ember - @extends Ember.Component - @uses Ember.TextSupport - */ - __exports__["default"] = Component.extend(TextSupport, { - instrumentDisplay: '{{input type="text"}}', + This is a convenient way to localize text within a template: - classNames: ['ember-text-field'], - tagName: "input", - attributeBindings: [ - 'accept', - 'autocomplete', - 'autosave', - 'dir', - 'formaction', - 'formenctype', - 'formmethod', - 'formnovalidate', - 'formtarget', - 'height', - 'inputmode', - 'lang', - 'list', - 'max', - 'min', - 'multiple', - 'name', - 'pattern', - 'size', - 'step', - 'type', - 'value', - 'width' - ], + ```javascript + Ember.STRINGS = { + '_welcome_': 'Bonjour' + }; + ``` - /** - The `value` attribute of the input element. As the user inputs text, this - property is updated live. + ```handlebars +
+ {{loc '_welcome_'}} +
+ ``` - @property value - @type String - @default "" - */ - value: "", + ```html +
+ Bonjour +
+ ``` - /** - The `type` attribute of the input element. + See [Ember.String.loc](/api/classes/Ember.String.html#method_loc) for how to + set up localized string references. - @property type - @type String - @default "text" - */ - type: "text", + @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; + } + } + return true; + })()); - /** - The `size` of the text field in characters. + return loc.apply(this, params); + } - @property size - @type String - @default null - */ - size: null, + __exports__.locHelper = locHelper; + }); +enifed("ember-htmlbars/helpers/log", + ["ember-metal/logger","ember-metal/streams/utils","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-htmlbars + */ + var Logger = __dependency1__["default"]; + var read = __dependency2__.read; - /** - The `pattern` attribute of input element. + /** + `log` allows you to output the value of variables in the current rendering + context. `log` also accepts primitive types such as strings or numbers. - @property pattern - @type String - @default null - */ - pattern: null, + ```handlebars + {{log "myVariable:" myVariable }} + ``` - /** - The `min` attribute of input element used with `type="number"` or `type="range"`. + @method log + @for Ember.Handlebars.helpers + @param {String} property + */ + function logHelper(params, hash, options, env) { + var logger = Logger.log; + var values = []; - @property min - @type String - @default null - @since 1.4.0 - */ - min: null, + for (var i = 0; i < params.length; i++) { + values.push(read(params[i])); + } - /** - The `max` attribute of input element used with `type="number"` or `type="range"`. + logger.apply(logger, values); + } - @property max - @type String - @default null - @since 1.4.0 - */ - max: null - }); + __exports__.logHelper = logHelper; }); -enifed("ember-handlebars/controls/text_support", - ["ember-metal/property_get","ember-metal/property_set","ember-metal/mixin","ember-runtime/mixins/target_action_support","exports"], +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 + + var isNone = __dependency2__["default"]; + var bind = __dependency3__.bind; + var isStream = __dependency4__.isStream; + /** @module ember - @submodule ember-handlebars + @submodule ember-htmlbars */ - var get = __dependency1__.get; - var set = __dependency2__.set; - var Mixin = __dependency3__.Mixin; - var TargetActionSupport = __dependency4__["default"]; - /** - Shared mixin used by `Ember.TextField` and `Ember.TextArea`. + The `partial` helper renders another template without + changing the template context: - @class TextSupport - @namespace Ember - @uses Ember.TargetActionSupport - @extends Ember.Mixin - @private - */ - var TextSupport = Mixin.create(TargetActionSupport, { - value: "", + ```handlebars + {{foo}} + {{partial "nav"}} + ``` - attributeBindings: [ - 'autocapitalize', - 'autocorrect', - 'autofocus', - 'disabled', - 'form', - 'maxlength', - 'placeholder', - 'readonly', - 'required', - 'selectionDirection', - 'spellcheck', - 'tabindex', - 'title' - ], - placeholder: null, - disabled: false, - maxlength: null, + 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. - init: function() { - this._super(); - this.on("paste", this, this._elementValueDidChange); - this.on("cut", this, this._elementValueDidChange); - this.on("input", this, this._elementValueDidChange); - }, + If a "_nav" template isn't found, the `partial` helper will + fall back to a template named "nav". - /** - The action to be sent when the user presses the return key. + ## Bound template names - 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. + The parameter supplied to `partial` can also be a path + to a property containing a template name, e.g.: - @property action - @type String - @default null - */ - action: null, + ```handlebars + {{partial someTemplateName}} + ``` - /** - The event that should send the action. + 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. - Options are: - * `enter`: the user pressed enter - * `keyPress`: the user pressed a key + @method partial + @for Ember.Handlebars.helpers + @param {String} partialName the name of the template to render minus the leading underscore + */ - @property onEvent - @type String - @default enter - */ - onEvent: 'enter', + function partialHelper(params, hash, options, env) { + options.helperName = options.helperName || 'partial'; - /** - Whether the `keyUp` event that triggers an `action` to be sent continues - propagating to other views. + var name = params[0]; - 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. + 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); + } + } - If you would like parent views to receive the `keyUp` event even after an - action has been dispatched, set `bubbles` to true. + __exports__.partialHelper = partialHelper;function exists(value) { + return !isNone(value); + } - @property bubbles - @type Boolean - @default false - */ - bubbles: false, + function lookupPartial(view, templateName) { + var nameParts = templateName.split("/"); + var lastPart = nameParts[nameParts.length - 1]; - interpretKeyEvents: function(event) { - var map = TextSupport.KEY_EVENTS; - var method = map[event.keyCode]; + nameParts[nameParts.length - 1] = "_" + lastPart; - this._elementValueDidChange(); - if (method) { return this[method](event); } - }, + var underscoredName = nameParts.join('/'); + var template = view.templateForName(underscoredName); + if (!template) { + template = view.templateForName(templateName); + } - _elementValueDidChange: function() { - set(this, 'value', this.$().val()); - }, + Ember.assert('Unable to find partial with name "'+templateName+'"', !!template); - /** - Called when the user inserts a new line. + return template; + } - Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 13. - Uses sendAction to send the `enter` action. + function renderPartial(name, view, env, contextualElement) { + var template = lookupPartial(view, name); + return template.render(view, env, contextualElement); + } - @method insertNewline - @param {Event} event - */ - insertNewline: function(event) { - sendAction('enter', this, event); - sendAction('insert-newline', this, event); - }, + 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; - /** - Called when the user hits escape. + /** + @module ember + @submodule ember-htmlbars + */ - Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 27. - Uses sendAction to send the `escape-press` action. + /** + @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."); - @method cancel - @param {Event} event - */ - cancel: function(event) { - sendAction('escape-press', this, event); - }, + options.helperName = options.helperName || 'template'; - change: function(event) { - this._elementValueDidChange(event); - }, + return env.helpers.partial.helperFunction.call(this, params, hash, options, env); + } - /** - Called when the text area is focused. + __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 + */ - Uses sendAction to send the `focus-in` action. + var Ember = __dependency1__["default"]; + // Ember.assert + var TextArea = __dependency2__["default"]; - @method focusIn - @param {Event} event - */ - focusIn: function(event) { - sendAction('focus-in', this, event); - }, + /** + `{{textarea}}` inserts a new instance of ` + ``` - Uses sendAction to send the `key-up` action passing the current value - and event as parameters. + Bound: - @method keyUp - @param {Event} event - */ - keyUp: function(event) { - this.interpretKeyEvents(event); + 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. - this.sendAction('key-up', get(this, 'value'), event); - }, + ```javascript + App.ApplicationController = Ember.Controller.extend({ + writtenWords: "Lots of text that IS bound" + }); + ``` - /** - Called when the browser triggers a `keydown` event on the element. + ```handlebars + {{textarea value=writtenWords}} + ``` - Uses sendAction to send the `key-down` action passing the current value - and event as parameters. Note that generally in key-down the value is unchanged - (as the key pressing has not completed yet). + Would result in the following HTML: - @method keyDown - @param {Event} event - */ - keyDown: function(event) { - this.sendAction('key-down', get(this, 'value'), event); - } - }); + ```html + + ``` - TextSupport.KEY_EVENTS = { - 13: 'insertNewline', - 27: 'cancel' - }; + 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`: - // 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'); + ```javascript + App.ApplicationController = Ember.Controller.extend({ + writtenWords: "Lots of text that IS bound", + outputWrittenWords: Ember.computed.oneWay("writtenWords") + }); + ``` - // 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); - } + ```handlebars + {{textarea value=writtenWords}} - view.sendAction(eventName, value); +
+ {{outputWrittenWords}} +
+ ``` - if (action || on === eventName) { - if(!get(view, 'bubbles')) { - event.stopPropagation(); - } - } - } + Would result in the following HTML: - __exports__["default"] = TextSupport; - }); -enifed("ember-handlebars/ext", - ["ember-metal/core","ember-runtime/system/string","ember-handlebars-compiler","ember-metal/property_get","ember-metal/error","ember-metal/mixin","ember-views/views/view","ember-metal/path_cache","ember-metal/streams/stream","ember-metal/streams/read","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // Ember.FEATURES, Ember.assert, Ember.Handlebars, Ember.lookup - // var emberAssert = Ember.assert; + ```html + - var fmt = __dependency2__.fmt; + <-- the following div will be updated in real time as you type --> - var EmberHandlebars = __dependency3__["default"]; +
+ Lots of text that IS bound +
+ ``` - var get = __dependency4__.get; - var EmberError = __dependency5__["default"]; - var IS_BINDING = __dependency6__.IS_BINDING; + 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 View = __dependency7__["default"]; - var detectIsGlobal = __dependency8__.isGlobal; + ```javascript + App.ApplicationController = Ember.Controller.extend({ + writtenWords: "Lots of text that IS bound", + twoWayWrittenWords: Ember.computed.alias("writtenWords") + }); + ``` - // late bound via requireModule because of circular dependencies. - var resolveHelper, SimpleHandlebarsView; + ```handlebars + {{textarea value=writtenWords}} + {{textarea value=twoWayWrittenWords}} + ``` - var Stream = __dependency9__["default"]; - var readArray = __dependency10__.readArray; - var readHash = __dependency10__.readHash; + ```html + - var slice = [].slice; + <-- both updated in real time --> - /** - Lookup both on root and on window. If the path starts with - a keyword, the corresponding object will be looked up in the - template's data hash and used to resolve the path. + + ``` - @method get - @for Ember.Handlebars - @param {Object} root The object to look up the property on - @param {String} path The path to be lookedup - @param {Object} options The template's option hash - @deprecated - */ - function handlebarsGet(root, path, options) { - Ember.deprecate('Usage of Ember.Handlebars.get is deprecated, use a Component or Ember.Handlebars.makeBoundHelper instead.'); + ## Actions - return options.data.view.getStream(path).value(); - } + The helper can send multiple actions based on user events. - /** - handlebarsGetView resolves a view based on strings passed into a template. - For example: + The action property defines the action which is send when + the user presses the return key. ```handlebars - {{view "some-view"}} - {{view view.someView}} - {{view App.SomeView}} {{! deprecated }} + {{input action="submit"}} ``` - A value is first checked to be a string- non-strings are presumed to be - an object and returned. This handles the "access a view on a context" - case (line 2 in the above examples). - - Next a string is normalized, then called on the context with `get`. If - there is still no value, a GlobalPath will be fetched from the global - context (raising a deprecation) and a localPath will be passed to the - container to be looked up. - - @private - @for Ember.Handlebars - @param {Object} context The context of the template being rendered - @param {String} path The path to be lookedup - @param {Object} container The container - @param {Object} data The template's data hash - */ - function handlebarsGetView(context, path, container, data) { - var viewClass; - if ('string' === typeof path) { - if (!data) { - throw new Error("handlebarsGetView: must pass data"); - } - - // Only lookup view class on context if there is a context. If not, - // the global lookup path on get may kick in. - var lazyValue = data.view.getStream(path); - viewClass = lazyValue.value(); - var isGlobal = detectIsGlobal(path); + The helper allows some user events to send actions. - if (!viewClass && !isGlobal) { - Ember.assert("View requires a container to resolve views not passed in through the context", !!container); - viewClass = container.lookupFactory('view:'+path); - } - if (!viewClass && isGlobal) { - var globalViewClass = get(path); - Ember.deprecate('Resolved the view "'+path+'" 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', !globalViewClass); - if (globalViewClass) { - viewClass = globalViewClass; - } - } - } else { - viewClass = path; - } + * `enter` + * `insert-newline` + * `escape-press` + * `focus-in` + * `focus-out` + * `key-press` - // Sometimes a view's value is yet another path - if ('string' === typeof viewClass && data && data.view) { - viewClass = handlebarsGetView(data.view, viewClass, container, data); - } + 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. - Ember.assert( - fmt(path+" must be a subclass or an instance of Ember.View, not %@", [viewClass]), - View.detect(viewClass) || View.detectInstance(viewClass) - ); + ```handlebars + {{textarea focus-in="alertMessage"}} + ``` - return viewClass; - } + See more about [Text Support Actions](/api/classes/Ember.TextArea.html) - function stringifyValue(value, shouldEscape) { - if (value === null || value === undefined) { - value = ""; - } else if (!(value instanceof Handlebars.SafeString)) { - value = String(value); - } + ## Extension - if (shouldEscape) { - value = Handlebars.Utils.escapeExpression(value); - } + 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: - return value; - } + ```javascript + Ember.TextArea.reopen({ + attributeBindings: ['data-error'] + }); + ``` - __exports__.stringifyValue = stringifyValue;/** - Registers a helper in Handlebars that will be called if no property with the - given name can be found on the current context object, and no helper with - that name is registered. + 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. - This throws an exception with a more helpful error message so the user can - track down where the problem is happening. + See more about [Ember components](/api/classes/Ember.Component.html) - @private - @method helperMissing + @method textarea @for Ember.Handlebars.helpers - @param {String} path @param {Hash} options */ - function helperMissingHelper(path) { - if (!resolveHelper) { - resolveHelper = requireModule('ember-handlebars/helpers/binding')['resolveHelper']; - } // ES6TODO: stupid circular dep - - var error, fmtError, view = ""; - - var options = arguments[arguments.length - 1]; - - var helper = resolveHelper(options.data.view.container, options.name); - - if (helper) { - return helper.apply(this, arguments); - } - - if (options.data) { - view = options.data.view; - } - - if (options.name.match(/-/)) { - error = "%@ Handlebars error: Could not find component or helper named '%@'"; - fmtError = fmt(error, [view, options.name]); - } else { - error = "%@ Handlebars error: Could not find property '%@' on object %@."; - fmtError = fmt(error, [view, options.name, this]); - } - - throw new EmberError(fmtError); - } + function textareaHelper(params, hash, options, env) { + Ember.assert('You can only pass attributes to the `textarea` helper, not arguments', params.length === 0); - __exports__.helperMissingHelper = helperMissingHelper;/** - @private - @method blockHelperMissingHelper - @for Ember.Handlebars.helpers - */ - function blockHelperMissingHelper() { - return; + return env.helpers.view.helperFunction.call(this, [TextArea], hash, options, env); } - __exports__.blockHelperMissingHelper = blockHelperMissingHelper;/** - 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. - - ## Simple example + __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"]; - ```javascript - Ember.Handlebars.registerBoundHelper('capitalize', function(value) { - return Ember.String.capitalize(value); - }); - ``` + /** + @module ember + @submodule ember-htmlbars + */ - The above bound helper can be used inside of templates as follows: + /** + `unbound` allows you to output a property without binding. *Important:* The + output will not be updated if the property changes. Use with caution. ```handlebars - {{capitalize name}} - ``` - - 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. - - ## Example with options - - Like normal handlebars helpers, bound helpers have access to the options - passed into the helper call. - - ```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(''); - }); +
{{unbound somePropertyThatDoesntChange}}
``` - This helper could be used in a template as follows: + `unbound` can also be used in conjunction with a bound helper to + render it in its unbound form: ```handlebars - {{repeat text count=3}} +
{{unbound helperName somePropertyThatDoesntChange}}
``` - ## Example with bound options + @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; - Bound hash options are also supported. Example: + options.helperName = options.helperName || 'unbound'; - ```handlebars - {{repeat text count=numRepeats}} - ``` + if (length === 1) { + result = read(params[0]); + } else if (length >= 2) { + env.data.isUnbound = true; - 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 helperName = params[0]._label; + var args = []; - ## Example with extra dependencies + for (var i = 1, l = params.length; i < l; i++) { + var value = read(params[i]); - 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. + args.push(value); + } - ```javascript - Ember.Handlebars.registerBoundHelper('capitalizeName', function(value) { - return value.get('name').toUpperCase(); - }, 'name'); - ``` + var helper = lookupHelper(helperName, this, env); - ## Example with multiple bound properties + if (!helper) { + throw new EmberError('HTMLBars error: Could not find component or helper named ' + helperName + '.'); + } - `Ember.Handlebars.registerBoundHelper` supports binding to - multiple properties, e.g.: + result = helper.helperFunction.call(this, args, hash, options, env); - ```javascript - Ember.Handlebars.registerBoundHelper('concatenate', function() { - var values = Array.prototype.slice.call(arguments, 0, -1); - return values.join('||'); - }); - ``` + delete env.data.isUnbound; + } - 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. + return result; + } - ## Use with unbound helper + __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 + */ - The `{{unbound}}` helper can be used with bound helper invocations - to render them in their unbound form, e.g. + var Ember = __dependency1__["default"]; + // Ember.warn, Ember.assert + 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"]; - ```handlebars - {{unbound capitalize name}} - ``` + var map = __dependency10__.map; + var streamifyClassNameBinding = __dependency11__.streamifyClassNameBinding; - In this example, if the name property changes, the helper - will not re-render. + function makeBindings(hash, options, view) { + for (var prop in hash) { + var value = hash[prop]; - ## Use with blocks not supported + // Classes are processed separately + if (prop === 'class' && isStream(value)) { + hash.classBinding = value._label; + delete hash['class']; + continue; + } - Bound helpers do not support use with Handlebars blocks or - the addition of child views of any kind. + if (prop === 'classBinding') { + continue; + } - @method registerBoundHelper - @for Ember.Handlebars - @param {String} name - @param {Function} function - @param {String} dependentKeys* - */ - function registerBoundHelper(name, fn) { - var boundHelperArgs = slice.call(arguments, 1); - var boundFn = makeBoundHelper.apply(this, boundHelperArgs); - EmberHandlebars.registerHelper(name, boundFn); + 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); + } + } else { + if (isStream(value) && prop !== 'id') { + hash[prop + 'Binding'] = view._getBindingForStream(value); + delete hash[prop]; + } + } + } } - __exports__.registerBoundHelper = registerBoundHelper;/** - A helper function used by `registerBoundHelper`. Takes the - provided Handlebars helper function fn and returns it in wrapped - bound helper form. + var ViewHelper = EmberObject.create({ + propertiesFromHTMLOptions: function(hash, options, env) { + var view = env.data.view; + var classes = read(hash['class']); - The main use case for using this outside of `registerBoundHelper` - is for registering helpers on the container: + var extensions = { + helperName: options.helperName || '' + }; - ```js - var boundHelperFn = Ember.Handlebars.makeBoundHelper(function(word) { - return word.toUpperCase(); - }); + if (hash.id) { + extensions.elementId = read(hash.id); + } - container.register('helper:my-bound-helper', boundHelperFn); - ``` + if (hash.tag) { + extensions.tagName = hash.tag; + } - In the above example, if the helper function hadn't been wrapped in - `makeBoundHelper`, the registered helper would be unbound. + if (classes) { + classes = classes.split(' '); + extensions.classNames = classes; + } - @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 + if (hash.classBinding) { + extensions.classNameBindings = hash.classBinding.split(' '); + } - var dependentKeys = []; - for (var i = 1; i < arguments.length; i++) { - dependentKeys.push(arguments[i]); - } + if (hash.classNameBindings) { + if (extensions.classNameBindings === undefined) { + extensions.classNameBindings = []; + } + extensions.classNameBindings = extensions.classNameBindings.concat(hash.classNameBindings.split(' ')); + } - function helper() { - var numParams = arguments.length - 1; - var options = arguments[numParams]; - var data = options.data; - var view = data.view; - var types = options.types; - var hash = options.hash; - var hashTypes = options.hashTypes; - var context = this; + 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; + } - Ember.assert("registerBoundHelper-generated helpers do not support use with Handlebars blocks.", !options.fn); + // 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 properties = new Array(numParams); - var params = new Array(numParams); + var hashKeys = keys(hash); - for (var i = 0; i < numParams; i++) { - properties[i] = arguments[i]; - if (types[i] === 'ID') { - params[i] = view.getStream(arguments[i]); - } else { - params[i] = arguments[i]; - } - } + for (var i = 0, l = hashKeys.length; i < l; i++) { + var prop = hashKeys[i]; - for (var prop in hash) { - if (IS_BINDING.test(prop)) { - hash[prop.slice(0, -7)] = view.getStream(hash[prop]); - hash[prop] = undefined; - } else if (hashTypes[prop] === 'ID') { - hash[prop] = view.getStream(hash[prop]); + if (prop !== 'classNameBindings') { + extensions[prop] = hash[prop]; } } - var valueFn = function() { - var args = readArray(params); - args.push({ - hash: readHash(hash), - data: { properties: properties } + 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 fn.apply(context, args); - }; + } - if (data.isUnbound) { - return valueFn(); - } else { - var lazyValue = new Stream(valueFn); - var bindView = new SimpleHandlebarsView(lazyValue, !options.hash.unescaped); - view.appendChild(bindView); + return extensions; + }, - var scheduledRerender = view._wrapAsScheduled(bindView.rerender); - lazyValue.subscribe(scheduledRerender, bindView); + helper: function(newView, hash, options, env) { + var data = env.data; + var template = options.template; + var newViewProto; - var param; + makeBindings(hash, options, env.data.view); - for (i = 0; i < numParams; i++) { - param = params[i]; - if (param && param.isStream) { - param.subscribe(lazyValue.notify, lazyValue); - } - } + var viewOptions = this.propertiesFromHTMLOptions(hash, options, env); + var currentView = data.view; - for (prop in hash) { - param = hash[prop]; - if (param && param.isStream) { - param.subscribe(lazyValue.notify, lazyValue); - } - } + if (View.detectInstance(newView)) { + newViewProto = newView; + } else { + newViewProto = newView.proto(); + } - if (numParams > 0) { - var firstParam = params[0]; - // Only bother with subscriptions if the first argument - // is a stream itself, and not a primitive. - if (firstParam && firstParam.isStream) { - 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); - } - } - } + 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; } - } - return helper; - } + // 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?! + } - __exports__.makeBoundHelper = makeBoundHelper; - __exports__.handlebarsGetView = handlebarsGetView; - __exports__.handlebarsGet = handlebarsGet; - }); -enifed("ember-handlebars/helpers/bind_attr", - ["ember-metal/core","ember-handlebars-compiler","ember-metal/utils","ember-runtime/system/string","ember-metal/array","ember-views/views/view","ember-metal/keys","ember-views/system/sanitize_attribute_value","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-handlebars - */ + viewOptions._morph = options.morph; - var Ember = __dependency1__["default"]; - // Ember.assert - var EmberHandlebars = __dependency2__["default"]; + currentView.appendChild(newView, viewOptions); + }, - var uuid = __dependency3__.uuid; - var fmt = __dependency4__.fmt; - var typeOf = __dependency3__.typeOf; - var forEach = __dependency5__.forEach; - var View = __dependency6__["default"]; - var keys = __dependency7__["default"]; + instanceHelper: function(newView, hash, options, env) { + var data = env.data; + var template = options.template; - var sanitizeAttributeValue = __dependency8__["default"]; + makeBindings(hash, options, env.data.view); - var helpers = EmberHandlebars.helpers; - var SafeString = EmberHandlebars.SafeString; + Ember.assert( + 'Only a instance of a view may be passed to the ViewHelper.instanceHelper', + View.detectInstance(newView) + ); - /** - `bind-attr` allows you to create a binding between DOM element attributes and - Ember objects. For example: + var viewOptions = this.propertiesFromHTMLOptions(hash, options, env); + var currentView = data.view; - ```handlebars - imageTitle - ``` + 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; + } - 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"`. + // 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?! + } - If the rendering context of this template is the following object: + viewOptions._morph = options.morph; - ```javascript - { - imageUrl: 'http://lolcats.info/haz-a-funny', - imageTitle: 'A humorous image of a cat' + currentView.appendChild(newView, viewOptions); } - ``` - - The resulting HTML output will be: - - ```html - A humorous image of a cat - ``` + }); + __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. - `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: + An empty `` and the following template: ```handlebars - imageTitle + A span: + {{#view tagName="span"}} + hello. + {{/view}} ``` - ### `bind-attr` and the `class` attribute + Will result in HTML structure: - `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: + ```html + + - * a string return value of an object's property. - * a boolean return value of an object's property - * a hard-coded value +
+ A span: + + Hello. + +
+ + ``` - 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: + ### `parentView` setting + + 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. ```javascript - AView = View.extend({ - someProperty: function() { - return "aValue"; - }.property() - }) - ``` + aView = Ember.View.create({ + template: Ember.Handlebars.compile("{{#view}} my parent: {{parentView.elementId}} {{/view}}") + }); - ```handlebars - +
+
+ my parent: ember1 +
+
``` - 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`. - - A class name is provided via the syntax - `somePropertyName:class-name-if-true`. + ### Setting CSS id and class attributes - ```javascript - AView = View.extend({ - someBool: true - }) - ``` + 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`. ```handlebars - + {{#view tagName="span" id="a-custom-id"}} + hello. + {{/view}} ``` - Result in the following rendered output: + Results in the following HTML structure: ```html - - ``` - - 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: - - ```handlebars - +
+ + hello. + +
``` - A hard-coded value can be used by prepending `:` to the desired - class name: `:class-name-to-always-apply`. + 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: ```handlebars - + {{#view tagName="span" class="a-custom-class"}} + hello. + {{/view}} ``` - Results in the following rendered output: + Results in the following HTML structure: ```html - +
+ + hello. + +
``` - All three strategies - string return value, boolean return value, and - hard-coded value – can be combined in a single declaration: + ### Supplying a different view class + + `{{view}}` can take an optional first argument before its supplied options to + specify a path to a custom view class. ```handlebars - + {{#view "custom"}}{{! will look up App.CustomView }} + hello. + {{/view}} ``` - @method bind-attr - @for Ember.Handlebars.helpers - @param {Hash} options - @return {String} HTML string - */ - function bindAttrHelper(options) { - var attrs = options.hash; - - Ember.assert("You must specify at least one hash argument to bind-attr", !!keys(attrs).length); + The first argument can also be a relative path accessible from the current + context. - var view = options.data.view; - var ret = []; + ```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}}') + }); - // 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; + MyApp.OuterView.create().appendTo('body'); + ``` - // 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 = uuid(); + Will result in the following HTML: - // Handle classes differently, as we can bind multiple classes - var classBindings = attrs['class']; - if (classBindings != null) { - var classResults = bindClasses(ctx, classBindings, view, dataId, options); + ```html +
+
+ hi +
+
+ ``` - ret.push('class="' + Handlebars.Utils.escapeExpression(classResults.join(' ')) + '"'); - delete attrs['class']; - } + ### Blockless use - var attrKeys = keys(attrs); + 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. - // 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]; + ```javascript + var App = Ember.Application.create(); + App.WithTemplateDefinedView = Ember.View.extend({ + templateName: 'defined-template' + }); + ``` - Ember.assert(fmt("You must provide an expression as the value of bound attribute." + - " You specified: %@=%@", [attr, path]), typeof path === 'string'); + ```handlebars + {{! application.hbs }} + {{view 'with-template-defined'}} + ``` - var lazyValue = view.getStream(path); - var value = lazyValue.value(); - value = sanitizeAttributeValue(null, attr, value); - var type = typeOf(value); + ```handlebars + {{! defined-template.hbs }} + Some content for the defined template view. + ``` - Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [value]), - value === null || value === undefined || type === 'number' || type === 'string' || type === 'boolean'); + ### `viewName` property - lazyValue.subscribe(view._wrapAsScheduled(function applyAttributeBindings() { - var result = lazyValue.value(); + 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. - 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'); + ```javascript + aView = Ember.View.create({ + template: Ember.Handlebars.compile('{{#view viewName="aChildByName"}} hi {{/view}}') + }); - var elem = view.$("[data-bindattr-" + dataId + "='" + dataId + "']"); + aView.appendTo('body'); + aView.get('aChildByName') // the instance of Ember.View created by {{view}} helper + ``` - Ember.assert("An attribute binding was triggered when the element was not in the DOM", elem && elem.length !== 0); + @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); - View.applyAttributeBindings(elem, attr, result); - })); + var container = this.container || read(this._keywords.view).container; + var viewClass; - // 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 + '"'); + if (params.length === 0) { + if (container) { + viewClass = container.lookupFactory('view:toplevel'); + } else { + viewClass = View; } - }, this); + } else { + var pathStream = params[0]; + viewClass = readViewFactory(pathStream, container); + } + + options.helperName = options.helperName || 'view'; - // 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(' ')); + return ViewHelper.helper(viewClass, hash, options, env); } + __exports__.viewHelper = viewHelper; + }); +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"; /** - See `bind-attr` - - @method bindAttr - @for Ember.Handlebars.helpers - @deprecated - @param {Function} context - @param {Hash} options - @return {String} HTML string + @module ember + @submodule ember-htmlbars */ - function bindAttrHelperDeprecated() { - Ember.deprecate("The 'bindAttr' view helper is deprecated in favor of 'bind-attr'"); - return helpers['bind-attr'].apply(this, arguments); - } + var Ember = __dependency1__["default"]; + // Ember.assert + var isNone = __dependency2__["default"]; + var bind = __dependency3__.bind; + var WithView = __dependency4__["default"]; /** - 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. + 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. - 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. + ```handlebars + // posts might not be + {{#with user.posts as blogPosts}} +
+ There are {{blogPosts.length}} blog posts written by {{user.name}}. +
- @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 = []; - var newClass, value, elem; - - // For each property passed, loop through and setup - // an observer. - forEach.call(classBindings.split(' '), function(binding) { - - // 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; - var parsedPath = View._parsePropertyPath(binding); - var path = parsedPath.path; - var initialValue; - - if (path === '') { - initialValue = true; - } else { - var lazyValue = view.getStream(path); - initialValue = lazyValue.value(); + {{#each post in blogPosts}} +
  • {{post.title}}
  • + {{/each}} + {{/with}} + ``` - // Set up an observer on the context. If the property changes, toggle the - // class name. - lazyValue.subscribe(view._wrapAsScheduled(function applyClassNameBindings() { - // Get the current value of the property - var value = lazyValue.value(); - newClass = classStringForParsedPath(parsedPath, value); - elem = bindAttrId ? view.$("[data-bindattr-" + bindAttrId + "='" + bindAttrId + "']") : view.$(); + Without the `as` operator, it would be impossible to reference `user.name` in the example above. - Ember.assert("A class name binding was triggered when the element was not in the DOM", elem && elem.length !== 0); + NOTE: The alias should not reuse a name from the bound property path. + For example: `{{#with foo.bar as foo}}` is not supported because it attempts to alias using + the first part of the property path, `foo`. Instead, use `{{#with foo.bar as baz}}`. - // If we had previously added a class to the element, remove it. - if (oldClass) { - elem.removeClass(oldClass); - } + ### `controller` option - // 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; - } - })); - } + Adding `controller='something'` instructs the `{{with}}` helper to create and use an instance of + the specified controller wrapping the aliased keyword. - // We've already setup the observer; now we just need to figure out the - // correct behavior right now on the first pass through. - value = classStringForParsedPath(parsedPath, initialValue); + This is very similar to using an `itemController` option with the `{{each}}` helper. - if (value) { - ret.push(value); + ```handlebars + {{#with users.posts as posts controller='userBlogPosts'}} + {{!- `posts` is wrapped in our controller instance }} + {{/with}} + ``` - // Make sure we save the current value so that it can be removed if the - // observer fires. - oldClass = value; - } - }); + 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. - return ret; - } + @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 + ); - function classStringForParsedPath(parsedPath, value) { - return View._classStringForValue(parsedPath.path, value, parsedPath.className, parsedPath.falsyClassName); - } + Ember.assert( + "The {{#with}} helper must be called with a block", + !!options.template + ); - __exports__["default"] = bindAttrHelper; + var preserveContext; - __exports__.bindAttrHelper = bindAttrHelper; - __exports__.bindAttrHelperDeprecated = bindAttrHelperDeprecated; - __exports__.bindClasses = bindClasses; + 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.", + false, + { url: 'http://emberjs.com/guides/deprecations/#toc_more-consistent-handlebars-scope' } + ); + preserveContext = false; + } + + bind.call(this, params[0], hash, options, env, preserveContext, exists, undefined, undefined, WithView); + } + + __exports__.withHelper = withHelper;function exists(value) { + return !isNone(value); + } }); -enifed("ember-handlebars/helpers/binding", - ["ember-metal/core","ember-handlebars-compiler","ember-metal/is_none","ember-metal/run_loop","ember-metal/cache","ember-metal/streams/simple","ember-handlebars/views/handlebars_bound_view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { +enifed("ember-htmlbars/helpers/yield", + ["ember-metal/core","ember-metal/property_get","exports"], + function(__dependency1__, __dependency2__, __exports__) { "use strict"; /** @module ember - @submodule ember-handlebars + @submodule ember-htmlbars */ var Ember = __dependency1__["default"]; - // Ember.assert - var EmberHandlebars = __dependency2__["default"]; - - var isNone = __dependency3__["default"]; - var run = __dependency4__["default"]; - var Cache = __dependency5__["default"]; - var SimpleStream = __dependency6__["default"]; - - var _HandlebarsBoundView = __dependency7__._HandlebarsBoundView; - var SimpleHandlebarsView = __dependency7__.SimpleHandlebarsView; - var helpers = EmberHandlebars.helpers; - - function exists(value) { - return !isNone(value); - } - - // Binds a property into the DOM. This will create a hook in DOM that the - // KVO system will look for and update if the property changes. - function bind(property, options, preserveContext, shouldDisplay, valueNormalizer, childProperties, _viewClass) { - var data = options.data; - var view = data.view; + var get = __dependency2__.get; - // 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 currentContext = this || window; + /** + `{{yield}}` denotes an area of a template that will be rendered inside + of another template. It has two main uses: - var valueStream = view.getStream(property); - var lazyValue; + ### 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. - if (childProperties) { - lazyValue = new SimpleStream(valueStream); + An empty `` and the following application code: - var subscriber = function(childStream) { - childStream.value(); - lazyValue.notify(); - }; + ```javascript + AView = Ember.View.extend({ + classNames: ['a-view-with-layout'], + layout: Ember.Handlebars.compile('
    {{yield}}
    '), + template: Ember.Handlebars.compile('I am wrapped') + }); - for (var i = 0; i < childProperties.length; i++) { - var childStream = valueStream.get(childProperties[i]); - childStream.value(); - childStream.subscribe(subscriber); - } - } else { - lazyValue = valueStream; - } + aView = AView.create(); + aView.appendTo('body'); + ``` - // Set up observers for observable objects - var viewClass = _viewClass || _HandlebarsBoundView; - var viewOptions = { - preserveContext: preserveContext, - shouldDisplayFunc: shouldDisplay, - valueNormalizerFunc: valueNormalizer, - displayTemplate: options.fn, - inverseTemplate: options.inverse, - lazyValue: lazyValue, - previousContext: currentContext, - isEscaped: !options.hash.unescaped, - templateData: options.data, - templateHash: options.hash, - helperName: options.helperName - }; + Will result in the following HTML output: - if (options.keywords) { - viewOptions._keywords = options.keywords; - } + ```html + +
    +
    + I am wrapped +
    +
    + + ``` - // 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._HandlebarsBoundView for more. - var bindView = view.createChildView(viewClass, viewOptions); + 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. - view.appendChild(bindView); + ```javascript + BView = Ember.View.extend({ + classNames: ['a-view-with-layout'], + template: Ember.Handlebars.compile('{{yield}}') + }); - lazyValue.subscribe(view._wrapAsScheduled(function() { - run.scheduleOnce('render', bindView, 'rerenderIfNeeded'); - })); - } + bView = BView.create(); + bView.appendTo('body'); - function simpleBind(currentContext, lazyValue, options) { - var data = options.data; - var view = data.view; + // throws + // Uncaught Error: assertion failed: + // You called yield in a template that was not a layout + ``` - var bindView = new SimpleHandlebarsView( - lazyValue, !options.hash.unescaped - ); + ### 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: - bindView._parentView = view; - view.appendChild(bindView); + ```handlebars + + {{#labeled-textfield value=someProperty}} + First name: + {{/labeled-textfield}} + ``` - lazyValue.subscribe(view._wrapAsScheduled(function() { - run.scheduleOnce('render', bindView, 'rerender'); - })); - } + ```handlebars + + + ``` - /** - '_triageMustache' is used internally select between a binding, helper, or component for - the given context. Until this point, it would be hard to determine if the - mustache is a property reference or a regular helper reference. This triage - helper resolves that. + Result: - This would not be typically invoked by directly. + ```html + + ``` - @private - @method _triageMustache + @method yield @for Ember.Handlebars.helpers - @param {String} property Property/helperID to triage - @param {Object} options hash of template/rendering options + @param {Hash} options @return {String} HTML string */ - function _triageMustacheHelper(property, options) { - Ember.assert("You cannot pass more than one argument to the _triageMustache helper", arguments.length <= 2); + function yieldHelper(params, hash, options, env) { + var view = this; - var helper = EmberHandlebars.resolveHelper(options.data.view.container, property); - if (helper) { - return helper.call(this, options); + // Yea gods + while (view && !get(view, 'layout')) { + if (view._contextView) { + view = view._contextView; + } else { + view = get(view, '_parentView'); + } } - return helpers.bind.call(this, property, options); + Ember.assert("You called yield in a template that was not a layout", !!view); + + return view._yield(null, env, options.morph, params); } - var ISNT_HELPER_CACHE = new Cache(1000, function(key) { - return key.indexOf('-') === -1; - }); - __exports__.ISNT_HELPER_CACHE = ISNT_HELPER_CACHE; + __exports__.yieldHelper = yieldHelper; + }); +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"; /** - Used to lookup/resolve handlebars helpers. The lookup order is: - - * Look for a registered helper - * If a dash exists in the name: - * Look for a helper registed in the container - * Use Ember.ComponentLookup to find an Ember.Component that resolves - to the given name - - @private - @method resolveHelper - @param {Container} container - @param {String} name the name of the helper to lookup - @return {Handlebars Helper} + @module ember + @submodule ember-htmlbars */ - function resolveHelper(container, name) { - if (helpers[name]) { - return helpers[name]; - } - if (!container || ISNT_HELPER_CACHE.get(name)) { - return; - } + var AttrNode = __dependency1__["default"]; + var EmberError = __dependency2__["default"]; + var isStream = __dependency3__.isStream; + var sanitizeAttributeValue = __dependency4__["default"]; - var helper = container.lookup('helper:' + name); - if (!helper) { - var componentLookup = container.lookup('component-lookup:main'); - Ember.assert("Could not find 'component-lookup:main' on the provided container," + - " which is necessary for performing component lookups", componentLookup); + var boundAttributesEnabled = false; - var Component = componentLookup.lookupFactory(name, container); - if (Component) { - helper = EmberHandlebars.makeViewHelper(Component); - container.register('helper:' + name, helper); + + __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); } } - return helper; } + }); +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 + */ + var appendSimpleBoundView = __dependency1__.appendSimpleBoundView; + var isStream = __dependency2__.isStream; + var lookupHelper = __dependency3__["default"]; - /** - `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`: + __exports__["default"] = function block(env, morph, view, path, params, hash, template, inverse) { + var helper = lookupHelper(path, view, env); - ```handlebars - {{bind "content.title"}} - ``` + Ember.assert("A helper named `"+path+"` could not be found", helper); - 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. + var options = { + morph: morph, + template: template, + inverse: inverse, + isBlock: true + }; + var result = helper.helperFunction.call(view, params, hash, options, env); - @private - @method bind - @for Ember.Handlebars.helpers - @param {String} property Property to bind - @param {Function} fn Context to provide for rendering - @return {String} HTML string + if (isStream(result)) { + appendSimpleBoundView(view, morph, result); + } else { + morph.setContent(result); + } + } + }); +enifed("ember-htmlbars/hooks/component", + ["ember-metal/core","ember-htmlbars/system/lookup-helper","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-htmlbars */ - function bindHelper(property, options) { - Ember.assert("You cannot pass more than one argument to the bind helper", arguments.length <= 2); - var context = (options.contexts && options.contexts.length) ? options.contexts[0] : this; + var Ember = __dependency1__["default"]; + var lookupHelper = __dependency2__["default"]; - if (!options.fn) { - var lazyValue = options.data.view.getStream(property); - return simpleBind(context, lazyValue, options); - } + __exports__["default"] = function component(env, morph, view, tagName, attrs, template) { + var helper = lookupHelper(tagName, view, env); - options.helperName = 'bind'; + Ember.assert('You specified `' + tagName + '` in your template, but a component for `' + tagName + '` could not be found.', !!helper); - return bind.call(context, property, options, false, exists); + return helper.helperFunction.call(view, [], attrs, {morph: morph, template: template}, env); } - - __exports__.bind = bind; - __exports__._triageMustacheHelper = _triageMustacheHelper; - __exports__.resolveHelper = resolveHelper; - __exports__.bindHelper = bindHelper; }); -enifed("ember-handlebars/helpers/collection", - ["ember-metal/core","ember-handlebars-compiler","ember-metal/mixin","ember-runtime/system/string","ember-metal/property_get","ember-metal/streams/simple","ember-handlebars/ext","ember-handlebars/helpers/view","ember-views/views/view","ember-views/views/collection_view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __exports__) { +enifed("ember-htmlbars/hooks/concat", + ["ember-metal/streams/utils","exports"], + function(__dependency1__, __exports__) { "use strict"; /** @module ember - @submodule ember-handlebars + @submodule ember-htmlbars */ - var Ember = __dependency1__["default"]; - // Ember.assert, Ember.deprecate + var streamConcat = __dependency1__.concat; - // var emberAssert = Ember.assert; - // emberDeprecate = Ember.deprecate; + __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 + */ - var EmberHandlebars = __dependency2__["default"]; + var appendSimpleBoundView = __dependency1__.appendSimpleBoundView; + var isStream = __dependency2__.isStream; + var lookupHelper = __dependency3__["default"]; - var IS_BINDING = __dependency3__.IS_BINDING; - var fmt = __dependency4__.fmt; - var get = __dependency5__.get; - var SimpleStream = __dependency6__["default"]; - var handlebarsGetView = __dependency7__.handlebarsGetView; - var ViewHelper = __dependency8__.ViewHelper; - var View = __dependency9__["default"]; - var CollectionView = __dependency10__["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); + } + if (isStream(result)) { + appendSimpleBoundView(view, morph, result); + } else { + morph.setContent(result); + } + } + }); +enifed("ember-htmlbars/hooks/element", + ["ember-metal/core","ember-metal/streams/utils","ember-htmlbars/system/lookup-helper","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; /** - `{{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. + @module ember + @submodule ember-htmlbars + */ - `{{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 Ember = __dependency1__["default"]; + var read = __dependency2__.read; + var lookupHelper = __dependency3__["default"]; - The provided block will be applied as the template for each item's view. + __exports__["default"] = function element(env, domElement, view, path, params, hash) { //jshint ignore:line + var helper = lookupHelper(path, view, env); + var valueOrLazyValue; - Given an empty `` the following template: + if (helper) { + var options = { + element: domElement + }; + valueOrLazyValue = helper.helperFunction.call(view, params, hash, options, env); + } else { + valueOrLazyValue = view.getStream(path); + } - ```handlebars - {{! application.hbs }} - {{#collection content=model}} - Hi {{view.content.name}} - {{/collection}} - ``` + var value = read(valueOrLazyValue); + if (value) { + Ember.deprecate('Returning a string of attributes from a helper inside an element is deprecated.'); - And the following application code + var parts = value.toString().split(/\s+/); + for (var i = 0, l = parts.length; i < l; i++) { + var attrParts = parts[i].split('='); + var attrName = attrParts[0]; + var attrValue = attrParts[1]; - ```javascript - App = Ember.Application.create(); - App.ApplicationRoute = Ember.Route.extend({ - model: function(){ - return [{name: 'Yehuda'},{name: 'Tom'},{name: 'Peter'}]; + attrValue = attrValue.replace(/^['"]/, '').replace(/['"]$/, ''); + + env.dom.setAttribute(domElement, attrName, attrValue); } - }); - ``` + } + } + }); +enifed("ember-htmlbars/hooks/get", + ["exports"], + function(__exports__) { + "use strict"; + /** + @module ember + @submodule ember-htmlbars + */ - The following HTML will result: + __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 + */ - ```html -
    -
    Hi Yehuda
    -
    Hi Tom
    -
    Hi Peter
    -
    - ``` + var appendSimpleBoundView = __dependency1__.appendSimpleBoundView; + var isStream = __dependency2__.isStream; + var lookupHelper = __dependency3__["default"]; - ### Non-block version of collection + __exports__["default"] = function inline(env, morph, view, path, params, hash) { + var helper = lookupHelper(path, view, env); - If you provide an `itemViewClass` option that has its own `template` you may - omit the block. + Ember.assert("A helper named '"+path+"' could not be found", helper); - The following template: + var result = helper.helperFunction.call(view, params, hash, {morph: morph}, env); - ```handlebars - {{! application.hbs }} - {{collection content=model itemViewClass="an-item"}} - ``` + if (isStream(result)) { + appendSimpleBoundView(view, morph, result); + } else { + morph.setContent(result); + } + } + }); +enifed("ember-htmlbars/hooks/set", + ["ember-metal/core","ember-metal/error","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-htmlbars + */ - And application code + var Ember = __dependency1__["default"]; + var EmberError = __dependency2__["default"]; - ```javascript - App = Ember.Application.create(); - App.ApplicationRoute = Ember.Route.extend({ - model: function(){ - return [{name: 'Yehuda'},{name: 'Tom'},{name: 'Peter'}]; - } - }); + __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 + */ - App.AnItemView = Ember.View.extend({ - template: Ember.Handlebars.compile("Greetings {{view.content.name}}") - }); - ``` + var lookupHelper = __dependency1__["default"]; - Will result in the HTML structure below + __exports__["default"] = function subexpr(env, view, path, params, hash) { + var helper = lookupHelper(path, view, env); - ```html -
    -
    Greetings Yehuda
    -
    Greetings Tom
    -
    Greetings Peter
    -
    - ``` + Ember.assert("A helper named '"+path+"' could not be found", helper); - ### Specifying a CollectionView subclass + 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 */ - 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: - - ```handlebars - {{#collection "my-custom-collection" content=model}} - Hi {{view.content.name}} - {{/collection}} - ``` - - This example would look for the class `App.MyCustomCollection`. - - ### 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}} - ``` - - Will result in the following HTML structure: + /** + @module ember + @submodule ember-htmlbars + */ - ```html -
    -

    Howdy Yehuda

    -

    Howdy Tom

    -

    Howdy Peter

    -
    - ``` + var Ember = __dependency1__["default"]; + var ComponentLookup = __dependency2__["default"]; + var jQuery = __dependency3__["default"]; + var EmberError = __dependency4__["default"]; + var onLoad = __dependency5__.onLoad; + var htmlbarsCompile = __dependency6__["default"]; - @method collection - @for Ember.Handlebars.helpers - @param {String} path - @param {Hash} options - @return {String} HTML string - @deprecated Use `{{each}}` helper instead. + /** + @module ember + @submodule ember-handlebars */ - 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 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); - } + /** + 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 fn = options.fn, - data = options.data, - inverse = options.inverse, - view = options.data.view, - // This should be deterministic, and should probably come from a - // parent view and not the controller. - container = (view.controller && view.controller.container ? view.controller.container : view.container); + 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. - // If passed a path string, convert that into an object. - // Otherwise, just default to the standard class. - var collectionClass; - if (path) { - collectionClass = handlebarsGetView(this, path, container, options.data); - Ember.assert(fmt("%@ #collection: Could not find collection class %@", [data.view, path]), !!collectionClass); - } - else { - collectionClass = CollectionView; - } + @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"]'; - var hash = options.hash; - var hashTypes = options.hashTypes; - var itemHash = {}; - var match; + jQuery(selectors, ctx) + .each(function() { + // Get a reference to the script tag + var script = jQuery(this); - // Extract item view class if provided else default to the standard class - var collectionPrototype = collectionClass.proto(); - var itemViewClass; + 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()); - if (hash.itemView) { - itemViewClass = hash.itemView; - } else if (hash.itemViewClass) { - if (hashTypes.itemViewClass === 'ID') { - var itemViewClassStream = view.getStream(hash.itemViewClass); - Ember.deprecate('Resolved the view "'+hash.itemViewClass+'" 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', !itemViewClassStream.isGlobal()); - itemViewClass = itemViewClassStream.value(); - } else { - itemViewClass = hash.itemViewClass; + // Check if template of same name already exists + if (Ember.TEMPLATES[templateName] !== undefined) { + throw new EmberError('Template named "' + templateName + '" already exists.'); } - } else { - itemViewClass = collectionPrototype.itemViewClass; - } - if (typeof itemViewClass === 'string') { - itemViewClass = container.lookupFactory('view:'+itemViewClass); - } - - Ember.assert(fmt("%@ #collection: Could not find itemViewClass %@", [data.view, itemViewClass]), !!itemViewClass); + // For templates which have a name, we save them and then remove them from the DOM + Ember.TEMPLATES[templateName] = template; - delete hash.itemViewClass; - delete hash.itemView; - delete hashTypes.itemViewClass; - delete hashTypes.itemView; + // Remove script tag from DOM + script.remove(); + }); + } - // 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]; + function _bootstrap() { + bootstrap( jQuery(document) ); + } - if (hashTypes[prop] === 'ID' || IS_BINDING.test(prop)) { - itemHash[childProp] = view._getBindingForStream(hash[prop]); - } else { - itemHash[childProp] = hash[prop]; - } - delete hash[prop]; - } - } - } + function registerComponentLookup(container) { + container.register('component-lookup:main', ComponentLookup); + } - if (fn) { - itemHash.template = fn; - delete options.fn; - } + /* + We tie this to application.load to ensure that we've at least + attempted to bootstrap at the point that the application is loaded. - 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 = handlebarsGetView(this, hash.emptyViewClass, container, options.data); - } - if (emptyViewClass) { hash.emptyView = emptyViewClass; } + We also tie this to document ready since we're guaranteed that all + the inline templates are present at this point. - if (hash.keyword) { - itemHash._contextBinding = '_parentView.context'; - } else { - itemHash._contextBinding = 'content'; - } + There's no harm to running this twice, since we remove the templates + from the DOM after processing. + */ - var viewOptions = ViewHelper.propertiesFromHTMLOptions({ data: data, hash: itemHash }, this); + onLoad('Ember.Application', function(Application) { + - if (hash.itemClassBinding) { - var itemClassBindings = hash.itemClassBinding.split(' '); + Application.initializer({ + name: 'domTemplates', + initialize: _bootstrap + }); - for (var i = 0; i < itemClassBindings.length; i++) { - var parsedPath = View._parsePropertyPath(itemClassBindings[i]); - if (parsedPath.path === '') { - parsedPath.stream = new SimpleStream(true); - } else { - parsedPath.stream = view.getStream(parsedPath.path); - } - itemClassBindings[i] = parsedPath; - } + Application.initializer({ + name: 'registerComponentLookup', + after: 'domTemplates', + initialize: registerComponentLookup + }); - viewOptions.classNameBindings = itemClassBindings; - } + + }); - hash.itemViewClass = itemViewClass; - hash._itemViewProps = viewOptions; + __exports__["default"] = bootstrap; + }); +enifed("ember-htmlbars/system/helper", + ["exports"], + function(__exports__) { + "use strict"; + /** + @module ember + @submodule ember-htmlbars + */ - options.helperName = options.helperName || 'collection'; + /** + @class Helper + @namespace Ember.HTMLBars + */ + function Helper(helper) { + this.helperFunction = helper; - return EmberHandlebars.helpers.view.call(this, collectionClass, options); + this.isHelper = true; + this.isHTMLBars = true; } - __exports__["default"] = collectionHelper; + __exports__["default"] = Helper; }); -enifed("ember-handlebars/helpers/debug", - ["ember-metal/core","ember-metal/utils","ember-metal/logger","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __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"; - /*jshint debug:true*/ - /** @module ember - @submodule ember-handlebars + @submodule ember-htmlbars */ - var Ember = __dependency1__["default"]; - // Ember.FEATURES, - var inspect = __dependency2__.inspect; - var Logger = __dependency3__["default"]; - var a_slice = [].slice; + var Ember = __dependency1__["default"]; + var Cache = __dependency2__["default"]; + var makeViewHelper = __dependency3__["default"]; + var HandlebarsCompatibleHelper = __dependency4__["default"]; + var ISNT_HELPER_CACHE = new Cache(1000, function(key) { + return key.indexOf('-') === -1; + }); + __exports__.ISNT_HELPER_CACHE = ISNT_HELPER_CACHE; /** - `log` allows you to output the value of variables in the current rendering - context. `log` also accepts primitive types such as strings or numbers. + Used to lookup/resolve handlebars helpers. The lookup order is: - ```handlebars - {{log "myVariable:" myVariable }} - ``` + * 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 - @method log - @for Ember.Handlebars.helpers - @param {String} property + @private + @method resolveHelper + @param {Container} container + @param {String} name the name of the helper to lookup + @return {Handlebars Helper} */ - function logHelper() { - var params = a_slice.call(arguments, 0, -1); - var options = arguments[arguments.length - 1]; - var view = options.data.view; - var logger = Logger.log; - var values = []; + __exports__["default"] = function lookupHelper(name, view, env) { + var helper = env.helpers[name]; + if (helper) { + return helper; + } - for (var i = 0; i < params.length; i++) { - if (options.types[i] === 'ID') { - var stream = view.getStream(params[i]); - values.push(stream.value()); - } else { - values.push(params[i]); - } + var container = view.container; + + if (!container || ISNT_HELPER_CACHE.get(name)) { + return; } - logger.apply(logger, values); - } + 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); - /** - Execute the `debugger` statement in the current context. + var Component = componentLookup.lookupFactory(name, container); + if (Component) { + helper = makeViewHelper(Component); + container.register(helperName, helper); + } + } - ```handlebars - {{debugger}} - ``` + if (helper && !helper.isHTMLBars) { + helper = new HandlebarsCompatibleHelper(helper); + container.unregister(helperName); + container.register(helperName, helper); + } - 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: + return helper; + } + }); +enifed("ember-htmlbars/system/make-view-helper", + ["ember-metal/core","ember-htmlbars/system/helper","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-htmlbars + */ - - 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 Ember = __dependency1__["default"]; + // Ember.assert + var Helper = __dependency2__["default"]; - 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`: + /** + Returns a helper function that renders the provided ViewClass. - ``` - > templateContext.get('foo') // -> "" - ``` + Used internally by Ember.Handlebars.helper and other methods + involving helper/component registration. - @method debugger - @for Ember.Handlebars.helpers - @param {String} property + @private + @method makeViewHelper + @param {Function} ViewClass view class constructor + @since 1.2.0 */ - function debuggerHelper(options) { + __exports__["default"] = function makeViewHelper(ViewClass) { + function helperFunc(params, hash, options, env) { + Ember.assert("You can only pass attributes (such as name=value) not bare " + + "values to a helper for a View found in '" + ViewClass.toString() + "'", params.length === 0); - // These are helpful values you can inspect while debugging. - /* jshint unused: false */ - var templateContext = this; - var typeOfTemplateContext = inspect(templateContext); - Ember.Logger.info('Use `this` to access the context of the calling template.'); + return env.helpers.view.helperFunction.call(this, [ViewClass], hash, options, env); + } - debugger; + return new Helper(helperFunc); } - - __exports__.logHelper = logHelper; - __exports__.debuggerHelper = debuggerHelper; }); -enifed("ember-handlebars/helpers/each", - ["ember-metal/core","ember-handlebars-compiler","ember-runtime/system/string","ember-metal/property_get","ember-metal/property_set","ember-views/views/collection_view","ember-metal/binding","ember-runtime/mixins/controller","ember-runtime/controllers/array_controller","ember-runtime/mixins/array","ember-metal/observer","ember-handlebars/views/metamorph_view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __exports__) { +enifed("ember-htmlbars/system/make_bound_helper", + ["ember-metal/core","ember-htmlbars/system/helper","ember-metal/streams/stream","ember-metal/streams/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; - /** @module ember - @submodule ember-handlebars + @submodule ember-htmlbars */ - var Ember = __dependency1__["default"]; - // Ember.assert;, Ember.K - - var EmberHandlebars = __dependency2__["default"]; - var fmt = __dependency3__.fmt; - var get = __dependency4__.get; - var set = __dependency5__.set; - var CollectionView = __dependency6__["default"]; - var Binding = __dependency7__.Binding; - var ControllerMixin = __dependency8__["default"]; - var ArrayController = __dependency9__["default"]; - var EmberArray = __dependency10__["default"]; + var Ember = __dependency1__["default"]; + // Ember.FEATURES, Ember.assert, Ember.Handlebars, Ember.lookup + var Helper = __dependency2__["default"]; - var addObserver = __dependency11__.addObserver; - var removeObserver = __dependency11__.removeObserver; - var addBeforeObserver = __dependency11__.addBeforeObserver; - var removeBeforeObserver = __dependency11__.removeBeforeObserver; + var Stream = __dependency3__["default"]; + var readArray = __dependency4__.readArray; + var readHash = __dependency4__.readHash; + var subscribe = __dependency4__.subscribe; + var scanHash = __dependency4__.scanHash; + var scanArray = __dependency4__.scanArray; - var _MetamorphView = __dependency12__["default"]; - var _Metamorph = __dependency12__._Metamorph; + /** + Create a bound helper. Accepts a function that receives the ordered and hash parameters + from the template. If a bound property was provided in the template it will be resolved to its + value and any changes to the bound property cause the helper function to be re-ran with the updated + values. - var EachView = CollectionView.extend(_Metamorph, { + * `params` - An array of resolved ordered parameters. + * `hash` - An object containing the hash parameters. - init: function() { - var itemController = get(this, 'itemController'); - var binding; + For example: - 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 - }); + * With an unqouted ordered parameter: - this.disableContentObservers(function() { - set(this, 'content', controller); - binding = new Binding('content', '_eachView.dataSource').oneWay(); - binding.connect(controller); - }); + ```javascript + {{x-capitalize foo}} + ``` - set(this, '_arrayController', controller); - } else { - this.disableContentObservers(function() { - binding = new Binding('content', 'dataSource').oneWay(); - binding.connect(this); - }); - } + Assuming `foo` was set to `"bar"`, the bound helper would receive `["bar"]` as its first argument, and + an empty hash as its second. - return this._super(); - }, + * With a quoted ordered parameter: - _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)); - }, + ```javascript + {{x-capitalize "foo"}} + ``` - disableContentObservers: function(callback) { - removeBeforeObserver(this, 'content', null, '_contentWillChange'); - removeObserver(this, 'content', null, '_contentDidChange'); + The bound helper would receive `["foo"]` as its first argument, and an empty hash as its second. - callback.call(this); + * With an unquoted hash parameter: - addBeforeObserver(this, 'content', null, '_contentWillChange'); - addObserver(this, 'content', null, '_contentDidChange'); - }, + ```javascript + {{x-repeat "foo" count=repeatCount}} + ``` - itemViewClass: _MetamorphView, - emptyViewClass: _MetamorphView, + Assuming that `repeatCount` resolved to 2, the bound helper would receive `["foo"]` as its first argument, + and { count: 2 } as its second. - createChildView: function(view, attrs) { - view = this._super(view, attrs); + @private + @method makeBoundHelper + @for Ember.HTMLBars + @param {Function} function + @since 1.10.0 + */ + __exports__["default"] = function makeBoundHelper(fn) { + function helperFunc(params, hash, options, env) { + var view = this; + var numParams = params.length; + var param, prop; - var content = get(view, 'content'); - var keyword = get(this, 'keyword'); + Ember.assert("makeBoundHelper generated helpers do not support use with blocks", !options.template); - if (keyword) { - view._keywords[keyword] = content; + function valueFn() { + return fn.call(view, readArray(params), readHash(hash), options, env); } - // If {{#each}} is looping over an array of controllers, - // point each child view at their respective controller. - if (content && content.isController) { - set(view, 'controller', content); - } + // 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); - return view; - }, + if (env.data.isUnbound || !hasStream) { + return valueFn(); + } else { + var lazyValue = new Stream(valueFn); - destroy: function() { - if (!this._super()) { return; } + for (var i = 0; i < numParams; i++) { + param = params[i]; + subscribe(param, lazyValue.notify, lazyValue); + } - var arrayController = get(this, '_arrayController'); + for (prop in hash) { + param = hash[prop]; + subscribe(param, lazyValue.notify, lazyValue); + } - if (arrayController) { - arrayController.destroy(); + return lazyValue; } - - return this; } - }); + return new Helper(helperFunc); + } + }); +enifed("ember-htmlbars/templates/component", + ["ember-template-compiler/system/template","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var template = __dependency1__["default"]; + var t = (function() { + return { + isHTMLBars: true, + blockParams: 0, + cachedFragment: null, + hasRendered: false, + build: function build(dom) { + var el0 = dom.createDocumentFragment(); + var el1 = dom.createTextNode(""); + dom.appendChild(el0, el1); + var el1 = dom.createTextNode(""); + dom.appendChild(el0, el1); + return el0; + }, + render: function render(context, env, contextualElement) { + var dom = env.dom; + var hooks = env.hooks, content = hooks.content; + dom.detectNamespace(contextualElement); + var fragment; + if (env.useFragmentCache && dom.canClone) { + if (this.cachedFragment === null) { + fragment = this.build(dom); + if (this.hasRendered) { + this.cachedFragment = fragment; + } else { + this.hasRendered = true; + } + } + if (this.cachedFragment) { + fragment = dom.cloneNode(this.cachedFragment, true); + } + } else { + fragment = this.build(dom); + } + if (this.cachedFragment) { dom.repairClonedNode(fragment,[0,1]); } + var morph0 = dom.createMorphAt(fragment,0,1,contextualElement); + content(env, morph0, context, "yield"); + return fragment; + } + }; + }()); + __exports__["default"] = template(t); + }); +enifed("ember-htmlbars/templates/select", + ["ember-template-compiler/system/template","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var template = __dependency1__["default"]; + var t = (function() { + var child0 = (function() { + return { + isHTMLBars: true, + blockParams: 0, + cachedFragment: null, + hasRendered: false, + build: function build(dom) { + var el0 = dom.createElement("option"); + dom.setAttribute(el0,"value",""); + return el0; + }, + render: function render(context, env, contextualElement) { + var dom = env.dom; + var hooks = env.hooks, content = hooks.content; + dom.detectNamespace(contextualElement); + var fragment; + if (env.useFragmentCache && dom.canClone) { + if (this.cachedFragment === null) { + fragment = this.build(dom); + if (this.hasRendered) { + this.cachedFragment = fragment; + } else { + this.hasRendered = true; + } + } + if (this.cachedFragment) { + fragment = dom.cloneNode(this.cachedFragment, true); + } + } else { + fragment = this.build(dom); + } + var morph0 = dom.createMorphAt(fragment,-1,-1); + content(env, morph0, context, "view.prompt"); + return fragment; + } + }; + }()); + var child1 = (function() { + var child0 = (function() { + return { + isHTMLBars: true, + blockParams: 0, + cachedFragment: null, + hasRendered: false, + build: function build(dom) { + var el0 = dom.createDocumentFragment(); + var el1 = dom.createTextNode(""); + dom.appendChild(el0, el1); + var el1 = dom.createTextNode(""); + dom.appendChild(el0, el1); + return el0; + }, + render: function render(context, env, contextualElement) { + var dom = env.dom; + var hooks = env.hooks, get = hooks.get, inline = hooks.inline; + dom.detectNamespace(contextualElement); + var fragment; + if (env.useFragmentCache && dom.canClone) { + if (this.cachedFragment === null) { + fragment = this.build(dom); + if (this.hasRendered) { + this.cachedFragment = fragment; + } else { + this.hasRendered = true; + } + } + if (this.cachedFragment) { + fragment = dom.cloneNode(this.cachedFragment, true); + } + } else { + fragment = this.build(dom); + } + if (this.cachedFragment) { dom.repairClonedNode(fragment,[0,1]); } + var morph0 = dom.createMorphAt(fragment,0,1,contextualElement); + inline(env, morph0, context, "view", [get(env, context, "view.groupView")], {"content": get(env, context, "group.content"), "label": get(env, context, "group.label")}); + return fragment; + } + }; + }()); + return { + isHTMLBars: true, + blockParams: 0, + cachedFragment: null, + hasRendered: false, + build: function build(dom) { + var el0 = dom.createDocumentFragment(); + var el1 = dom.createTextNode(""); + dom.appendChild(el0, el1); + var el1 = dom.createTextNode(""); + dom.appendChild(el0, el1); + return el0; + }, + render: function render(context, env, contextualElement) { + var dom = env.dom; + var hooks = env.hooks, get = hooks.get, block = hooks.block; + dom.detectNamespace(contextualElement); + var fragment; + if (env.useFragmentCache && dom.canClone) { + if (this.cachedFragment === null) { + fragment = this.build(dom); + if (this.hasRendered) { + this.cachedFragment = fragment; + } else { + this.hasRendered = true; + } + } + if (this.cachedFragment) { + fragment = dom.cloneNode(this.cachedFragment, true); + } + } else { + fragment = this.build(dom); + } + if (this.cachedFragment) { dom.repairClonedNode(fragment,[0,1]); } + var morph0 = dom.createMorphAt(fragment,0,1,contextualElement); + block(env, morph0, context, "each", [get(env, context, "view.groupedContent")], {"keyword": "group"}, child0, null); + return fragment; + } + }; + }()); + var child2 = (function() { + var child0 = (function() { + return { + isHTMLBars: true, + blockParams: 0, + cachedFragment: null, + hasRendered: false, + build: function build(dom) { + var el0 = dom.createDocumentFragment(); + var el1 = dom.createTextNode(""); + dom.appendChild(el0, el1); + var el1 = dom.createTextNode(""); + dom.appendChild(el0, el1); + return el0; + }, + render: function render(context, env, contextualElement) { + var dom = env.dom; + var hooks = env.hooks, get = hooks.get, inline = hooks.inline; + dom.detectNamespace(contextualElement); + var fragment; + if (env.useFragmentCache && dom.canClone) { + if (this.cachedFragment === null) { + fragment = this.build(dom); + if (this.hasRendered) { + this.cachedFragment = fragment; + } else { + this.hasRendered = true; + } + } + if (this.cachedFragment) { + fragment = dom.cloneNode(this.cachedFragment, true); + } + } else { + fragment = this.build(dom); + } + if (this.cachedFragment) { dom.repairClonedNode(fragment,[0,1]); } + var morph0 = dom.createMorphAt(fragment,0,1,contextualElement); + inline(env, morph0, context, "view", [get(env, context, "view.optionView")], {"content": get(env, context, "item")}); + return fragment; + } + }; + }()); + return { + isHTMLBars: true, + blockParams: 0, + cachedFragment: null, + hasRendered: false, + build: function build(dom) { + var el0 = dom.createDocumentFragment(); + var el1 = dom.createTextNode(""); + dom.appendChild(el0, el1); + var el1 = dom.createTextNode(""); + dom.appendChild(el0, el1); + return el0; + }, + render: function render(context, env, contextualElement) { + var dom = env.dom; + var hooks = env.hooks, get = hooks.get, block = hooks.block; + dom.detectNamespace(contextualElement); + var fragment; + if (env.useFragmentCache && dom.canClone) { + if (this.cachedFragment === null) { + fragment = this.build(dom); + if (this.hasRendered) { + this.cachedFragment = fragment; + } else { + this.hasRendered = true; + } + } + if (this.cachedFragment) { + fragment = dom.cloneNode(this.cachedFragment, true); + } + } else { + fragment = this.build(dom); + } + if (this.cachedFragment) { dom.repairClonedNode(fragment,[0,1]); } + var morph0 = dom.createMorphAt(fragment,0,1,contextualElement); + block(env, morph0, context, "each", [get(env, context, "view.content")], {"keyword": "item"}, child0, null); + return fragment; + } + }; + }()); + return { + isHTMLBars: true, + blockParams: 0, + cachedFragment: null, + hasRendered: false, + build: function build(dom) { + var el0 = dom.createDocumentFragment(); + var el1 = dom.createTextNode(""); + dom.appendChild(el0, el1); + var el1 = dom.createTextNode(""); + dom.appendChild(el0, el1); + var el1 = dom.createTextNode("\n"); + dom.appendChild(el0, el1); + return el0; + }, + render: function render(context, env, contextualElement) { + var dom = env.dom; + var hooks = env.hooks, get = hooks.get, block = hooks.block; + dom.detectNamespace(contextualElement); + var fragment; + if (env.useFragmentCache && dom.canClone) { + if (this.cachedFragment === null) { + fragment = this.build(dom); + if (this.hasRendered) { + this.cachedFragment = fragment; + } else { + this.hasRendered = true; + } + } + if (this.cachedFragment) { + fragment = dom.cloneNode(this.cachedFragment, true); + } + } else { + fragment = this.build(dom); + } + if (this.cachedFragment) { dom.repairClonedNode(fragment,[0,1]); } + var morph0 = dom.createMorphAt(fragment,0,1,contextualElement); + var morph1 = dom.createMorphAt(fragment,1,2,contextualElement); + block(env, morph0, context, "if", [get(env, context, "view.prompt")], {}, child0, null); + block(env, morph1, context, "if", [get(env, context, "view.optionGroupPath")], {}, child1, child2); + return fragment; + } + }; + }()); + __exports__["default"] = template(t); + }); +enifed("ember-htmlbars/utils/string", + ["htmlbars-util","ember-runtime/system/string","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; /** - The `{{#each}}` helper loops over elements in a collection. It is an extension - of the base Handlebars `{{#each}}` helper. + @module ember + @submodule ember-htmlbars + */ - The default behavior of `{{#each}}` is to yield its inner block once for every - item in an array. + // required so we can extend this object. + var SafeString = __dependency1__.SafeString; + var escapeExpression = __dependency1__.escapeExpression; + var EmberStringUtils = __dependency2__["default"]; - ```javascript - var developers = [{name: 'Yehuda'},{name: 'Tom'}, {name: 'Paul'}]; - ``` + /** + Mark a string as safe for unescaped output with Handlebars. If you + return HTML from a Handlebars helper, use this function to + ensure Handlebars does not escape the HTML. - ```handlebars - {{#each person in developers}} - {{person.name}} - {{! `this` is whatever it was outside the #each }} - {{/each}} + ```javascript + Ember.String.htmlSafe('
    someString
    ') ``` - The same rules apply to arrays of primitives, but the items may need to be - references with `{{this}}`. - - ```javascript - var developerNames = ['Yehuda', 'Tom', 'Paul'] - ``` - - ```handlebars - {{#each name in developerNames}} - {{name}} - {{/each}} - ``` - - ### {{else}} condition - - `{{#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: + @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 ""; + } - ```handlebars -
      - {{#each developer in developers itemViewClass="person"}} - {{developer.name}} - {{/each}} -
    - ``` + if (typeof str !== 'string') { + str = ''+str; + } + return new SafeString(str); + } - Will use the following view for each item + EmberStringUtils.htmlSafe = htmlSafe; + if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { - ```javascript - App.PersonView = Ember.View.extend({ - tagName: 'li' - }); - ``` + /** + Mark a string as being safe for unescaped output with Handlebars. - Resulting in HTML output that looks like the following: + ```javascript + '
    someString
    '.htmlSafe() + ``` - ```html -
      -
    • Yehuda
    • -
    • Tom
    • -
    • Paul
    • -
    - ``` + See [Ember.String.htmlSafe](/api/classes/Ember.String.html#method_htmlSafe). - `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: + @method htmlSafe + @for String + @return {Handlebars.SafeString} a string that will not be html escaped by Handlebars + */ + String.prototype.htmlSafe = function() { + return htmlSafe(this); + }; + } - ```javascript - App.PersonView = Ember.View.extend({ - tagName: 'li', - template: '{{developer.name}}' - }); - ``` + __exports__.SafeString = SafeString; + __exports__.htmlSafe = htmlSafe; + __exports__.escapeExpression = escapeExpression; + }); +enifed("ember-metal-views", + ["ember-metal-views/renderer","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Renderer = __dependency1__["default"]; + __exports__.Renderer = Renderer; + }); +enifed("ember-metal-views/renderer", + ["morph","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var DOMHelper = __dependency1__.DOMHelper; - ```handlebars -
      - {{each developer in developers itemViewClass="person"}} -
    - ``` + function Renderer() { + this._uuid = 0; + this._views = new Array(2000); + this._queue = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; + this._parents = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; + this._elements = new Array(17); + this._inserts = {}; + this._dom = new DOMHelper(); + } - ### Specifying an alternative view for no items (else) + function Renderer_renderTree(_view, _parentView, _insertAt) { + var views = this._views; + views[0] = _view; + var insertAt = _insertAt === undefined ? -1 : _insertAt; + var index = 0; + var total = 1; + var levelBase = _parentView ? _parentView._level+1 : 0; - The `emptyViewClass` option provides the same flexibility to the `{{else}}` - case of the each helper. + var root = _parentView == null ? _view : _parentView._root; - ```javascript - App.NoPeopleView = Ember.View.extend({ - tagName: 'li', - template: 'No person is available, sorry' - }); - ``` + // if root view has a _morph assigned + var willInsert = !!root._morph; - ```handlebars -
      - {{#each developer in developers emptyViewClass="no-people"}} -
    • {{developer.name}}
    • - {{/each}} -
    - ``` + var queue = this._queue; + queue[0] = 0; + var length = 1; - ### Wrapping each item in a controller + var parentIndex = -1; + var parents = this._parents; + var parent = _parentView || null; + var elements = this._elements; + var element = null; + var contextualElement = null; + var level = 0; - 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 view = _view; + var children, i, child; + while (length) { + elements[level] = element; + if (!view._morph) { + // ensure props we add are in same order + view._morph = null; + } + view._root = root; + this.uuid(view); + view._level = levelBase + level; + if (view._elementCreated) { + this.remove(view, false, true); + } - This allows state and decoration to be added to the controller - while any other property lookups are delegated to the model. An example: + this.willCreateElement(view); - ```javascript - App.RecruitController = Ember.ObjectController.extend({ - isAvailableForHire: function() { - return !this.get('isEmployed') && this.get('isSeekingWork'); - }.property('isEmployed', 'isSeekingWork') - }) - ``` + contextualElement = view._morph && view._morph.contextualElement; + if (!contextualElement && parent && parent._childViewsMorph) { + contextualElement = parent._childViewsMorph.contextualElement; + } + if (!contextualElement && view._didCreateElementWithoutMorph) { + // This code path is only used by createElement and rerender when createElement + // was previously called on a view. + contextualElement = document.body; + } + element = this.createElement(view, contextualElement); - ```handlebars - {{#each person in developers itemController="recruit"}} - {{person.name}} {{#if person.isAvailableForHire}}Hire me!{{/if}} - {{/each}} - ``` + parents[level++] = parentIndex; + parentIndex = index; + parent = view; - @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(path) { - var options = arguments[arguments.length - 1]; - var helperName = 'each'; - var keywordName; + // enqueue for end + queue[length++] = index; + // enqueue children + children = this.childViews(view); + if (children) { + for (i=children.length-1;i>=0;i--) { + child = children[i]; + index = total++; + views[index] = child; + queue[length++] = index; + view = child; + } + } - if (arguments.length === 4) { - Ember.assert("If you pass more than one argument to the each helper," + - " it must be in the form #each foo in bar", arguments[1] === "in"); + index = queue[--length]; + view = views[index]; - keywordName = arguments[0]; - path = arguments[2]; + while (parentIndex === index) { + level--; + view._elementCreated = true; + this.didCreateElement(view); + if (willInsert) { + this.willInsertElement(view); + } - helperName += ' ' + keywordName + ' in ' + path; + if (level === 0) { + length--; + break; + } - options.hash.keyword = keywordName; - } else if (arguments.length === 1) { - path = ''; - } else { - helperName += ' ' + path; + 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; + } } - 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.', keywordName); + this.insertElement(view, _parentView, element, insertAt); - options.hash.emptyViewClass = Ember._MetamorphView; - options.hash.dataSourceBinding = path; - options.hashTypes.dataSourceBinding = 'STRING'; - options.helperName = options.helperName || helperName; + for (i=total-1; i>=0; i--) { + if (willInsert) { + views[i]._elementInserted = true; + this.didInsertElement(views[i]); + } + views[i] = null; + } - return EmberHandlebars.helpers.collection.call(this, EmberHandlebars.EachView, options); + return element; } - __exports__.EachView = EachView; - __exports__.eachHelper = eachHelper; - }); -enifed("ember-handlebars/helpers/if_unless", - ["ember-metal/core","ember-handlebars-compiler","ember-handlebars/helpers/binding","ember-metal/property_get","ember-metal/utils","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-handlebars - */ - - var Ember = __dependency1__["default"]; - // Ember.assert - var EmberHandlebars = __dependency2__["default"]; + 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 bind = __dependency3__.bind; + 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); + }); + }; - var get = __dependency4__.get; - var isArray = __dependency5__.isArray; + Renderer.prototype.appendTo = + function Renderer_appendTo(view, target) { + var morph = this._dom.appendMorph(target); + this.scheduleInsert(view, morph); + }; - var helpers = EmberHandlebars.helpers; + Renderer.prototype.replaceIn = + function Renderer_replaceIn(view, target) { + var morph = this._dom.createMorph(target, null, null); + this.scheduleInsert(view, morph); + }; - function shouldDisplayIfHelperContent(result) { - var truthy = result && get(result, 'isTruthy'); - if (typeof truthy === 'boolean') { return truthy; } + function Renderer_remove(_view, shouldDestroy, reset) { + var viewId = this.uuid(_view); - if (isArray(result)) { - return get(result, 'length') !== 0; - } else { - return !!result; + if (this._inserts[viewId]) { + this.cancelRender(this._inserts[viewId]); + this._inserts[viewId] = undefined; } - } - /** - Use the `boundIf` helper to create a conditional that re-evaluates - whenever the truthiness of the bound value changes. + if (!_view._elementCreated) { + return; + } - ```handlebars - {{#boundIf "content.shouldDisplayTitle"}} - {{content.title}} - {{/boundIf}} - ``` + var removeQueue = []; + var destroyQueue = []; + var morph = _view._morph; + var idx, len, view, queue, childViews, i, l; - @private - @method boundIf - @for Ember.Handlebars.helpers - @param {String} property Property to bind - @param {Function} fn Context to provide for rendering - @return {String} HTML string - */ - function boundIfHelper(property, fn) { - var context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : this; + removeQueue.push(_view); - fn.helperName = fn.helperName || 'boundIf'; + for (idx=0; idx - {{loc '_welcome_'}} -
    - ``` - - ```html -
    - Bonjour -
    - ``` - - See [Ember.String.loc](/api/classes/Ember.String.html#method_loc) for how to - set up localized string references. - - @method loc - @for Ember.Handlebars.helpers - @param {String} str The string to format - @see {Ember.String#loc} - */ - __exports__["default"] = loc; - }); -enifed("ember-handlebars/helpers/partial", - ["ember-metal/core","ember-metal/is_none","ember-handlebars/helpers/binding","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; + // BEGIN IMPORTS var Ember = __dependency1__["default"]; - // Ember.assert - // var emberAssert = Ember.assert; + var merge = __dependency2__["default"]; + var instrument = __dependency3__.instrument; + var reset = __dependency3__.reset; + var subscribe = __dependency3__.subscribe; + var unsubscribe = __dependency3__.unsubscribe; + var EMPTY_META = __dependency4__.EMPTY_META; + var GUID_KEY = __dependency4__.GUID_KEY; + var META_DESC = __dependency4__.META_DESC; + var apply = __dependency4__.apply; + var applyStr = __dependency4__.applyStr; + var canInvoke = __dependency4__.canInvoke; + var generateGuid = __dependency4__.generateGuid; + var getMeta = __dependency4__.getMeta; + var guidFor = __dependency4__.guidFor; + var inspect = __dependency4__.inspect; + var isArray = __dependency4__.isArray; + var makeArray = __dependency4__.makeArray; + var meta = __dependency4__.meta; + var metaPath = __dependency4__.metaPath; + var setMeta = __dependency4__.setMeta; + var tryCatchFinally = __dependency4__.tryCatchFinally; + var tryFinally = __dependency4__.tryFinally; + var tryInvoke = __dependency4__.tryInvoke; + var typeOf = __dependency4__.typeOf; + var uuid = __dependency4__.uuid; + var wrap = __dependency4__.wrap; + var EmberError = __dependency5__["default"]; + var EnumerableUtils = __dependency6__["default"]; + var Cache = __dependency7__["default"]; + var create = __dependency8__.create; + var hasPropertyAccessors = __dependency8__.hasPropertyAccessors; + var filter = __dependency9__.filter; + var forEach = __dependency9__.forEach; + var indexOf = __dependency9__.indexOf; + var map = __dependency9__.map; + var Logger = __dependency10__["default"]; - var isNone = __dependency2__["default"]; - var bind = __dependency3__.bind; + var _getPath = __dependency11__._getPath; + var get = __dependency11__.get; + var getWithDefault = __dependency11__.getWithDefault; + var normalizeTuple = __dependency11__.normalizeTuple; - /** - @module ember - @submodule ember-handlebars - */ + var accumulateListeners = __dependency12__.accumulateListeners; + var addListener = __dependency12__.addListener; + var hasListeners = __dependency12__.hasListeners; + var listenersFor = __dependency12__.listenersFor; + var on = __dependency12__.on; + var removeListener = __dependency12__.removeListener; + var sendEvent = __dependency12__.sendEvent; + var suspendListener = __dependency12__.suspendListener; + var suspendListeners = __dependency12__.suspendListeners; + var watchedEvents = __dependency12__.watchedEvents; - /** - The `partial` helper renders another template without - changing the template context: + var ObserverSet = __dependency13__["default"]; - ```handlebars - {{foo}} - {{partial "nav"}} - ``` + var beginPropertyChanges = __dependency14__.beginPropertyChanges; + var changeProperties = __dependency14__.changeProperties; + var endPropertyChanges = __dependency14__.endPropertyChanges; + var overrideChains = __dependency14__.overrideChains; + var propertyDidChange = __dependency14__.propertyDidChange; + var propertyWillChange = __dependency14__.propertyWillChange; - 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. + var Descriptor = __dependency15__.Descriptor; + var defineProperty = __dependency15__.defineProperty; + var set = __dependency16__.set; + var trySet = __dependency16__.trySet; - If a "_nav" template isn't found, the `partial` helper will - fall back to a template named "nav". + var Map = __dependency17__.Map; + var MapWithDefault = __dependency17__.MapWithDefault; + var OrderedSet = __dependency17__.OrderedSet; + var getProperties = __dependency18__["default"]; + var setProperties = __dependency19__["default"]; + var watchKey = __dependency20__.watchKey; + var unwatchKey = __dependency20__.unwatchKey; + var ChainNode = __dependency21__.ChainNode; + var finishChains = __dependency21__.finishChains; + var flushPendingChains = __dependency21__.flushPendingChains; + var removeChainWatcher = __dependency21__.removeChainWatcher; + var watchPath = __dependency22__.watchPath; + var unwatchPath = __dependency22__.unwatchPath; + var destroy = __dependency23__.destroy; + var isWatching = __dependency23__.isWatching; + var rewatch = __dependency23__.rewatch; + var unwatch = __dependency23__.unwatch; + var watch = __dependency23__.watch; + var expandProperties = __dependency24__["default"]; + var ComputedProperty = __dependency25__.ComputedProperty; + var computed = __dependency25__.computed; + var cacheFor = __dependency25__.cacheFor; - ## Bound template names + // side effect of defining the computed.* macros - The parameter supplied to `partial` can also be a path - to a property containing a template name, e.g.: + var _suspendBeforeObserver = __dependency27__._suspendBeforeObserver; + var _suspendBeforeObservers = __dependency27__._suspendBeforeObservers; + var _suspendObserver = __dependency27__._suspendObserver; + var _suspendObservers = __dependency27__._suspendObservers; + var addBeforeObserver = __dependency27__.addBeforeObserver; + var addObserver = __dependency27__.addObserver; + var beforeObserversFor = __dependency27__.beforeObserversFor; + var observersFor = __dependency27__.observersFor; + var removeBeforeObserver = __dependency27__.removeBeforeObserver; + var removeObserver = __dependency27__.removeObserver; + var IS_BINDING = __dependency28__.IS_BINDING; + var Mixin = __dependency28__.Mixin; + var aliasMethod = __dependency28__.aliasMethod; + var beforeObserver = __dependency28__.beforeObserver; + var immediateObserver = __dependency28__.immediateObserver; + var mixin = __dependency28__.mixin; + var observer = __dependency28__.observer; + var required = __dependency28__.required; + var Binding = __dependency29__.Binding; + var bind = __dependency29__.bind; + var isGlobalPath = __dependency29__.isGlobalPath; + var oneWay = __dependency29__.oneWay; + var run = __dependency30__["default"]; + var Libraries = __dependency31__["default"]; + var isNone = __dependency32__["default"]; + var isEmpty = __dependency33__["default"]; + var isBlank = __dependency34__["default"]; + var isPresent = __dependency35__["default"]; + var keys = __dependency36__["default"]; + var Backburner = __dependency37__["default"]; - ```handlebars - {{partial someTemplateName}} - ``` + // END IMPORTS - 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. + // BEGIN EXPORTS + var EmberInstrumentation = Ember.Instrumentation = {}; + EmberInstrumentation.instrument = instrument; + EmberInstrumentation.subscribe = subscribe; + EmberInstrumentation.unsubscribe = unsubscribe; + EmberInstrumentation.reset = reset; + Ember.instrument = instrument; + Ember.subscribe = subscribe; - @method partial - @for Ember.Handlebars.helpers - @param {String} partialName the name of the template to render minus the leading underscore - */ + Ember._Cache = Cache; - __exports__["default"] = function partialHelper(name, options) { - var view = options.data.view; + Ember.generateGuid = generateGuid; + Ember.GUID_KEY = GUID_KEY; + Ember.create = create; + Ember.keys = keys; + Ember.platform = { + defineProperty: defineProperty, + hasPropertyAccessors: hasPropertyAccessors + }; - var context = (options.contexts && options.contexts.length) ? options.contexts[0] : this; + var EmberArrayPolyfills = Ember.ArrayPolyfills = {}; - options.helperName = options.helperName || 'partial'; + EmberArrayPolyfills.map = map; + EmberArrayPolyfills.forEach = forEach; + EmberArrayPolyfills.filter = filter; + EmberArrayPolyfills.indexOf = indexOf; - if (options.types[0] === "ID") { - var partialNameStream = view.getStream(name); - // 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) { - renderPartial(context, partialNameStream.value(), fnOptions); - }; + Ember.Error = EmberError; + Ember.guidFor = guidFor; + Ember.META_DESC = META_DESC; + Ember.EMPTY_META = EMPTY_META; + Ember.meta = meta; + Ember.getMeta = getMeta; + Ember.setMeta = setMeta; + Ember.metaPath = metaPath; + Ember.inspect = inspect; + Ember.typeOf = typeOf; + Ember.tryCatchFinally = tryCatchFinally; + Ember.isArray = isArray; + Ember.makeArray = makeArray; + Ember.canInvoke = canInvoke; + Ember.tryInvoke = tryInvoke; + Ember.tryFinally = tryFinally; + Ember.wrap = wrap; + Ember.apply = apply; + Ember.applyStr = applyStr; + Ember.uuid = uuid; - return bind.call(context, name, options, true, exists); - } else { - // Render the partial right into parent template. - renderPartial(context, name, options); - } - } + Ember.Logger = Logger; - function exists(value) { - return !isNone(value); - } + Ember.get = get; + Ember.getWithDefault = getWithDefault; + Ember.normalizeTuple = normalizeTuple; + Ember._getPath = _getPath; - function renderPartial(context, name, options) { - var nameParts = name.split("/"); - var lastPart = nameParts[nameParts.length - 1]; + Ember.EnumerableUtils = EnumerableUtils; - nameParts[nameParts.length - 1] = "_" + lastPart; + Ember.on = on; + Ember.addListener = addListener; + Ember.removeListener = removeListener; + Ember._suspendListener = suspendListener; + Ember._suspendListeners = suspendListeners; + Ember.sendEvent = sendEvent; + Ember.hasListeners = hasListeners; + Ember.watchedEvents = watchedEvents; + Ember.listenersFor = listenersFor; + Ember.accumulateListeners = accumulateListeners; - var view = options.data.view; - var underscoredName = nameParts.join("/"); - var template = view.templateForName(underscoredName); - var deprecatedTemplate = !template && view.templateForName(name); + Ember._ObserverSet = ObserverSet; - Ember.assert("Unable to find partial with name '"+name+"'.", template || deprecatedTemplate); + Ember.propertyWillChange = propertyWillChange; + Ember.propertyDidChange = propertyDidChange; + Ember.overrideChains = overrideChains; + Ember.beginPropertyChanges = beginPropertyChanges; + Ember.endPropertyChanges = endPropertyChanges; + Ember.changeProperties = changeProperties; - template = template || deprecatedTemplate; + Ember.Descriptor = Descriptor; + Ember.defineProperty = defineProperty; - template(context, { - data: options.data - }); - } - }); -enifed("ember-handlebars/helpers/template", - ["ember-metal/core","ember-handlebars-compiler","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // Ember.deprecate; + Ember.set = set; + Ember.trySet = trySet; + + Ember.OrderedSet = OrderedSet; + Ember.Map = Map; + Ember.MapWithDefault = MapWithDefault; + + Ember.getProperties = getProperties; + Ember.setProperties = setProperties; + + Ember.watchKey = watchKey; + Ember.unwatchKey = unwatchKey; + + Ember.flushPendingChains = flushPendingChains; + Ember.removeChainWatcher = removeChainWatcher; + Ember._ChainNode = ChainNode; + Ember.finishChains = finishChains; + + Ember.watchPath = watchPath; + Ember.unwatchPath = unwatchPath; + + Ember.watch = watch; + Ember.isWatching = isWatching; + Ember.unwatch = unwatch; + Ember.rewatch = rewatch; + Ember.destroy = destroy; + + Ember.expandProperties = expandProperties; + + Ember.ComputedProperty = ComputedProperty; + Ember.computed = computed; + Ember.cacheFor = cacheFor; + + Ember.addObserver = addObserver; + Ember.observersFor = observersFor; + Ember.removeObserver = removeObserver; + Ember.addBeforeObserver = addBeforeObserver; + Ember._suspendBeforeObserver = _suspendBeforeObserver; + Ember._suspendBeforeObservers = _suspendBeforeObservers; + Ember._suspendObserver = _suspendObserver; + Ember._suspendObservers = _suspendObservers; + Ember.beforeObserversFor = beforeObserversFor; + Ember.removeBeforeObserver = removeBeforeObserver; + + Ember.IS_BINDING = IS_BINDING; + Ember.required = required; + Ember.aliasMethod = aliasMethod; + Ember.observer = observer; + Ember.immediateObserver = immediateObserver; + Ember.beforeObserver = beforeObserver; + Ember.mixin = mixin; + Ember.Mixin = Mixin; + + Ember.oneWay = oneWay; + Ember.bind = bind; + Ember.Binding = Binding; + Ember.isGlobalPath = isGlobalPath; + + Ember.run = run; - var EmberHandlebars = __dependency2__["default"]; /** - @module ember - @submodule ember-handlebars + * @class Backburner + * @for Ember + * @private */ + Ember.Backburner = Backburner; + + Ember.libraries = new Libraries(); + Ember.libraries.registerCoreLibrary('Ember', Ember.VERSION); + + Ember.isNone = isNone; + Ember.isEmpty = isEmpty; + Ember.isBlank = isBlank; + + + Ember.isPresent = isPresent; + + + Ember.merge = merge; /** - @deprecated - @method template - @for Ember.Handlebars.helpers - @param {String} templateName the template to render - */ - __exports__["default"] = function templateHelper(name, options) { - Ember.deprecate("The `template` helper has been deprecated in favor of the `partial` helper." + - " Please use `partial` instead, which will work the same way."); + A function may be assigned to `Ember.onerror` to be called when Ember + internals encounter an error. This is useful for specialized error handling + and reporting code. - options.helperName = options.helperName || 'template'; + ```javascript + Ember.onerror = function(error) { + Em.$.ajax('/report-error', 'POST', { + stack: error.stack, + otherInformation: 'whatever app state you want to provide' + }); + }; + ``` - return EmberHandlebars.helpers.partial.apply(this, arguments); + Internally, `Ember.onerror` is used as Backburner's error handler. + + @event onerror + @for Ember + @param {Exception} error the error object + */ + Ember.onerror = null; + // END EXPORTS + + // do this for side-effects of updating Ember.assert, warn, etc when + // ember-debug is present + if (Ember.__loader.registry['ember-debug']) { + requireModule('ember-debug'); } + + __exports__["default"] = Ember; }); -enifed("ember-handlebars/helpers/unbound", - ["ember-handlebars-compiler","ember-handlebars/helpers/binding","exports"], - function(__dependency1__, __dependency2__, __exports__) { +enifed("ember-metal/alias", + ["ember-metal/property_get","ember-metal/property_set","ember-metal/core","ember-metal/error","ember-metal/properties","ember-metal/computed","ember-metal/platform","ember-metal/utils","ember-metal/dependent_keys","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __exports__) { "use strict"; - /** - @module ember - @submodule ember-handlebars - */ + var get = __dependency1__.get; + var set = __dependency2__.set; + var Ember = __dependency3__["default"]; + // Ember.assert + var EmberError = __dependency4__["default"]; + var Descriptor = __dependency5__.Descriptor; + var defineProperty = __dependency5__.defineProperty; + var ComputedProperty = __dependency6__.ComputedProperty; + var create = __dependency7__.create; + var meta = __dependency8__.meta; + var inspect = __dependency8__.inspect; + var addDependentKeys = __dependency9__.addDependentKeys; + var removeDependentKeys = __dependency9__.removeDependentKeys; - var EmberHandlebars = __dependency1__["default"]; + __exports__["default"] = function alias(altKey) { + return new AliasedProperty(altKey); + } - var resolveHelper = __dependency2__.resolveHelper; + function AliasedProperty(altKey) { + this.altKey = altKey; + this._dependentKeys = [ altKey ]; + } - /** - `unbound` allows you to output a property without binding. *Important:* The - output will not be updated if the property changes. Use with caution. + __exports__.AliasedProperty = AliasedProperty;AliasedProperty.prototype = create(Descriptor.prototype); - ```handlebars -
    {{unbound somePropertyThatDoesntChange}}
    - ``` + AliasedProperty.prototype.get = function AliasedProperty_get(obj, keyName) { + return get(obj, this.altKey); + }; - `unbound` can also be used in conjunction with a bound helper to - render it in its unbound form: + AliasedProperty.prototype.set = function AliasedProperty_set(obj, keyName, value) { + return set(obj, this.altKey, value); + }; - ```handlebars -
    {{unbound helperName somePropertyThatDoesntChange}}
    - ``` + AliasedProperty.prototype.willWatch = function(obj, keyName) { + addDependentKeys(this, obj, keyName, meta(obj)); + }; - @method unbound - @for Ember.Handlebars.helpers - @param {String} property - @return {String} HTML string - */ - __exports__["default"] = function unboundHelper(property) { - var argsLength = arguments.length; - var options = arguments[argsLength - 1]; - var view = options.data.view; - var container = view.container; + AliasedProperty.prototype.didUnwatch = function(obj, keyName) { + removeDependentKeys(this, obj, keyName, meta(obj)); + }; - if (argsLength <= 2) { - return view.getStream(property).value(); - } else { - options.data.isUnbound = true; - options.types.shift(); + 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); + } + }; - var args = new Array(argsLength - 1); - for (var i = 1; i < argsLength; i++) { - args[i - 1] = arguments[i]; - } + AliasedProperty.prototype.teardown = function(obj, keyName) { + var m = meta(obj); + if (m.watching[keyName]) { + removeDependentKeys(this, obj, keyName, m); + } + }; - var helper = resolveHelper(container, property) || EmberHandlebars.helpers.helperMissing; + AliasedProperty.prototype.readOnly = function() { + this.set = AliasedProperty_readOnlySet; + return this; + }; - // Attempt to exec the first field as a helper - options.name = arguments[0]; + function AliasedProperty_readOnlySet(obj, keyName, value) { + throw new EmberError('Cannot set read-only property "' + keyName + '" on object: ' + inspect(obj)); + } - var result = helper.apply(this, args); + AliasedProperty.prototype.oneWay = function() { + this.set = AliasedProperty_oneWaySet; + return this; + }; - delete options.data.isUnbound; - return result; - } + 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-handlebars/helpers/view", - ["ember-metal/core","ember-runtime/system/object","ember-metal/property_get","ember-metal/keys","ember-metal/mixin","ember-views/streams/read","ember-views/views/view","ember-metal/streams/simple","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { +enifed("ember-metal/array", + ["exports"], + function(__exports__) { "use strict"; /** - @module ember - @submodule ember-handlebars + @module ember-metal */ - var Ember = __dependency1__["default"]; - // Ember.warn, Ember.assert - // var emberWarn = Ember.warn, emberAssert = Ember.assert; + var ArrayPrototype = Array.prototype; - var EmberObject = __dependency2__["default"]; - var get = __dependency3__.get; - var keys = __dependency4__["default"]; - var IS_BINDING = __dependency5__.IS_BINDING; - var readViewFactory = __dependency6__.readViewFactory; - var View = __dependency7__["default"]; - var SimpleStream = __dependency8__["default"]; + // 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; + }; + + var defineNativeShim = function(nativeFunc, shim) { + if (isNativeFunc(nativeFunc)) { + return nativeFunc; + } + return shim; + }; - function makeBindings(options) { - var hash = options.hash; - var hashTypes = options.hashTypes; - var view = options.data.view; + // From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/map + var map = defineNativeShim(ArrayPrototype.map, function(fun /*, thisp */) { + //"use strict"; - for (var prop in hash) { - var hashType = hashTypes[prop]; - var value = hash[prop]; + if (this === void 0 || this === null || typeof fun !== "function") { + throw new TypeError(); + } - if (IS_BINDING.test(prop)) { - // classBinding is processed separately - if (prop === 'classBinding') { - continue; - } + var t = Object(this); + var len = t.length >>> 0; + var res = new Array(len); + var thisp = arguments[1]; - if (hashType === 'ID') { - Ember.warn("You're attempting to render a view by passing " + - prop + "=" + value + - " to a view helper, but this syntax is ambiguous. You should either surround " + - value + " in quotes or remove `Binding` from " + prop + "."); - hash[prop] = view._getBindingForStream(value); - } else if (typeof value === 'string') { - hash[prop] = view._getBindingForStream(value); - } - } else { - if (hashType === 'ID') { - if (prop === 'class') { - hash.classBinding = value; - } else { - hash[prop + 'Binding'] = view._getBindingForStream(value); - } - delete hash[prop]; - delete hashTypes[prop]; - } + for (var i = 0; i < len; i++) { + if (i in t) { + res[i] = fun.call(thisp, t[i], i, t); } } - if (hash.idBinding) { - // id can't be bound, so just perform one-time lookup. - hash.id = hash.idBinding.value(); - hashTypes.id = 'STRING'; - delete hash.idBinding; - delete hashTypes.idBinding; - } - } + return res; + }); - var ViewHelper = EmberObject.create({ - propertiesFromHTMLOptions: function(options) { - var view = options.data.view; - var hash = options.hash; - var classes = hash['class']; + // From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/foreach + var forEach = defineNativeShim(ArrayPrototype.forEach, function(fun /*, thisp */) { + //"use strict"; - var extensions = { - helperName: options.helperName || '' - }; + if (this === void 0 || this === null || typeof fun !== "function") { + throw new TypeError(); + } - if (hash.id) { - extensions.elementId = hash.id; - } + var t = Object(this); + var len = t.length >>> 0; + var thisp = arguments[1]; - if (hash.tag) { - extensions.tagName = hash.tag; + for (var i = 0; i < len; i++) { + if (i in t) { + fun.call(thisp, t[i], i, t); } + } + }); - if (classes) { - classes = classes.split(' '); - extensions.classNames = classes; - } - - if (hash.classBinding) { - extensions.classNameBindings = hash.classBinding.split(' '); - } - - if (hash.classNameBindings) { - if (extensions.classNameBindings === undefined) { - extensions.classNameBindings = []; - } - extensions.classNameBindings = extensions.classNameBindings.concat(hash.classNameBindings.split(' ')); - } + 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); + } - if (hash.attributeBindings) { - Ember.assert("Setting 'attributeBindings' via Handlebars is not allowed." + - " Please subclass Ember.View and set it there instead."); - extensions.attributeBindings = null; + for (var i = fromIndex, j = this.length; i < j; i++) { + if (this[i] === obj) { + return i; } + } + return -1; + }); - // 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); + var lastIndexOf = defineNativeShim(ArrayPrototype.lastIndexOf, function(obj, fromIndex) { + var len = this.length; + var idx; - for (var i = 0, l = hashKeys.length; i < l; i++) { - var prop = hashKeys[i]; + if (fromIndex === undefined) fromIndex = len-1; + else fromIndex = (fromIndex < 0) ? Math.ceil(fromIndex) : Math.floor(fromIndex); + if (fromIndex < 0) fromIndex += len; - if (prop !== 'classNameBindings') { - extensions[prop] = hash[prop]; - } + for(idx = fromIndex;idx>=0;idx--) { + if (this[idx] === obj) return idx ; } + return -1; + }); - var classNameBindings = extensions.classNameBindings; - if (classNameBindings) { - for (var j = 0; j < classNameBindings.length; j++) { - var parsedPath = View._parsePropertyPath(classNameBindings[j]); - if (parsedPath.path === '') { - parsedPath.stream = new SimpleStream(true); - } else { - parsedPath.stream = view.getStream(parsedPath.path); - } - classNameBindings[j] = parsedPath; + 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; + }); - return extensions; - }, - - helper: function(thisContext, newView, options) { - var data = options.data; - var fn = options.fn; - var newViewProto; + 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; + } - makeBindings(options); + /** + Array polyfills to support ES5 features in older browsers. - var viewOptions = this.propertiesFromHTMLOptions(options, thisContext); - var currentView = data.view; - viewOptions.templateData = data; + @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; - if (View.detectInstance(newView)) { - newViewProto = newView; - } else { - newViewProto = newView.proto(); - } - if (fn) { - Ember.assert("You cannot provide a template block if you also specified a templateName", - !get(viewOptions, 'templateName') && !get(newViewProto, 'templateName')); - viewOptions.template = fn; - } + // ES6TODO: where is Ember.lookup defined? + /** + @module ember-metal + */ - // 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 = thisContext; - } + // .......................................................... + // CONSTANTS + // - currentView.appendChild(newView, viewOptions); - }, + /** + 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. - instanceHelper: function(thisContext, newView, options) { - var data = options.data; - var fn = options.fn; + @property LOG_BINDINGS + @for Ember + @type Boolean + @default false + */ + Ember.LOG_BINDINGS = false || !!Ember.ENV.LOG_BINDINGS; - makeBindings(options); + /** + Returns true if the provided path is global (e.g., `MyApp.fooController.bar`) + instead of local (`foo.bar.baz`). - Ember.assert( - 'Only a instance of a view may be passed to the ViewHelper.instanceHelper', - View.detectInstance(newView) - ); + @method isGlobalPath + @for Ember + @private + @param {String} path + @return Boolean + */ - var viewOptions = this.propertiesFromHTMLOptions(options, thisContext); - var currentView = data.view; - viewOptions.templateData = data; + function getWithGlobals(obj, path) { + return get(isGlobalPath(path) ? Ember.lookup : obj, path); + } - if (fn) { - Ember.assert("You cannot provide a template block if you also specified a templateName", - !get(viewOptions, 'templateName') && !get(newView, 'templateName')); - viewOptions.template = fn; - } + // .......................................................... + // BINDING + // - // 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 = thisContext; - } + function Binding(toPath, fromPath) { + this._direction = undefined; + this._from = fromPath; + this._to = toPath; + this._readyToSync = undefined; + this._oneWay = undefined; + } - 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. - - An empty `` and the following template: - - ```handlebars - A span: - {{#view tagName="span"}} - hello. - {{/view}} - ``` + @class Binding + @namespace Ember + */ - Will result in HTML structure: + Binding.prototype = { + /** + This copies the Binding so it can be connected to another object. - ```html - - + @method copy + @return {Ember.Binding} `this` + */ + copy: function () { + var copy = new Binding(this._to, this._from); + if (this._oneWay) { copy._oneWay = true; } + return copy; + }, -
    - A span: - - Hello. - -
    - - ``` + // .......................................................... + // CONFIG + // - ### `parentView` setting + /** + 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. - 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. + 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. - ```javascript - aView = Ember.View.create({ - template: Ember.Handlebars.compile("{{#view}} my parent: {{parentView.elementId}} {{/view}}") - }); + @method from + @param {String} path the property path to connect to + @return {Ember.Binding} `this` + */ + from: function(path) { + this._from = path; + return this; + }, - aView.appendTo('body'); - ``` + /** + 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. - Will result in HTML structure: + 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. - ```html -
    -
    - my parent: ember1 -
    -
    - ``` + @method to + @param {String|Tuple} path A property path or tuple + @return {Ember.Binding} `this` + */ + to: function(path) { + this._to = path; + return this; + }, - ### Setting CSS id and class attributes + /** + 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. - 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`. + @method oneWay + @return {Ember.Binding} `this` + */ + oneWay: function() { + this._oneWay = true; + return this; + }, - ```handlebars - {{#view tagName="span" id="a-custom-id"}} - hello. - {{/view}} - ``` + /** + @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; + }, - Results in the following HTML structure: + // .......................................................... + // CONNECT AND SYNC + // - ```html -
    - - hello. - -
    - ``` + /** + 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. - 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: + @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); - ```handlebars - {{#view tagName="span" class="a-custom-class"}} - hello. - {{/view}} - ``` + var fromPath = this._from; + var toPath = this._to; + trySet(obj, toPath, getWithGlobals(obj, fromPath)); - Results in the following HTML structure: + // add an observer on the object to be notified when the binding should be updated + addObserver(obj, fromPath, this, this.fromDidChange); - ```html -
    - - hello. - -
    - ``` + // 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); + } - ### Supplying a different view class + this._readyToSync = true; - `{{view}}` can take an optional first argument before its supplied options to - specify a path to a custom view class. + return this; + }, - ```handlebars - {{#view "custom"}}{{! will look up App.CustomView }} - hello. - {{/view}} - ``` + /** + Disconnects the binding instance. Changes will no longer be relayed. You + will not usually need to call this method. - The first argument can also be a relative path accessible from the current - context. + @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); - ```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}}') - }); + var twoWay = !this._oneWay; - MyApp.OuterView.create().appendTo('body'); - ``` + // 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); - Will result in the following HTML: + // if the binding is two-way, remove the observer from the target as well + if (twoWay) { + removeObserver(obj, this._to, this, this.toDidChange); + } - ```html -
    -
    - hi -
    -
    - ``` + this._readyToSync = false; // disable scheduled syncs... + return this; + }, - ### Blockless use + // .......................................................... + // PRIVATE + // - 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. + /* called when the from side changes */ + fromDidChange: function(target) { + this._scheduleSync(target, 'fwd'); + }, - ```javascript - var App = Ember.Application.create(); - App.WithTemplateDefinedView = Ember.View.extend({ - templateName: 'defined-template' - }); - ``` + /* called when the to side changes */ + toDidChange: function(target) { + this._scheduleSync(target, 'back'); + }, - ```handlebars - {{! application.hbs }} - {{view 'with-template-defined'}} - ``` + _scheduleSync: function(obj, dir) { + var existingDir = this._direction; - ```handlebars - {{! defined-template.hbs }} - Some content for the defined template view. - ``` + // if we haven't scheduled the binding yet, schedule it + if (existingDir === undefined) { + run.schedule('sync', this, this._sync, obj); + this._direction = dir; + } - ### `viewName` property + // 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'; + } + }, - 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. + _sync: function(obj) { + var log = Ember.LOG_BINDINGS; - ```javascript - aView = Ember.View.create({ - template: Ember.Handlebars.compile('{{#view viewName="aChildByName"}} hi {{/view}}') - }); + // don't synchronize destroyed objects or disconnected bindings + if (obj.isDestroyed || !this._readyToSync) { return; } - aView.appendTo('body'); - aView.get('aChildByName') // the instance of Ember.View created by {{view}} helper - ``` + // get the direction of the binding for the object we are + // synchronizing from + var direction = this._direction; - @method view - @for Ember.Handlebars.helpers - @param {String} path - @param {Hash} options - @return {String} HTML string - */ - function viewHelper(path) { - Ember.assert("The view helper only takes a single argument", arguments.length <= 2); + var fromPath = this._from; + var toPath = this._to; - var options = arguments[arguments.length - 1]; - var types = options.types; - var view = options.data.view; - var container = view.container || view._keywords.view.value().container; - var viewClass; + this._direction = undefined; - // If no path is provided, treat path param as options - // and get an instance of the registered `view:toplevel` - if (arguments.length === 1) { - if (container) { - viewClass = container.lookupFactory('view:toplevel'); - } else { - viewClass = View; - } - } else { - var pathStream; - if (typeof path === 'string' && types[0] === 'ID') { - pathStream = view.getStream(path); - Ember.deprecate('Resolved the view "'+path+'" 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', !pathStream.isGlobal()); - } else { - pathStream = path; + // 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); + }); } - - viewClass = readViewFactory(pathStream, container); } - options.helperName = options.helperName || 'view'; + }; - return ViewHelper.helper(this, viewClass, options); + function mixinProperties(to, from) { + for (var key in from) { + if (from.hasOwnProperty(key)) { + to[key] = from[key]; + } + } } - __exports__.viewHelper = viewHelper; - }); -enifed("ember-handlebars/helpers/with", - ["ember-metal/core","ember-metal/property_set","ember-metal/utils","ember-metal/platform","ember-metal/is_none","ember-handlebars/helpers/binding","ember-handlebars/views/handlebars_bound_view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-handlebars - */ - - var Ember = __dependency1__["default"]; - // Ember.assert + mixinProperties(Binding, { - var set = __dependency2__.set; - var apply = __dependency3__.apply; - var o_create = __dependency4__.create; - var isNone = __dependency5__["default"]; - var bind = __dependency6__.bind; - var _HandlebarsBoundView = __dependency7__._HandlebarsBoundView; + /* + See `Ember.Binding.from`. - function exists(value) { - return !isNone(value); - } - - var WithView = _HandlebarsBoundView.extend({ - init: function() { - apply(this, this._super, arguments); - - var keywordName = this.templateHash.keywordName; - 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; + @method from + @static + */ + from: function(from) { + var C = this; + return new C(undefined, from); + }, - if (this.preserveContext) { - this._keywords[keywordName] = 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; - }; - } + /* + See `Ember.Binding.to`. - set(controller, 'model', this.lazyValue.value()); - } + @method to + @static + */ + to: function(to) { + var C = this; + return new C(to, undefined); }, - willDestroy: function() { - this._super(); + /** + 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 (this._generatedController) { - this._generatedController.destroy(); - } + 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); } - }); + }); /** - 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. - - ```handlebars - // posts might not be - {{#with user.posts as blogPosts}} -
    - There are {{blogPosts.length}} blog posts written by {{user.name}}. -
    - - {{#each post in blogPosts}} -
  • {{post.title}}
  • - {{/each}} - {{/with}} - ``` - - Without the `as` operator, it would be impossible to reference `user.name` in the example above. - - 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}}`. + 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. - ### `controller` option + ## Automatic Creation of Bindings with `/^*Binding/`-named Properties - Adding `controller='something'` instructs the `{{with}}` helper to create and use an instance of - the specified controller wrapping the aliased keyword. + You do not usually create Binding objects directly but instead describe + bindings in your class or object definition using automatic binding + detection. - This is very similar to using an `itemController` option with the `{{each}}` helper. + 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"): - ```handlebars - {{#with users.posts as posts controller='userBlogPosts'}} - {{!- `posts` is wrapped in our controller instance }} - {{/with}} + ``` + valueBinding: "MyApp.someController.title" ``` - 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. - - @method with - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string - */ - __exports__["default"] = function withHelper(contextPath) { - var options = arguments[arguments.length - 1]; - var view = options.data.view; - var bindContext, preserveContext; - var helperName = 'with'; - - if (arguments.length === 4) { - 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"); - - var keywordName = arguments[2]; + 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. - if (contextPath) { - helperName += ' ' + contextPath + ' as ' + keywordName; - } + ## One Way Bindings - Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop); + 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: - var localizedOptions = o_create(options); - localizedOptions.data = o_create(options.data); + ``` + bigTitlesBinding: Ember.Binding.oneWay("MyApp.preferencesController.bigTitles") + ``` - localizedOptions.keywords = {}; - localizedOptions.keywords[keywordName] = view.getStream(contextPath); + 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`. - localizedOptions.hash.keywordName = keywordName; + 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. - bindContext = this; - options = localizedOptions; - 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.'); + 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). - 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); + ## Adding Bindings Manually - helperName += ' ' + contextPath; - bindContext = options.contexts[0]; - preserveContext = false; - } + 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. - options.helperName = helperName; + 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. - return bind.call(bindContext, contextPath, options, preserveContext, exists, undefined, undefined, WithView); - } - }); -enifed("ember-handlebars/helpers/yield", - ["ember-metal/core","ember-metal/property_get","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-handlebars - */ + 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: - var Ember = __dependency1__["default"]; - // var emberAssert = Ember.assert; + ```javascript + binding = Ember.Binding.from("valueBinding").to("value"); + ``` - var get = __dependency2__.get; + 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: - /** - `{{yield}}` denotes an area of a template that will be rendered inside - of another template. It has two main uses: + ```javascript + binding.connect(this); + ``` - ### 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. + 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. - An empty `` and the following application code: + This also allows you to bind between objects using the paths you declare in + `from` and `to`: ```javascript - AView = Ember.View.extend({ - classNames: ['a-view-with-layout'], - layout: Ember.Handlebars.compile('
    {{yield}}
    '), - template: Ember.Handlebars.compile('I am wrapped') - }); + // Example 1 + binding = Ember.Binding.from("App.someObject.value").to("value"); + binding.connect(this); - aView = AView.create(); - aView.appendTo('body'); + // Example 2 + binding = Ember.Binding.from("parentView.value").to("App.someObject.value"); + binding.connect(this); ``` - Will result in the following HTML output: - - ```html - -
    -
    - I am wrapped -
    -
    - - ``` + Now that the binding is connected, it will observe both the from and to side + and relay changes. - 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. + 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): ```javascript - BView = Ember.View.extend({ - classNames: ['a-view-with-layout'], - template: Ember.Handlebars.compile('{{yield}}') - }); - - bView = BView.create(); - bView.appendTo('body'); - - // throws - // Uncaught Error: assertion failed: - // You called yield in a template that was not a layout + Ember.bind(MyApp.anotherObject, "value", "MyApp.someController.value"); ``` - ### 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: + Both of these code fragments have the same effect as doing the most friendly + form of binding creation like so: - ```handlebars - - {{#labeled-textfield value=someProperty}} - First name: - {{/labeled-textfield}} - ``` + ```javascript + MyApp.anotherObject = Ember.Object.create({ + valueBinding: "MyApp.someController.value", - ```handlebars - - + // OTHER CODE FOR THIS OBJECT... + }); ``` - Result: - - ```html - - ``` + 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. - @method yield - @for Ember.Handlebars.helpers - @param {Hash} options - @return {String} HTML string + @class Binding + @namespace Ember + @since Ember 0.9 */ - __exports__["default"] = function yieldHelper(options) { - var view = options.data.view; + // Ember.Binding = Binding; ES6TODO: where to put this? - while (view && !get(view, 'layout')) { - if (view._contextView) { - view = view._contextView; - } else { - view = get(view, '_parentView'); - } - } - Ember.assert("You called yield in a template that was not a layout", !!view); + /** + 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. - view._yield(this, options); + @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); } - }); -enifed("ember-handlebars/loader", - ["ember-handlebars/component_lookup","ember-views/system/jquery","ember-metal/error","ember-runtime/system/lazy_load","ember-handlebars-compiler","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { - "use strict"; - /*globals Handlebars */ - - var ComponentLookup = __dependency1__["default"]; - var jQuery = __dependency2__["default"]; - var EmberError = __dependency3__["default"]; - var onLoad = __dependency4__.onLoad; - - var EmberHandlebars = __dependency5__["default"]; - /** - @module ember - @submodule ember-handlebars + __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); + } - /** - 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. + __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; - 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 Cache(limit, func) { + this.store = dictionary(null); + this.size = 0; + this.misses = 0; + this.hits = 0; + this.limit = limit; + this.func = func; + } - @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"]'; + var UNDEFINED = function() { }; - jQuery(selectors, ctx) - .each(function() { - // Get a reference to the script tag - var script = jQuery(this); + 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; + } + } - var compile = (script.attr('type') === 'text/x-raw-handlebars') ? - jQuery.proxy(Handlebars.compile, Handlebars) : - jQuery.proxy(EmberHandlebars.compile, EmberHandlebars); - // 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()); + return value; + }, - // Check if template of same name already exists - if (Ember.TEMPLATES[templateName] !== undefined) { - throw new EmberError('Template named "' + templateName + '" already exists.'); + 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 } - // For templates which have a name, we save them and then remove them from the DOM - Ember.TEMPLATES[templateName] = template; + return value; + }, - // Remove script tag from DOM - script.remove(); - }); - } + 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; - function _bootstrap() { - bootstrap( jQuery(document) ); - } + var warn = Ember.warn; + var FIRST_KEY = /^([^\.]+)/; - function registerComponentLookup(container) { - container.register('component-lookup:main', ComponentLookup); + function firstKey(path) { + return path.match(FIRST_KEY)[0]; } - /* - 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. + var pendingQueue = []; - There's no harm to running this twice, since we remove the templates - from the DOM after processing. - */ + // 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 - onLoad('Ember.Application', function(Application) { - Application.initializer({ - name: 'domTemplates', - initialize: _bootstrap - }); + var queue = pendingQueue; + pendingQueue = []; - Application.initializer({ - name: 'registerComponentLookup', - after: 'domTemplates', - initialize: registerComponentLookup + forEach.call(queue, function(q) { + q[0].add(q[1]); }); - }); - __exports__["default"] = bootstrap; - }); -enifed("ember-handlebars/string", - ["ember-runtime/system/string","exports"], - function(__dependency1__, __exports__) { - "use strict"; - // required so we can extend this object. - var EmberStringUtils = __dependency1__["default"]; + 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); + } - /** - Mark a string as safe for unescaped output with Handlebars. If you - return HTML from a Handlebars helper, use this function to - ensure Handlebars does not escape the HTML. + __exports__.flushPendingChains = flushPendingChains;function addChainWatcher(obj, keyName, node) { + if (!obj || ('object' !== typeof obj)) { return; } // nothing to do - ```javascript - Ember.String.htmlSafe('
    someString
    ') - ``` + var m = metaFor(obj); + var nodes = m.chainWatchers; - @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 ""; + if (!m.hasOwnProperty('chainWatchers')) { + nodes = m.chainWatchers = {}; } - if (typeof str !== 'string') { - str = ''+str; + if (!nodes[keyName]) { + nodes[keyName] = []; } - return new Handlebars.SafeString(str); + nodes[keyName].push(node); + watchKey(obj, keyName, m); } - EmberStringUtils.htmlSafe = htmlSafe; - if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { + function removeChainWatcher(obj, keyName, node) { + if (!obj || 'object' !== typeof obj) { return; } // nothing to do - /** - Mark a string as being safe for unescaped output with Handlebars. + var m = obj['__ember_meta__']; + if (m && !m.hasOwnProperty('chainWatchers')) { return; } // nothing to do - ```javascript - '
    someString
    '.htmlSafe() - ``` + var nodes = m && m.chainWatchers; - See [Ember.String.htmlSafe](/api/classes/Ember.String.html#method_htmlSafe). - - @method htmlSafe - @for String - @return {Handlebars.SafeString} a string that will not be html escaped by Handlebars - */ - String.prototype.htmlSafe = function() { - return htmlSafe(this); - }; + 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); } - __exports__["default"] = htmlSafe; - }); -enifed("ember-handlebars/views/handlebars_bound_view", - ["ember-handlebars-compiler","ember-metal/core","ember-metal/error","ember-metal/property_get","ember-metal/property_set","ember-metal/merge","ember-metal/run_loop","ember-handlebars/string","ember-views/views/states","ember-handlebars/views/metamorph_view","ember-metal/utils","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { - "use strict"; - /*jshint newcap:false*/ - - - /** - @module ember - @submodule ember-handlebars - */ - - var EmberHandlebars = __dependency1__["default"]; - // EmberHandlebars.SafeString; - - var Ember = __dependency2__["default"]; - // Ember.K - var K = Ember.K; + // 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; - var EmberError = __dependency3__["default"]; - var get = __dependency4__.get; - var set = __dependency5__.set; - var merge = __dependency6__["default"]; - var run = __dependency7__["default"]; - var htmlSafe = __dependency8__["default"]; - var cloneStates = __dependency9__.cloneStates; - var viewStates = __dependency9__.states; + // _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; - var _MetamorphView = __dependency10__["default"]; - var uuid = __dependency11__.uuid; + this._value = value; + this._paths = {}; + if (this._watching) { + this._object = parent.value(); + if (this._object) { + addChainWatcher(this._object, this._key, this); + } + } - function SimpleHandlebarsView(lazyValue, isEscaped) { - this.lazyValue = lazyValue; - this.isEscaped = isEscaped; - this[Ember.GUID_KEY] = uuid(); - this._lastNormalizedValue = undefined; - this.state = 'preRender'; - this.updateId = null; - this._parentView = null; - this.buffer = null; - this._morph = 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(); + } } - SimpleHandlebarsView.prototype = { - isVirtual: true, - isView: true, - - destroy: function () { - if (this.updateId) { - run.cancel(this.updateId); - this.updateId = null; - } - if (this._parentView) { - this._parentView.removeChild(this); - } - this.morph = null; - this.state = 'destroyed'; - }, + var ChainNodePrototype = ChainNode.prototype; - propertyWillChange: K, + function lazyGet(obj, key) { + if (!obj) return undefined; - propertyDidChange: K, + var meta = obj['__ember_meta__']; + // check if object meant only to be a prototype + if (meta && meta.proto === obj) { + return undefined; + } - normalizedValue: function() { - var result = this.lazyValue.value(); + if (key === "@each") { + return get(obj, key); + } - if (result === null || result === undefined) { - result = ""; - } else if (!this.isEscaped && !(result instanceof EmberHandlebars.SafeString)) { - result = htmlSafe(result); + // 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; } + } - return result; - }, + return get(obj, key); + } - render: function(buffer) { - var value = this.normalizedValue(); - this._lastNormalizedValue = value; - buffer._element = value; - }, + ChainNodePrototype.value = function() { + if (this._value === undefined && this._watching) { + var obj = this._parent.value(); + this._value = lazyGet(obj, this._key); + } + return this._value; + }; - rerender: function() { - switch(this.state) { - case 'preRender': - case 'destroyed': - break; - case 'inBuffer': - throw new EmberError("Something you did tried to replace an {{expression}} before it was inserted into the DOM."); - case 'hasElement': - case 'inDOM': - this.updateId = run.scheduleOnce('render', this, 'update'); - break; + ChainNodePrototype.destroy = function() { + if (this._watching) { + var obj = this._object; + if (obj) { + removeChainWatcher(obj, this._key, this); } - return this; - }, + this._watching = false; // so future calls do nothing + } + }; - update: function () { - this.updateId = null; - var value = this.normalizedValue(); - // doesn't diff EmberHandlebars.SafeString instances - if (value !== this._lastNormalizedValue) { - this._lastNormalizedValue = value; - this._morph.update(value); - } - }, + // copies a top level object only + ChainNodePrototype.copy = function(obj) { + var ret = new ChainNode(null, null, obj); + var paths = this._paths; + var path; - _transitionTo: function(state) { - this.state = state; + for (path in paths) { + // this check will also catch non-number vals. + if (paths[path] <= 0) { + continue; + } + ret.add(path); } + return ret; }; - var states = cloneStates(viewStates); + // 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; - merge(states._default, { - rerenderIfNeeded: K - }); + paths = this._paths; + paths[path] = (paths[path] || 0) + 1; - merge(states.inDOM, { - rerenderIfNeeded: function(view) { - if (view.normalizedValue() !== view._lastNormalizedValue) { - view.rerender(); - } - } - }); + obj = this.value(); + tuple = normalizeTuple(obj, path); - /** - `Ember._HandlebarsBoundView` is a private view created by the Handlebars - `{{bind}}` helpers that is used to keep track of bound properties. + // the path was a local path + if (tuple[0] && tuple[0] === obj) { + path = tuple[1]; + key = firstKey(path); + path = path.slice(key.length+1); - Every time a property is bound using a `{{mustache}}`, an anonymous subclass - of `Ember._HandlebarsBoundView` is created with the appropriate sub-template - and context set up. When the associated property changes, just the template - for this view will re-render. + // 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; - @class _HandlebarsBoundView - @namespace Ember - @extends Ember._MetamorphView - @private - */ - var _HandlebarsBoundView = _MetamorphView.extend({ - instrumentName: 'boundHandlebars', + // global path, and object already exists + } else { + src = tuple[0]; + key = path.slice(0, 0-(tuple[1].length+1)); + path = tuple[1]; + } - _states: states, + tuple.length = 0; + this.chain(key, path, src); + }; - /** - The function used to determine if the `displayTemplate` or - `inverseTemplate` should be rendered. This should be a function that takes - a value and returns a Boolean. + // 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; - @property shouldDisplayFunc - @type Function - @default null - */ - shouldDisplayFunc: null, + paths = this._paths; + if (paths[path] > 0) { + paths[path]--; + } - /** - Whether the template rendered by this view gets passed the context object - of its parent template, or gets passed the value of retrieving `path` - from the `pathRoot`. + 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]; + } - For example, this is true when using the `{{#if}}` helper, because the - template inside the helper should look up properties relative to the same - object as outside the block. This would be `false` when used with `{{#with - foo}}` because the template should receive the object found by evaluating - `foo`. + tuple.length = 0; + this.unchain(key, path); + }; - @property preserveContext - @type Boolean - @default false - */ - preserveContext: false, + ChainNodePrototype.count = 0; - /** - If `preserveContext` is true, this is the object that will be used - to render the template. + ChainNodePrototype.chain = function(key, path, src) { + var chains = this._chains; + var node; + if (!chains) { + chains = this._chains = {}; + } - @property previousContext - @type Object - */ - previousContext: null, + node = chains[key]; + if (!node) { + node = chains[key] = new ChainNode(this, key, src); + } + node.count++; // count chains... - /** - The template to render when `shouldDisplayFunc` evaluates to `true`. + // 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... + } + }; - @property displayTemplate - @type Function - @default null - */ - displayTemplate: null, + ChainNodePrototype.unchain = function(key, path) { + var chains = this._chains; + var node = chains[key]; - /** - The template to render when `shouldDisplayFunc` evaluates to `false`. + // 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); + } - @property inverseTemplate - @type Function - @default null - */ - inverseTemplate: null, + // delete node if needed. + node.count--; + if (node.count<=0) { + delete chains[node._key]; + node.destroy(); + } - lazyValue: null, + }; - normalizedValue: function() { - var value = this.lazyValue.value(); - var valueNormalizer = get(this, 'valueNormalizerFunc'); - return valueNormalizer ? valueNormalizer(value) : value; - }, + ChainNodePrototype.willChange = function(events) { + var chains = this._chains; + if (chains) { + for(var key in chains) { + if (!chains.hasOwnProperty(key)) { + continue; + } + chains[key].willChange(events); + } + } - rerenderIfNeeded: function() { - this.currentState.rerenderIfNeeded(this); - }, + if (this._parent) { + this._parent.chainWillChange(this, this._key, 1, events); + } + }; - /** - Determines which template to invoke, sets up the correct state based on - that logic, then invokes the default `Ember.View` `render` implementation. + ChainNodePrototype.chainWillChange = function(chain, path, depth, events) { + if (this._key) { + path = this._key + '.' + path; + } - This method will first look up the `path` key on `pathRoot`, - then pass that value to the `shouldDisplayFunc` function. If that returns - `true,` the `displayTemplate` function will be rendered to DOM. Otherwise, - `inverseTemplate`, if specified, will be rendered. + 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); + } + } + }; - For example, if this `Ember._HandlebarsBoundView` represented the `{{#with - foo}}` helper, it would look up the `foo` property of its context, and - `shouldDisplayFunc` would always return true. The object found by looking - up `foo` would be passed to `displayTemplate`. + ChainNodePrototype.chainDidChange = function(chain, path, depth, events) { + if (this._key) { + path = this._key + '.' + path; + } - @method render - @param {Ember.RenderBuffer} buffer - */ - render: function(buffer) { - // If not invoked via a triple-mustache ({{{foo}}}), escape - // the content of the template. - var escape = get(this, 'isEscaped'); + 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); + } + } + }; - var shouldDisplay = get(this, 'shouldDisplayFunc'); - var preserveContext = get(this, 'preserveContext'); - var context = get(this, 'previousContext'); + 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; - var inverseTemplate = get(this, 'inverseTemplate'); - var displayTemplate = get(this, 'displayTemplate'); + // Special-case: the EachProxy relies on immediate evaluation to + // establish its observers. + if (this._parent && this._parent._key === '@each') { + this.value(); + } + } - var result = this.normalizedValue(); + // then notify chains... + var chains = this._chains; + if (chains) { + for(var key in chains) { + if (!chains.hasOwnProperty(key)) { continue; } + chains[key].didChange(events); + } + } - this._lastNormalizedValue = result; + // if no events are passed in then we only care about the above wiring update + if (events === null) { + return; + } - // First, test the conditional to see if we should - // render the template or not. - if (shouldDisplay(result)) { - set(this, 'template', displayTemplate); + // and finally tell parent about my path changing... + if (this._parent) { + this._parent.chainDidChange(this, this._key, 1, events); + } + }; - // If we are preserving the context (for example, if this - // is an #if block, call the template with the same object. - if (preserveContext) { - set(this, '_context', context); - } else { - // Otherwise, determine if this is a block bind or not. - // If so, pass the specified object to the template - if (displayTemplate) { - set(this, '_context', result); - } else { - // This is not a bind block, just push the result of the - // expression to the render context and return. - if (result === null || result === undefined) { - result = ""; - } else if (!(result instanceof EmberHandlebars.SafeString)) { - result = String(result); - } + function finishChains(obj) { + // We only create meta if we really have to + var m = obj['__ember_meta__']; + var chains, chainWatchers, chainNodes; - if (escape) { result = Handlebars.Utils.escapeExpression(result); } - buffer.push(result); - return; + 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; } - } - } else if (inverseTemplate) { - set(this, 'template', inverseTemplate); - if (preserveContext) { - set(this, '_context', context); - } else { - set(this, '_context', result); + chainNodes = chainWatchers[key]; + if (chainNodes) { + for (var i=0,l=chainNodes.length;i=0;i--) { - child = children[i]; - index = total++; - views[index] = child; - queue[length++] = index; - view = child; - } - } + // setter + } else { + var name = value.split(' '); - index = queue[--length]; - view = views[index]; + this.set('firstName', name[0]); + this.set('lastName', name[1]); - while (parentIndex === index) { - level--; - view._elementCreated = true; - this.didCreateElement(view); - if (willInsert) { - this.willInsertElement(view); + return value; } + }.property('firstName', 'lastName') + }); - if (level === 0) { - length--; - break; - } + var person = Person.create(); - 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; - } - } + person.set('fullName', 'Peter Wagenet'); + person.get('firstName'); // 'Peter' + person.get('lastName'); // 'Wagenet' + ``` - this.insertElement(view, _parentView, element, insertAt); + @class ComputedProperty + @namespace Ember + @extends Ember.Descriptor + @constructor + */ + function ComputedProperty(func, opts) { + func.__ember_arity__ = func.length; + this.func = func; - for (i=total-1; i>=0; i--) { - if (willInsert) { - views[i]._elementInserted = true; - this.didInsertElement(views[i]); - } - views[i] = null; - } + this._dependentKeys = undefined; + this._suspended = undefined; + this._meta = undefined; - return element; + Ember.deprecate("Passing opts.cacheable to the CP constructor is deprecated. Invoke `volatile()` on the CP instead.", !opts || !opts.hasOwnProperty('cacheable')); + this._cacheable = (opts && opts.cacheable !== undefined) ? opts.cacheable : true; // TODO: Set always to `true` once this deprecation is gone. + this._dependentKeys = opts && opts.dependentKeys; + Ember.deprecate("Passing opts.readOnly to the CP constructor is deprecated. All CPs are writable by default. Yo can invoke `readOnly()` on the CP to change this.", !opts || !opts.hasOwnProperty('readOnly')); + this._readOnly = opts && (opts.readOnly !== undefined || !!opts.readOnly) || false; // TODO: Set always to `false` once this deprecation is gone. } - 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; + ComputedProperty.prototype = new Descriptor(); + + var ComputedPropertyPrototype = ComputedProperty.prototype; + + /** + Properties are cacheable by default. Computed property will automatically + cache the return value of your function until one of the dependent keys changes. + + Call `volatile()` to set it into non-cached mode. When in this mode + the computed property will not automatically cache the return value. + + However, if a property is properly observable, there is no reason to disable + caching. + + @method cacheable + @param {Boolean} aFlag optional set to `false` to disable caching + @return {Ember.ComputedProperty} this + @chainable + @deprecated All computed properties are cacheble by default. Use `volatile()` instead to opt-out to caching. + */ + ComputedPropertyPrototype.cacheable = function(aFlag) { + Ember.deprecate('ComputedProperty.cacheable() is deprecated. All computed properties are cacheable by default.'); + this._cacheable = aFlag !== false; + return this; }; - 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); - }); - }; + /** + 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. - Renderer.prototype.appendTo = - function Renderer_appendTo(view, target) { - var morph = this._dom.appendMorph(target); - this.scheduleInsert(view, morph); - }; + ```javascript + var outsideService = Ember.Object.extend({ + value: function() { + return OutsideService.getValue(); + }.property().volatile() + }).create(); + ``` - Renderer.prototype.replaceIn = - function Renderer_replaceIn(view, target) { - var morph = this._dom.createMorph(target, null, null); - this.scheduleInsert(view, morph); - }; + @method volatile + @return {Ember.ComputedProperty} this + @chainable + */ + ComputedPropertyPrototype["volatile"] = function() { + this._cacheable = false; + return this; + }; - function Renderer_remove(_view, shouldDestroy, reset) { - var viewId = this.uuid(_view); + /** + 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._inserts[viewId]) { - this.cancelRender(this._inserts[viewId]); - this._inserts[viewId] = undefined; - } + ```javascript + var Person = Ember.Object.extend({ + guid: function() { + return 'guid-guid-guid'; + }.property().readOnly() + }); - if (!_view._elementCreated) { - return; - } + var person = Person.create(); - var removeQueue = []; - var destroyQueue = []; - var morph = _view._morph; - var idx, len, view, queue, childViews, i, l; + person.set('guid', 'new-guid'); // will throw an exception + ``` - removeQueue.push(_view); + @method readOnly + @return {Ember.ComputedProperty} this + @chainable + */ + ComputedPropertyPrototype.readOnly = function(readOnly) { + Ember.deprecate('Passing arguments to ComputedProperty.readOnly() is deprecated.', arguments.length === 0); + this._readOnly = readOnly === undefined || !!readOnly; // Force to true once this deprecation is gone + return this; + }; - for (idx=0; idx 1) { + args = a_slice.call(arguments); + func = args.pop(); + } - __exports__["default"] = Ember; - }); -enifed("ember-metal/alias", - ["ember-metal/property_get","ember-metal/property_set","ember-metal/core","ember-metal/error","ember-metal/properties","ember-metal/computed","ember-metal/platform","ember-metal/utils","ember-metal/dependent_keys","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __exports__) { - "use strict"; - var get = __dependency1__.get; - var set = __dependency2__.set; - var Ember = __dependency3__["default"]; - // Ember.assert - var EmberError = __dependency4__["default"]; - var Descriptor = __dependency5__.Descriptor; - var defineProperty = __dependency5__.defineProperty; - var ComputedProperty = __dependency6__.ComputedProperty; - var create = __dependency7__.create; - var meta = __dependency8__.meta; - var inspect = __dependency8__.inspect; - var addDependentKeys = __dependency9__.addDependentKeys; - var removeDependentKeys = __dependency9__.removeDependentKeys; + if (typeof func !== "function") { + throw new EmberError("Computed Property declared without a property function"); + } - __exports__["default"] = function alias(altKey) { - return new AliasedProperty(altKey); - } + var cp = new ComputedProperty(func); - function AliasedProperty(altKey) { - this.altKey = altKey; - this._dependentKeys = [ altKey ]; - } + if (args) { + cp.property.apply(cp, args); + } - __exports__.AliasedProperty = AliasedProperty;AliasedProperty.prototype = create(Descriptor.prototype); + return cp; + } - AliasedProperty.prototype.get = function AliasedProperty_get(obj, keyName) { - return get(obj, this.altKey); - }; + /** + 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. - AliasedProperty.prototype.set = function AliasedProperty_set(obj, keyName, value) { - return set(obj, this.altKey, value); - }; + @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]; - AliasedProperty.prototype.willWatch = function(obj, keyName) { - addDependentKeys(this, obj, keyName, meta(obj)); - }; + if (ret === UNDEFINED) { + return undefined; + } + return ret; + } - AliasedProperty.prototype.didUnwatch = function(obj, keyName) { - removeDependentKeys(this, obj, keyName, meta(obj)); + cacheFor.set = function(cache, key, value) { + if (value === undefined) { + cache[key] = UNDEFINED; + } else { + cache[key] = 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); + cacheFor.get = function(cache, key) { + var ret = cache[key]; + if (ret === UNDEFINED) { + return undefined; } + return ret; }; - 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; + cacheFor.remove = function(cache, key) { + cache[key] = undefined; }; - 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; + __exports__.ComputedProperty = ComputedProperty; + __exports__.computed = computed; + __exports__.cacheFor = cacheFor; }); -enifed("ember-metal/array", - ["exports"], - function(__exports__) { +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"]; + /** @module ember-metal */ - var ArrayPrototype = Array.prototype; - - // 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; - }; + var a_slice = [].slice; - var defineNativeShim = function(nativeFunc, shim) { - if (isNativeFunc(nativeFunc)) { - return nativeFunc; + function getProperties(self, propertyNames) { + var ret = {}; + for(var i = 0; i < propertyNames.length; i++) { + ret[propertyNames[i]] = get(self, propertyNames[i]); } - return shim; - }; - - // From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/map - var map = defineNativeShim(ArrayPrototype.map, function(fun /*, thisp */) { - //"use strict"; + return ret; + } - if (this === void 0 || this === null || typeof fun !== "function") { - throw new TypeError(); - } + function registerComputed(name, macro) { + computed[name] = function(dependentKey) { + var args = a_slice.call(arguments); + return computed(dependentKey, function() { + return macro.apply(this, args); + }); + }; + } - var t = Object(this); - var len = t.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; + function registerComputedWithProperties(name, macro) { + computed[name] = function() { + var properties = a_slice.call(arguments); - for (var i = 0; i < len; i++) { - if (i in t) { - res[i] = fun.call(thisp, t[i], i, t); - } - } + var computedFunc = computed(function() { + return macro.apply(this, [getProperties(this, properties)]); + }); - return res; - }); + return computedFunc.property.apply(computedFunc, properties); + }; + } - // From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/foreach - var forEach = defineNativeShim(ArrayPrototype.forEach, function(fun /*, thisp */) { - //"use strict"; + /** + A computed property that returns true if the value of the dependent + property is null, an empty string, empty array, or empty function. - if (this === void 0 || this === null || typeof fun !== "function") { - throw new TypeError(); - } + Example - var t = Object(this); - var len = t.length >>> 0; - var thisp = arguments[1]; + ```javascript + var ToDoList = Ember.Object.extend({ + isDone: Ember.computed.empty('todos') + }); - for (var i = 0; i < len; i++) { - if (i in t) { - fun.call(thisp, t[i], i, t); - } - } - }); + var todoList = ToDoList.create({ + todos: ['Unit Test', 'Documentation', 'Release'] + }); - 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); - } + todoList.get('isDone'); // false + todoList.get('todos').clear(); + todoList.get('isDone'); // true + ``` - for (var i = fromIndex, j = this.length; i < j; i++) { - if (this[i] === obj) { - return i; - } - } - return -1; - }); + @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)); + }); + }; - var lastIndexOf = defineNativeShim(ArrayPrototype.lastIndexOf, function(obj, fromIndex) { - var len = this.length; - var idx; + /** + 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 (fromIndex === undefined) fromIndex = len-1; - else fromIndex = (fromIndex < 0) ? Math.ceil(fromIndex) : Math.floor(fromIndex); - if (fromIndex < 0) fromIndex += len; + Example - for(idx = fromIndex;idx>=0;idx--) { - if (this[idx] === obj) return idx ; - } - return -1; - }); + ```javascript + var Hamster = Ember.Object.extend({ + hasStuff: Ember.computed.notEmpty('backpack') + }); - var filter = defineNativeShim(ArrayPrototype.filter, function (fn, context) { - var i, value; - var result = []; - var length = this.length; + var hamster = Hamster.create({ backpack: ['Food', 'Sleeping Bag', 'Tent'] }); - 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; - }); + hamster.get('hasStuff'); // true + hamster.get('backpack').clear(); // [] + hamster.get('hasStuff'); // false + ``` - 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; - } + @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)); + }); + }; /** - Array polyfills to support ES5 features in older browsers. - - @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; + 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. + Example - // ES6TODO: where is Ember.lookup defined? - /** - @module ember-metal - */ + ```javascript + var Hamster = Ember.Object.extend({ + isHungry: Ember.computed.none('food') + }); - // .......................................................... - // CONSTANTS - // + var hamster = Hamster.create(); - /** - 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. + hamster.get('isHungry'); // true + hamster.set('food', 'Banana'); + hamster.get('isHungry'); // false + hamster.set('food', null); + hamster.get('isHungry'); // true + ``` - @property LOG_BINDINGS + @method computed.none @for Ember - @type Boolean - @default false + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which + returns true if original value for property is null or undefined. */ - Ember.LOG_BINDINGS = false || !!Ember.ENV.LOG_BINDINGS; + registerComputed('none', function(dependentKey) { + return isNone(get(this, dependentKey)); + }); /** - Returns true if the provided path is global (e.g., `MyApp.fooController.bar`) - instead of local (`foo.bar.baz`). + A computed property that returns the inverse boolean value + of the original value for the dependent property. - @method isGlobalPath - @for Ember - @private - @param {String} path - @return Boolean - */ + Example - function getWithGlobals(obj, path) { - return get(isGlobalPath(path) ? Ember.lookup : obj, path); - } + ```javascript + var User = Ember.Object.extend({ + isAnonymous: Ember.computed.not('loggedIn') + }); - // .......................................................... - // BINDING - // + var user = User.create({loggedIn: false}); - function Binding(toPath, fromPath) { - this._direction = undefined; - this._from = fromPath; - this._to = toPath; - this._readyToSync = undefined; - this._oneWay = undefined; - } + user.get('isAnonymous'); // true + user.set('loggedIn', true); + user.get('isAnonymous'); // false + ``` - /** - @class Binding - @namespace Ember + @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); + }); - Binding.prototype = { - /** - This copies the Binding so it can be connected to another object. - - @method copy - @return {Ember.Binding} `this` - */ - copy: function () { - var copy = new Binding(this._to, this._from); - if (this._oneWay) { copy._oneWay = true; } - return copy; - }, - - // .......................................................... - // CONFIG - // + /** + A computed property that converts the provided dependent property + into a boolean value. - /** - 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. + ```javascript + var Hamster = Ember.Object.extend({ + hasBananas: Ember.computed.bool('numBananas') + }); - 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. + var hamster = Hamster.create(); - @method from - @param {String} path the property path to connect to - @return {Ember.Binding} `this` - */ - from: function(path) { - this._from = path; - return this; - }, + 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 + ``` - /** - 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. + @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); + }); - 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. + /** + 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. - @method to - @param {String|Tuple} path A property path or tuple - @return {Ember.Binding} `this` - */ - to: function(path) { - this._to = path; - return this; - }, + Example - /** - 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. + ```javascript + var User = Ember.Object.extend({ + hasValidEmail: Ember.computed.match('email', /^.+@.+\..+$/) + }); - @method oneWay - @return {Ember.Binding} `this` - */ - oneWay: function() { - this._oneWay = true; - return this; - }, + var user = User.create({loggedIn: false}); - /** - @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; - }, + user.get('hasValidEmail'); // false + user.set('email', ''); + user.get('hasValidEmail'); // false + user.set('email', 'ember_hamster@example.com'); + user.get('hasValidEmail'); // true + ``` - // .......................................................... - // CONNECT AND SYNC - // + @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; + }); - /** - 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. + /** + A computed property that returns true if the provided dependent property + is equal to the given value. - @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); + Example - var fromPath = this._from; - var toPath = this._to; - trySet(obj, toPath, getWithGlobals(obj, fromPath)); + ```javascript + var Hamster = Ember.Object.extend({ + napTime: Ember.computed.equal('state', 'sleepy') + }); - // add an observer on the object to be notified when the binding should be updated - addObserver(obj, fromPath, this, this.fromDidChange); + var hamster = Hamster.create(); - // 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); - } + hamster.get('napTime'); // false + hamster.set('state', 'sleepy'); + hamster.get('napTime'); // true + hamster.set('state', 'hungry'); + hamster.get('napTime'); // false + ``` - this._readyToSync = true; + @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; + }); - return this; - }, + /** + A computed property that returns true if the provided dependent property + is greater than the provided value. - /** - Disconnects the binding instance. Changes will no longer be relayed. You - will not usually need to call this method. + Example - @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); + ```javascript + var Hamster = Ember.Object.extend({ + hasTooManyBananas: Ember.computed.gt('numBananas', 10) + }); - var twoWay = !this._oneWay; + var hamster = Hamster.create(); - // 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); + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 3); + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 11); + hamster.get('hasTooManyBananas'); // true + ``` - // if the binding is two-way, remove the observer from the target as well - if (twoWay) { - removeObserver(obj, this._to, this, this.toDidChange); - } + @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; + }); - this._readyToSync = false; // disable scheduled syncs... - return this; - }, + /** + A computed property that returns true if the provided dependent property + is greater than or equal to the provided value. - // .......................................................... - // PRIVATE - // + Example - /* called when the from side changes */ - fromDidChange: function(target) { - this._scheduleSync(target, 'fwd'); - }, + ```javascript + var Hamster = Ember.Object.extend({ + hasTooManyBananas: Ember.computed.gte('numBananas', 10) + }); - /* called when the to side changes */ - toDidChange: function(target) { - this._scheduleSync(target, 'back'); - }, + var hamster = Hamster.create(); - _scheduleSync: function(obj, dir) { - var existingDir = this._direction; + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 3); + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 10); + hamster.get('hasTooManyBananas'); // true + ``` - // if we haven't scheduled the binding yet, schedule it - if (existingDir === undefined) { - run.schedule('sync', this, this._sync, obj); - this._direction = dir; - } + @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 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'; - } - }, + /** + A computed property that returns true if the provided dependent property + is less than the provided value. - _sync: function(obj) { - var log = Ember.LOG_BINDINGS; + Example - // don't synchronize destroyed objects or disconnected bindings - if (obj.isDestroyed || !this._readyToSync) { return; } + ```javascript + var Hamster = Ember.Object.extend({ + needsMoreBananas: Ember.computed.lt('numBananas', 3) + }); - // get the direction of the binding for the object we are - // synchronizing from - var direction = this._direction; + var hamster = Hamster.create(); - var fromPath = this._from; - var toPath = this._to; + hamster.get('needsMoreBananas'); // true + hamster.set('numBananas', 3); + hamster.get('needsMoreBananas'); // false + hamster.set('numBananas', 2); + hamster.get('needsMoreBananas'); // true + ``` - this._direction = undefined; + @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; + }); - // 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); - }); - } - } + /** + A computed property that returns true if the provided dependent property + is less than or equal to the provided value. - }; + Example - function mixinProperties(to, from) { - for (var key in from) { - if (from.hasOwnProperty(key)) { - to[key] = from[key]; - } - } - } + ```javascript + var Hamster = Ember.Object.extend({ + needsMoreBananas: Ember.computed.lte('numBananas', 3) + }); - mixinProperties(Binding, { + var hamster = Hamster.create(); - /* - See `Ember.Binding.from`. + hamster.get('needsMoreBananas'); // true + hamster.set('numBananas', 5); + hamster.get('needsMoreBananas'); // false + hamster.set('numBananas', 3); + hamster.get('needsMoreBananas'); // true + ``` - @method from - @static - */ - from: function(from) { - var C = this; - return new C(undefined, from); - }, + @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; + }); - /* - See `Ember.Binding.to`. + /** + A computed property that performs a logical `and` on the + original values for the provided dependent properties. - @method to - @static - */ - to: function(to) { - var C = this; - return new C(to, undefined); - }, + Example - /** - 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. + ```javascript + var Hamster = Ember.Object.extend({ + readyForCamp: Ember.computed.and('hasTent', 'hasBackpack') + }); - See `Binding.oneWay`. + var hamster = Hamster.create(); - @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); - } + hamster.get('readyForCamp'); // false + hamster.set('hasTent', true); + hamster.get('readyForCamp'); // false + hamster.set('hasBackpack', true); + hamster.get('readyForCamp'); // true + ``` + @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; }); + /** - 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. + A computed property which performs a logical `or` on the + original values for the provided dependent properties. - ## Automatic Creation of Bindings with `/^*Binding/`-named Properties + Example - You do not usually create Binding objects directly but instead describe - bindings in your class or object definition using automatic binding - detection. + ```javascript + var Hamster = Ember.Object.extend({ + readyForRain: Ember.computed.or('hasJacket', 'hasUmbrella') + }); - 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"): + var hamster = Hamster.create(); - ``` - valueBinding: "MyApp.someController.title" + hamster.get('readyForRain'); // false + hamster.set('hasJacket', true); + hamster.get('readyForRain'); // true ``` - 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. - - ## One Way Bindings - - 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: + @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; + }); - ``` - bigTitlesBinding: Ember.Binding.oneWay("MyApp.preferencesController.bigTitles") - ``` + /** + A computed property that returns the first truthy value + from a list of dependent properties. - 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`. + Example - 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. + ```javascript + var Hamster = Ember.Object.extend({ + hasClothes: Ember.computed.any('hat', 'shirt') + }); - 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). + var hamster = Hamster.create(); - ## Adding Bindings Manually + hamster.get('hasClothes'); // null + hamster.set('shirt', 'Hawaiian Shirt'); + hamster.get('hasClothes'); // 'Hawaiian Shirt' + ``` - 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. + @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; + }); - 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. + /** + A computed property that returns the array of values + for the provided dependent properties. - 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: + Example ```javascript - binding = Ember.Binding.from("valueBinding").to("value"); - ``` + var Hamster = Ember.Object.extend({ + clothes: Ember.computed.collect('hat', 'shirt') + }); - 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: + var hamster = Hamster.create(); - ```javascript - binding.connect(this); + hamster.get('clothes'); // [null, null] + hamster.set('hat', 'Camp Hat'); + hamster.set('shirt', 'Camp Shirt'); + hamster.get('clothes'); // ['Camp Hat', 'Camp Shirt'] ``` - 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. + @method computed.collect + @for Ember + @param {String} dependentKey* + @return {Ember.ComputedProperty} computed property which maps + values of all passed in properties to an array. + */ + registerComputedWithProperties('collect', function(properties) { + var res = Ember.A(); + for (var key in properties) { + if (properties.hasOwnProperty(key)) { + if (isNone(properties[key])) { + res.push(null); + } else { + res.push(properties[key]); + } + } + } + return res; + }); - This also allows you to bind between objects using the paths you declare in - `from` and `to`: + /** + 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 - // Example 1 - binding = Ember.Binding.from("App.someObject.value").to("value"); - binding.connect(this); - - // Example 2 - binding = Ember.Binding.from("parentView.value").to("App.someObject.value"); - binding.connect(this); - ``` + var Person = Ember.Object.extend({ + name: 'Alex Matchneer', + nomen: Ember.computed.alias('name') + }); - Now that the binding is connected, it will observe both the from and to side - and relay changes. + var alex = Person.create(); - 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): + alex.get('nomen'); // 'Alex Matchneer' + alex.get('name'); // 'Alex Matchneer' - ```javascript - Ember.bind(MyApp.anotherObject, "value", "MyApp.someController.value"); + alex.set('nomen', '@machty'); + alex.get('name'); // '@machty' ``` - Both of these code fragments have the same effect as doing the most friendly - form of binding creation like so: + @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 = alias; + + /** + 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. + + Example ```javascript - MyApp.anotherObject = Ember.Object.create({ - valueBinding: "MyApp.someController.value", + var User = Ember.Object.extend({ + firstName: null, + lastName: null, + nickName: Ember.computed.oneWay('firstName') + }); - // OTHER CODE FOR THIS OBJECT... + var teddy = User.create({ + firstName: 'Teddy', + lastName: 'Zeenny' }); - ``` - 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. + teddy.get('nickName'); // 'Teddy' + teddy.set('nickName', 'TeddyBear'); // 'TeddyBear' + teddy.get('firstName'); // 'Teddy' + ``` - @class Binding - @namespace Ember - @since Ember 0.9 + @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. */ - // Ember.Binding = Binding; ES6TODO: where to put this? - + computed.oneWay = function(dependentKey) { + return alias(dependentKey).oneWay(); + }; /** - 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. + This is a more semantically meaningful alias of `computed.oneWay`, + whose name is somewhat ambiguous as to which direction the data flows. - @method bind + @method computed.reads @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); - } + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which creates a + one way computed property to the original value for property. + */ + computed.reads = computed.oneWay; - __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); - } + /** + 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 propagate back up, as they will replace the value. - __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; + This prevents the reverse flow, and also throws an exception when it occurs. - function Cache(limit, func) { - this.store = dictionary(null); - this.size = 0; - this.misses = 0; - this.hits = 0; - this.limit = limit; - this.func = func; - } + Example - var UNDEFINED = function() { }; + ```javascript + var User = Ember.Object.extend({ + firstName: null, + lastName: null, + nickName: Ember.computed.readOnly('firstName') + }); - 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; - } - } + var teddy = User.create({ + firstName: 'Teddy', + lastName: 'Zeenny' + }); - return value; - }, + teddy.get('nickName'); // 'Teddy' + teddy.set('nickName', 'TeddyBear'); // throws Exception + // throw new Ember.Error('Cannot Set: nickName on: ' );` + teddy.get('firstName'); // 'Teddy' + ``` - get: function(key) { - var value = this.store[key]; + @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 - 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 - } + Example - return value; - }, + ```javascript + var Hamster = Ember.Object.extend({ + wishList: Ember.computed.defaultTo('favoriteFood') + }); - 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 hamster = Hamster.create({ favoriteFood: 'Banana' }); - var warn = Ember.warn; - var FIRST_KEY = /^([^\.]+)/; + hamster.get('wishList'); // 'Banana' + hamster.set('wishList', 'More Unit Tests'); + hamster.get('wishList'); // 'More Unit Tests' + hamster.get('favoriteFood'); // 'Banana' + ``` - function firstKey(path) { - return path.match(FIRST_KEY)[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.'); - var pendingQueue = []; + if (arguments.length === 1) { + return get(this, defaultPath); + } + return newValue != null ? newValue : get(this, defaultPath); + }); + }; - // 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 + /** + 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. - var queue = pendingQueue; - pendingQueue = []; + @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.'); - forEach.call(queue, function(q) { - q[0].add(q[1]); + 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 */ - 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); - } + /** + @module ember + @submodule ember-metal + */ - __exports__.flushPendingChains = flushPendingChains;function addChainWatcher(obj, keyName, node) { - if (!obj || ('object' !== typeof obj)) { return; } // nothing to do + /** + 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. - var m = metaFor(obj); - var nodes = m.chainWatchers; + You can also use the shorthand `Em` instead of `Ember`. - if (!m.hasOwnProperty('chainWatchers')) { - nodes = m.chainWatchers = {}; - } + 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. - if (!nodes[keyName]) { - nodes[keyName] = []; - } - nodes[keyName].push(node); - watchKey(obj, keyName, m); + The core Runtime framework is based on the jQuery API with a number of + performance optimizations. + + @class Ember + @static + @version 1.10.0 + */ + + 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 removeChainWatcher(obj, keyName, node) { - if (!obj || 'object' !== typeof obj) { return; } // nothing to do + // 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; - var m = obj['__ember_meta__']; - if (m && !m.hasOwnProperty('chainWatchers')) { return; } // nothing to do + // aliases needed to keep minifiers from removing the global context + exports.Em = exports.Ember = Ember; - var nodes = m && m.chainWatchers; + // Make sure these are set whether Ember was already defined or not - 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); - } + Ember.isNamespace = true; - // 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; + Ember.toString = function() { return "Ember"; }; - // _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); - } - } + /** + @property VERSION + @type String + @default '1.10.0' + @static + */ + Ember.VERSION = '1.10.0'; - // 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(); - } - } + /** + Standard environmental variables. You can define these in a global `EmberENV` + variable before loading Ember to control various configuration settings. - var ChainNodePrototype = ChainNode.prototype; + For backwards compatibility with earlier versions of Ember the global `ENV` + variable will be used if `EmberENV` is not defined. - function lazyGet(obj, key) { - if (!obj) return undefined; + @property ENV + @type Hash + */ - var meta = obj['__ember_meta__']; - // check if object meant only to be a prototype - if (meta && meta.proto === obj) { - return undefined; - } + 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 (key === "@each") { - return get(obj, key); - } + Ember.config = Ember.config || {}; - // 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; - } - } + // We disable the RANGE API by default for performance reasons + if ('undefined' === typeof Ember.ENV.DISABLE_RANGE_API) { + Ember.ENV.DISABLE_RANGE_API = true; + } - return get(obj, key); + if ("undefined" === typeof MetamorphENV) { + exports.MetamorphENV = {}; } - ChainNodePrototype.value = function() { - if (this._value === undefined && this._watching) { - var obj = this._parent.value(); - this._value = lazyGet(obj, this._key); - } - return this._value; - }; + MetamorphENV.DISABLE_RANGE_API = Ember.ENV.DISABLE_RANGE_API; - 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 - } - }; + /** + Hash of enabled Canary features. Add to this before creating your application. - // copies a top level object only - ChainNodePrototype.copy = function(obj) { - var ret = new ChainNode(null, null, obj); - var paths = this._paths; - var path; + You can also define `EmberENV.FEATURES` if you need to enable features flagged at runtime. - for (path in paths) { - // this check will also catch non-number vals. - if (paths[path] <= 0) { - continue; - } - ret.add(path); - } - return ret; - }; + @class FEATURES + @namespace Ember + @static + @since 1.1.0 + */ - // 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; + Ember.FEATURES = Ember.ENV.FEATURES || {}; - paths = this._paths; - paths[path] = (paths[path] || 0) + 1; + /** + Test that a feature is enabled. Parsed by Ember's build tools to leave + experimental features out of beta/stable builds. - obj = this.value(); - tuple = normalizeTuple(obj, path); + You can define the following configuration options: - // the path was a local path - if (tuple[0] && tuple[0] === obj) { - path = tuple[1]; - key = firstKey(path); - path = path.slice(key.length+1); + * `EmberENV.ENABLE_ALL_FEATURES` - force all features to be enabled. + * `EmberENV.ENABLE_OPTIONAL_FEATURES` - enable any features that have not been explicitly + enabled/disabled. - // 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; + @method isEnabled + @param {String} feature + @return {Boolean} + @for Ember.FEATURES + @since 1.1.0 + */ - // global path, and object already exists + Ember.FEATURES.isEnabled = function(feature) { + var featureValue = Ember.FEATURES[feature]; + + 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 { - src = tuple[0]; - key = path.slice(0, 0-(tuple[1].length+1)); - path = tuple[1]; + return false; } - - tuple.length = 0; - this.chain(key, path, src); }; - // 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; + // .......................................................... + // BOOTSTRAP + // - paths = this._paths; - if (paths[path] > 0) { - paths[path]--; - } + /** + 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. - 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 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 `EmberENV.EXTEND_PROTOTYPES` config to disable it. - tuple.length = 0; - this.unchain(key, path); - }; + @property EXTEND_PROTOTYPES + @type Boolean + @default true + @for Ember + */ + Ember.EXTEND_PROTOTYPES = Ember.ENV.EXTEND_PROTOTYPES; - ChainNodePrototype.count = 0; + if (typeof Ember.EXTEND_PROTOTYPES === 'undefined') { + Ember.EXTEND_PROTOTYPES = true; + } - ChainNodePrototype.chain = function(key, path, src) { - var chains = this._chains; - var node; - if (!chains) { - chains = this._chains = {}; - } + /** + Determines whether Ember logs a full stack trace during deprecation warnings - node = chains[key]; - if (!node) { - node = chains[key] = new ChainNode(this, key, src); - } - node.count++; // count chains... + @property LOG_STACKTRACE_ON_DEPRECATION + @type Boolean + @default true + */ + Ember.LOG_STACKTRACE_ON_DEPRECATION = (Ember.ENV.LOG_STACKTRACE_ON_DEPRECATION !== false); - // 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... - } - }; + /** + Determines whether Ember should add ECMAScript 5 Array shims to older browsers. - ChainNodePrototype.unchain = function(key, path) { - var chains = this._chains; - var node = chains[key]; + @property SHIM_ES5 + @type Boolean + @default Ember.EXTEND_PROTOTYPES + */ + Ember.SHIM_ES5 = (Ember.ENV.SHIM_ES5 === false) ? false : Ember.EXTEND_PROTOTYPES; - // 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); - } + /** + Determines whether Ember logs info about version of used libraries - // delete node if needed. - node.count--; - if (node.count<=0) { - delete chains[node._key]; - node.destroy(); - } + @property LOG_VERSION + @type Boolean + @default true + */ + Ember.LOG_VERSION = (Ember.ENV.LOG_VERSION === false) ? false : true; - }; + /** + Empty function. Useful for some operations. Always returns `this`. - 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 K + @private + @return {Object} + */ + function K() { return this; } + __exports__.K = K; + Ember.K = K; + //TODO: ES6 GLOBAL TODO - if (this._parent) { - this._parent.chainWillChange(this, this._key, 1, events); - } - }; + // Stub out the methods defined by the ember-debug package in case it's not loaded - ChainNodePrototype.chainWillChange = function(chain, path, depth, events) { - if (this._key) { - path = this._key + '.' + path; - } + 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; }; + } - 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"] = Ember; + }); +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 - ChainNodePrototype.chainDidChange = function(chain, path, depth, events) { - if (this._key) { - path = this._key + '.' + path; - } + var o_create = __dependency1__.create; + var watch = __dependency2__.watch; + var unwatch = __dependency2__.unwatch; - 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); - } - } - }; + /** + @module ember-metal + */ - 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; + // .......................................................... + // DEPENDENT KEYS + // - // Special-case: the EachProxy relies on immediate evaluation to - // establish its observers. - if (this._parent && this._parent._key === '@each') { - this.value(); - } - } + // data structure: + // meta.deps = { + // 'depKey': { + // 'keyName': count, + // } + // } - // then notify chains... - var chains = this._chains; - if (chains) { - for(var key in chains) { - if (!chains.hasOwnProperty(key)) { continue; } - chains[key].didChange(events); - } + /* + 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; + } - // if no events are passed in then we only care about the above wiring update - if (events === null) { - return; - } + function metaForDeps(meta) { + return keysForDep(meta, 'deps'); + } - // and finally tell parent about my path changing... - if (this._parent) { - this._parent.chainDidChange(this, this._key, 1, events); + 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; + + depsMeta = metaForDeps(meta); + + 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); } - }; + } - function finishChains(obj) { - // We only create meta if we really have to - var m = obj['__ember_meta__']; - var chains, chainWatchers, chainNodes; + __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 (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; - } + depsMeta = metaForDeps(meta); - chainNodes = chainWatchers[key]; - if (chainNodes) { - for (var i=0,l=chainNodes.length;i size ? size : ends; + if (count <= 0) { count = 0; } - @class ComputedProperty - @namespace Ember - @extends Ember.Descriptor - @constructor - */ - function ComputedProperty(func, opts) { - func.__ember_arity__ = func.length; - this.func = func; + chunk = args.splice(0, size); + chunk = [start, count].concat(chunk); - this._dependentKeys = undefined; - this._suspended = undefined; - this._meta = undefined; + start += size; + ends -= count; - this._cacheable = (opts && opts.cacheable !== undefined) ? opts.cacheable : true; - this._dependentKeys = opts && opts.dependentKeys; - this._readOnly = opts && (opts.readOnly !== undefined || !!opts.readOnly) || false; + ret = ret.concat(splice.apply(array, chunk)); + } + return ret; } - ComputedProperty.prototype = new Descriptor(); + __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); + } + } - var ComputedPropertyPrototype = ComputedProperty.prototype; + __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); + } + }); - /** - Properties are cacheable by default. Computed property will automatically - cache the return value of your function until one of the dependent keys changes. + return result; + } - Call `volatile()` to set it into non-cached mode. When in this mode - the computed property will not automatically cache the return value. - - However, if a property is properly observable, there is no reason to disable - caching. - - @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; + __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; - /** - 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. + var errorProps = [ + 'description', + 'fileName', + 'lineNumber', + 'message', + 'name', + 'number', + 'stack' + ]; - ```javascript - var outsideService = Ember.Object.extend({ - value: function() { - return OutsideService.getValue(); - }.property().volatile() - }).create(); - ``` + /** + A subclass of the JavaScript Error object for use in Ember. - @method volatile - @return {Ember.ComputedProperty} this - @chainable + @class Error + @namespace Ember + @extends Error + @constructor */ - ComputedPropertyPrototype["volatile"] = function() { - return this.cacheable(false); - }; - - /** - 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. + function EmberError() { + var tmp = Error.apply(this, arguments); - ```javascript - var Person = Ember.Object.extend({ - guid: function() { - return 'guid-guid-guid'; - }.property().readOnly() - }); + // 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 person = Person.create(); + EmberError.prototype = create(Error.prototype); - person.set('guid', 'new-guid'); // will throw an exception - ``` + __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 - @method readOnly - @return {Ember.ComputedProperty} this - @chainable + /** + @module ember-metal */ - ComputedPropertyPrototype.readOnly = function(readOnly) { - this._readOnly = readOnly === undefined || !!readOnly; - return this; - }; + var Ember = __dependency1__["default"]; + var metaFor = __dependency2__.meta; + var tryFinally = __dependency2__.tryFinally; + var apply = __dependency2__.apply; + var applyStr = __dependency2__.applyStr; + var create = __dependency3__.create; - /** - Sets the dependent keys on this computed property. Pass any number of - arguments containing key paths that this computed property depends on. + var a_slice = [].slice; - ```javascript - var President = Ember.Object.extend({ - fullName: computed(function() { - return this.get('firstName') + ' ' + this.get('lastName'); + /* listener flags */ + var ONCE = 1; + var SUSPENDED = 2; - // Tell Ember that this computed property depends on firstName - // and lastName - }).property('firstName', 'lastName') - }); - var president = President.create({ - firstName: 'Barack', - lastName: 'Obama' - }); + /* + 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. - president.get('fullName'); // 'Barack Obama' - ``` + The hashes are stored in the object's meta hash, and look like this: + + // Object's meta hash + { + listeners: { // variable name: `listenerSet` + "foo:changed": [ // variable name: `actions` + target, method, flags + ] + } + } - @method property - @param {String} path* zero or more property paths - @return {Ember.ComputedProperty} this - @chainable */ - ComputedPropertyPrototype.property = function() { - var args; - var addArg = function (property) { - args.push(property); - }; + 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; + } - args = []; - for (var i = 0, l = arguments.length; i < l; i++) { - expandProperties(arguments[i], addArg); + function actionsFor(obj, eventName) { + var meta = metaFor(obj, true); + var actions; + var listeners = meta.listeners; + + 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; } - this._dependentKeys = args; - return this; - }; + actions = listeners[eventName]; - /** - 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. + // 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; + } - You can pass a hash of these values to a computed property like this: + return actions; + } - ``` - person: function() { - var personId = this.get('personId'); - return App.Person.create({ id: personId }); - }.property().meta({ type: App.Person }) - ``` + function accumulateListeners(obj, eventName, otherActions) { + var meta = obj['__ember_meta__']; + var actions = meta && meta.listeners && meta.listeners[eventName]; - 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. + if (!actions) { return; } - @method meta - @param {Hash} meta - @chainable - */ + var newActions = []; - ComputedPropertyPrototype.meta = function(meta) { - if (arguments.length === 0) { - return this._meta || {}; - } else { - this._meta = meta; - return this; - } - }; + 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); - /* 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 (actionIndex === -1) { + otherActions.push(target, method, flags); + newActions.push(target, method, flags); } } - }; - function finishChains(chainNodes) - { - for (var i=0, l=chainNodes.length; i= 0; i -= 3) { + _removeListener(actions[i], actions[i+1]); + } + } + } - var person = Person.create(); + /** + Suspend listener during callback. - person.set('fullName', 'Peter Wagenet'); - person.get('firstName'); // 'Peter' - person.get('lastName'); // 'Wagenet' - ``` + 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. - @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. + @method suspendListener + @for Ember + + @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 */ - ComputedPropertyPrototype.set = function computedPropertySetWithSuspend(obj, keyName, value) { - var oldSuspended = this._suspended; + function suspendListener(obj, eventName, target, method, callback) { + if (!method && 'function' === typeof target) { + method = target; + target = null; + } - this._suspended = obj; + var actions = actionsFor(obj, eventName); + var actionIndex = indexOf(actions, target, method); - try { - this._set(obj, keyName, value); - } finally { - this._suspended = oldSuspended; + if (actionIndex !== -1) { + actions[actionIndex+2] |= SUSPENDED; // mark the action as suspended } - }; - ComputedPropertyPrototype._set = function computedPropertySet(obj, keyName, value) { - var cacheable = this._cacheable; - var func = this.func; - var meta = metaFor(obj, cacheable); - var cache = meta.cache; - var hadCachedValue = false; + function tryable() { return callback.call(target); } + function finalizer() { if (actionIndex !== -1) { actions[actionIndex+2] &= ~SUSPENDED; } } - var funcArgLength, cachedValue, ret; + return tryFinally(tryable, finalizer); + } - if (this._readOnly) { - throw new EmberError('Cannot set read-only property "' + keyName + '" on object: ' + inspect(obj)); - } + __exports__.suspendListener = suspendListener;/** + Suspends multiple listeners during a callback. - if (cacheable && cache[keyName] !== undefined) { - if(cache[keyName] !== UNDEFINED) { - cachedValue = cache[keyName]; - } + @method suspendListeners + @for Ember - hadCachedValue = true; + @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; } - // Check if the CP has been wrapped. If it has, use the - // length from the wrapped function. + var suspendedActions = []; + var actionsList = []; + var eventName, actions, i, l; - funcArgLength = func.wrappedFunction ? func.wrappedFunction.__ember_arity__ : func.__ember_arity__; + 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; + } - if (this._cacheable) { delete meta.cache[keyName]; } + __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]; - return null; // no value to restore - }; + return !!(actions && actions.length); + } + __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]; - /** - 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()`. + if (!actions) { return ret; } - 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. - - A computed property defined in this way might look like this: + for (var i = 0, l = actions.length; i < l; i += 3) { + var target = actions[i]; + var method = actions[i+1]; + ret.push([target, method]); + } - ```js - var Person = Ember.Object.extend({ - firstName: 'Betty', - lastName: 'Jones', + return ret; + } - fullName: Ember.computed('firstName', 'lastName', function(key, value) { - return this.get('firstName') + ' ' + this.get('lastName'); + __exports__.listenersFor = listenersFor;/** + Define a property as a function that should be executed when + a specified event or events are triggered. + + + ``` javascript + var Job = Ember.Object.extend({ + logCompleted: Ember.on('completed', function() { + console.log('Job completed!'); }) }); - var client = Person.create(); + var job = Job.create(); - client.get('fullName'); // 'Betty Jones' - - client.set('lastName', 'Fuller'); - client.get('fullName'); // 'Betty Fuller' - ``` + Ember.sendEvent(job, 'completed'); // Logs 'Job completed!' + ``` - _Note: This is the prefered 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 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; + } - 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): + __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; - ```js - fullName: function () { - return this.get('firstName') + ' ' + this.get('lastName'); - }.property('firstName', 'lastName') - ``` + /** + @module ember-metal + */ - @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; + var BRACE_EXPANSION = /^((?:[^\.]*\.)*)\{(.*)\}$/; + var SPLIT_REGEX = /\{|\}/; - if (arguments.length > 1) { - args = a_slice.call(arguments); - func = args.pop(); - } + /** + Expands `pattern`, invoking `callback` for each expansion. - if (typeof func !== "function") { - throw new EmberError("Computed Property declared without a property function"); - } + The only pattern supported is brace-expansion, anything else will be passed + once to `callback` directly. - var cp = new ComputedProperty(func); + Example - if (args) { - cp.property.apply(cp, args); + ```js + function echo(arg){ console.log(arg); } + + 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' + ``` + + @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}`'); } - return cp; - } + + return newExpandProperties(pattern, callback); + } - /** - 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. + function oldExpandProperties(pattern, callback) { + var match, prefix, list; - @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]; + if (match = BRACE_EXPANSION.exec(pattern)) { + prefix = match[1]; + list = match[2]; - if (ret === UNDEFINED) { - return undefined; + forEach(list.split(','), function (suffix) { + callback(prefix + suffix); + }); + } else { + callback(pattern); } - return ret; } - cacheFor.set = function(cache, key, value) { - if (value === undefined) { - cache[key] = UNDEFINED; + function newExpandProperties(pattern, callback) { + if ('string' === Ember.typeOf(pattern)) { + var parts = pattern.split(SPLIT_REGEX); + var properties = [parts]; + + forEach(parts, function(part, index) { + if (part.indexOf(',') >= 0) { + properties = duplicateAndReplace(properties, part.split(','), index); + } + }); + + forEach(properties, function(property) { + callback(property.join('')); + }); } else { - cache[key] = value; + callback(pattern); } - }; + } - cacheFor.get = function(cache, key) { - var ret = cache[key]; - if (ret === UNDEFINED) { - return undefined; - } - return ret; - }; + function duplicateAndReplace(properties, currentParts, index) { + var all = []; - cacheFor.remove = function(cache, key) { - cache[key] = undefined; - }; + forEach(properties, function(property) { + forEach(currentParts, function(part) { + var current = property.slice(0); + current[index] = part; + all.push(current); + }); + }); - __exports__.ComputedProperty = ComputedProperty; - __exports__.computed = computed; - __exports__.cacheFor = cacheFor; + return all; + } }); -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__) { +enifed("ember-metal/get_properties", + ["ember-metal/property_get","ember-metal/utils","exports"], + function(__dependency1__, __dependency2__, __exports__) { "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"]; + var get = __dependency1__.get; + var typeOf = __dependency2__.typeOf; /** - @module ember-metal - */ + To get multiple properties at once, call `Ember.getProperties` + with an object followed by a list of strings or an array: - var a_slice = [].slice; + ```javascript + Ember.getProperties(record, 'firstName', 'lastName', 'zipCode'); + // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } + ``` - function getProperties(self, propertyNames) { + is equivalent to: + + ```javascript + Ember.getProperties(record, ['firstName', 'lastName', 'zipCode']); + // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } + ``` + + @method getProperties + @for Ember + @param {Object} obj + @param {String...|Array} list of keys to get + @return {Object} + */ + __exports__["default"] = function getProperties(obj) { var ret = {}; - for(var i = 0; i < propertyNames.length; i++) { - ret[propertyNames[i]] = get(self, propertyNames[i]); + 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; - function registerComputed(name, macro) { - computed[name] = function(dependentKey) { - var args = a_slice.call(arguments); - return computed(dependentKey, function() { - return macro.apply(this, args); - }); - }; + /** + Read-only property that returns the result of a container lookup. + + @class InjectedProperty + @namespace Ember + @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 InjectedProperty(type, name) { + this.type = type; + this.name = name; + + this._super$Constructor(injectedPropertyGet); + AliasedPropertyPrototype.oneWay.call(this); } - function registerComputedWithProperties(name, macro) { - computed[name] = function() { - var properties = a_slice.call(arguments); + function injectedPropertyGet(keyName) { + var desc = meta(this).descs[keyName]; - var computedFunc = computed(function() { - return macro.apply(this, [getProperties(this, properties)]); - }); + 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); - return computedFunc.property.apply(computedFunc, properties); - }; + return this.container.lookup(desc.type + ':' + (desc.name || keyName)); } - /** - A computed property that returns true if the value of the dependent - property is null, an empty string, empty array, or empty function. + InjectedProperty.prototype = create(Descriptor.prototype); - Example + var InjectedPropertyPrototype = InjectedProperty.prototype; + var ComputedPropertyPrototype = ComputedProperty.prototype; + var AliasedPropertyPrototype = AliasedProperty.prototype; - ```javascript - var ToDoList = Ember.Object.extend({ - done: Ember.computed.empty('todos') - }); + InjectedPropertyPrototype._super$Constructor = ComputedProperty; - var todoList = ToDoList.create({ - todos: ['Unit Test', 'Documentation', 'Release'] - }); + InjectedPropertyPrototype.get = ComputedPropertyPrototype.get; + InjectedPropertyPrototype.readOnly = ComputedPropertyPrototype.readOnly; - todoList.get('done'); // false - todoList.get('todos').clear(); - todoList.get('done'); // true - ``` + InjectedPropertyPrototype.teardown = ComputedPropertyPrototype.teardown; - @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)); - }); - }; + __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; /** - A computed property that returns true if the value of the dependent - property is NOT null, an empty string, empty array, or empty function. + The purpose of the Ember Instrumentation module is + to provide efficient, general-purpose instrumentation + for Ember. - Example + Subscribe to a listener by using `Ember.subscribe`: ```javascript - var Hamster = Ember.Object.extend({ - hasStuff: Ember.computed.notEmpty('backpack') - }); + Ember.subscribe("render", { + before: function(name, timestamp, payload) { - var hamster = Hamster.create({ backpack: ['Food', 'Sleeping Bag', 'Tent'] }); + }, - hamster.get('hasStuff'); // true - hamster.get('backpack').clear(); // [] - hamster.get('hasStuff'); // false - ``` + after: function(name, timestamp, payload) { - @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)); + } }); - }; + ``` - /** - 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. + If you return a value from the `before` callback, that same + value will be passed as a fourth parameter to the `after` + callback. - Example + Instrument a block of code by using `Ember.instrument`: ```javascript - var Hamster = Ember.Object.extend({ - isHungry: Ember.computed.none('food') - }); + Ember.instrument("render.handlebars", payload, function() { + // rendering logic + }, binding); + ``` - var hamster = Hamster.create(); + 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. - hamster.get('isHungry'); // true - hamster.set('food', 'Banana'); - hamster.get('isHungry'); // false - hamster.set('food', null); - hamster.get('isHungry'); // true - ``` + 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`. - @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. + @class Instrumentation + @namespace Ember + @static */ - registerComputed('none', function(dependentKey) { - return isNone(get(this, dependentKey)); - }); + var subscribers = []; + __exports__.subscribers = subscribers;var cache = {}; - /** - A computed property that returns the inverse boolean value - of the original value for the dependent property. - - Example - - ```javascript - var User = Ember.Object.extend({ - isAnonymous: Ember.computed.not('loggedIn') - }); + var populateListeners = function(name) { + var listeners = []; + var subscriber; - var user = User.create({loggedIn: false}); + for (var i=0, l=subscribers.length; i value; - }); + function subscribe(pattern, object) { + var paths = pattern.split("."), path, regex = []; - /** - A computed property that returns true if the provided dependent property - is greater than or equal to the provided value. + for (var i=0, l=paths.length; i= value; - }); + __exports__.subscribe = subscribe;/** + Unsubscribes from a particular event or instrumented block of code. - /** - A computed property that returns true if the provided dependent property - is less than the provided value. + @method unsubscribe + @namespace Ember.Instrumentation - Example + @param {Object} [subscriber] + */ + function unsubscribe(subscriber) { + var index; - ```javascript - var Hamster = Ember.Object.extend({ - needsMoreBananas: Ember.computed.lt('numBananas', 3) - }); + for (var i=0, l=subscribers.length; i' );` - teddy.get('firstName'); // 'Teddy' - ``` + if (lib) { + index = indexOf(this._registry, lib); + this._registry.splice(index, 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 alias(dependentKey).readOnly(); + 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); + }); + } }; - /** - 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 - Example + __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"]; - ```javascript - var Hamster = Ember.Object.extend({ - wishList: Ember.computed.defaultTo('favoriteFood') - }); + function K() { return this; } - var hamster = Hamster.create({ favoriteFood: 'Banana' }); + function consoleMethod(name) { + var consoleObj, logToConsole; + if (Ember.imports.console) { + consoleObj = Ember.imports.console; + } else if (typeof console !== 'undefined') { + consoleObj = console; + } - hamster.get('wishList'); // 'Banana' - hamster.set('wishList', 'More Unit Tests'); - hamster.get('wishList'); // 'More Unit Tests' - hamster.get('favoriteFood'); // 'Banana' - ``` + var method = typeof consoleObj === 'object' ? consoleObj[name] : null; - @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 (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); + }; + } + } + } - if (arguments.length === 1) { - return get(this, defaultPath); + 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); } - return newValue != null ? newValue : get(this, defaultPath); - }); - }; + } + } /** - 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. + Inside Ember-Metal, simply uses the methods from `imports.console`. + Override this to provide more robust logging functionality. - @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 + @class Logger + @namespace Ember */ - computed.deprecatingAlias = function(dependentKey) { - return computed(dependentKey, function(key, value) { - Ember.deprecate('Usage of `' + key + '` is deprecated, use `' + dependentKey + '` instead.'); + __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. - 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 */ + ```javascript + var foo = 1; + Ember.Logger.log('log value of foo:', foo); + // "log value of foo: 1" will be printed to the console + ``` - /** - @module ember - @submodule ember-metal - */ + @method log + @for Ember.Logger + @param {*} arguments + */ + log: consoleMethod('log') || K, - /** - 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. + /** + 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. - You can also use the shorthand `Em` instead of `Ember`. + ```javascript + Ember.Logger.warn('Something happened!'); + // "Something happened!" will be printed to the console with a warning icon. + ``` - 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. + @method warn + @for Ember.Logger + @param {*} arguments + */ + warn: consoleMethod('warn') || K, - The core Runtime framework is based on the jQuery API with a number of - performance optimizations. + /** + 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. - @class Ember - @static - @version 1.9.1 - */ + ```javascript + Ember.Logger.error('Danger! Danger!'); + // "Danger! Danger!" will be printed to the console in red text. + ``` - 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 = {}; - } + @method error + @for Ember.Logger + @param {*} arguments + */ + error: consoleMethod('error') || K, - // 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; + /** + Logs the arguments to the console. + You can pass as many arguments as you want and they will be joined together with a space. - // aliases needed to keep minifiers from removing the global context - exports.Em = exports.Ember = Ember; + ```javascript + var foo = 1; + Ember.Logger.info('log value of foo:', foo); + // "log value of foo: 1" will be printed to the console + ``` - // Make sure these are set whether Ember was already defined or not + @method info + @for Ember.Logger + @param {*} arguments + */ + info: consoleMethod('info') || K, - Ember.isNamespace = true; + /** + 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. - Ember.toString = function() { return "Ember"; }; + ```javascript + var foo = 1; + Ember.Logger.debug('log value of foo:', foo); + // "log value of foo: 1" will be printed to the console + ``` + + @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"; /** - @property VERSION - @type String - @default '1.9.1' - @static + @module ember-metal */ - Ember.VERSION = '1.9.1'; - /** - Standard environmental variables. You can define these in a global `EmberENV` - variable before loading Ember to control various configuration settings. + /* + JavaScript (before ES6) does not have a Map implementation. Objects, + which are often used as dictionaries, may only have Strings as keys. - For backwards compatibility with earlier versions of Ember the global `ENV` - variable will be used if `EmberENV` is not defined. + 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 ENV - @type Hash - */ + 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. - 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 = {}; - } + Map is mocked out to look like an Ember object, so you can do + `Ember.Map.create()` for symmetry with other Ember classes. + */ - Ember.config = Ember.config || {}; + var guidFor = __dependency1__.guidFor; + var indexOf = __dependency2__.indexOf; + var create = __dependency3__.create; + var deprecateProperty = __dependency4__.deprecateProperty; - // 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 missingFunction(fn) { + throw new TypeError('' + Object.prototype.toString.call(fn) + " is not a function"); } - if ("undefined" === typeof MetamorphENV) { - exports.MetamorphENV = {}; + function missingNew(name) { + throw new TypeError("Constructor " + name + "requires 'new'"); } - MetamorphENV.DISABLE_RANGE_API = Ember.ENV.DISABLE_RANGE_API; + function copyNull(obj) { + var output = create(null); - /** - Hash of enabled Canary features. Add to this before creating your application. + for (var prop in obj) { + // hasOwnPropery is not needed because obj is Object.create(null); + output[prop] = obj[prop]; + } - You can also define `ENV.FEATURES` if you need to enable features flagged at runtime. + return output; + } - @class FEATURES - @namespace Ember - @static - @since 1.1.0 - */ - - Ember.FEATURES = Ember.ENV.FEATURES || {}; + function copyMap(original, newObject) { + var keys = original.keys.copy(); + var values = copyNull(original.values); - /** - Test that a feature is enabled. Parsed by Ember's build tools to leave - experimental features out of beta/stable builds. + newObject.keys = keys; + newObject.values = values; + newObject.size = original.size; - You can define the following configuration options: + return newObject; + } - * `ENV.ENABLE_ALL_FEATURES` - force all features to be enabled. - * `ENV.ENABLE_OPTIONAL_FEATURES` - enable any features that have not been explicitly - enabled/disabled. + /** + 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. - @method isEnabled - @param {String} feature - @return {Boolean} - @for Ember.FEATURES - @since 1.1.0 + @class OrderedSet + @namespace Ember + @constructor + @private */ + function OrderedSet() { - Ember.FEATURES.isEnabled = function(feature) { - var featureValue = Ember.FEATURES[feature]; - - 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; + if (this instanceof OrderedSet) { + this.clear(); + this._silenceRemoveDeprecation = false; } else { - return false; + missingNew("OrderedSet"); } - }; - - // .......................................................... - // 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 + @method create + @static + @return {Ember.OrderedSet} */ - Ember.EXTEND_PROTOTYPES = Ember.ENV.EXTEND_PROTOTYPES; - - if (typeof Ember.EXTEND_PROTOTYPES === 'undefined') { - Ember.EXTEND_PROTOTYPES = true; - } + OrderedSet.create = function() { + var Constructor = this; - /** - Determines whether Ember logs a full stack trace during deprecation warnings + return new Constructor(); + }; - @property LOG_STACKTRACE_ON_DEPRECATION - @type Boolean - @default true - */ - Ember.LOG_STACKTRACE_ON_DEPRECATION = (Ember.ENV.LOG_STACKTRACE_ON_DEPRECATION !== false); + OrderedSet.prototype = { + constructor: OrderedSet, + /** + @method clear + */ + clear: function() { + this.presenceSet = create(null); + this.list = []; + this.size = 0; + }, - /** - Determines whether Ember should add ECMAScript 5 Array shims to older browsers. + /** + @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; - @property SHIM_ES5 - @type Boolean - @default Ember.EXTEND_PROTOTYPES - */ - Ember.SHIM_ES5 = (Ember.ENV.SHIM_ES5 === false) ? false : Ember.EXTEND_PROTOTYPES; + if (presenceSet[guid] === true) { + return; + } - /** - Determines whether Ember logs info about version of used libraries + presenceSet[guid] = true; + this.size = list.push(obj); - @property LOG_VERSION - @type Boolean - @default true - */ - Ember.LOG_VERSION = (Ember.ENV.LOG_VERSION === false) ? false : true; + return this; + }, - /** - Empty function. Useful for some operations. Always returns `this`. + /** + @deprecated - @method K - @private - @return {Object} - */ - function K() { return this; } - __exports__.K = K; - Ember.K = K; - //TODO: ES6 GLOBAL TODO + @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); - // Stub out the methods defined by the ember-debug package in case it's not loaded + return this["delete"](obj, _guid); + }, - 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; }; - } + /** + @since 1.8.0 + @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; - __exports__["default"] = Ember; - }); -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 + 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; + } + }, - var o_create = __dependency1__.create; - var watch = __dependency2__.watch; - var unwatch = __dependency2__.unwatch; + /** + @method isEmpty + @return {Boolean} + */ + isEmpty: function() { + return this.size === 0; + }, - /** - @module ember-metal - */ + /** + @method has + @param obj + @return {Boolean} + */ + has: function(obj) { + if (this.size === 0) { return false; } - // .......................................................... - // DEPENDENT KEYS - // + var guid = guidFor(obj); + var presenceSet = this.presenceSet; - // data structure: - // meta.deps = { - // 'depKey': { - // 'keyName': count, - // } - // } + return presenceSet[guid] === true; + }, - /* - 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; - } + /** + @method forEach + @param {Function} fn + @param self + */ + forEach: function(fn /*, thisArg*/) { + if (typeof fn !== 'function') { + missingFunction(fn); + } - function metaForDeps(meta) { - return keysForDep(meta, 'deps'); - } + if (this.size === 0) { return; } - 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; + var list = this.list; + var length = arguments.length; + var i; - depsMeta = metaForDeps(meta); + 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]); + } + } + }, - 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); - } - } + /** + @method toArray + @return {Array} + */ + toArray: function() { + return this.list.slice(); + }, - __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; + /** + @method copy + @return {Ember.OrderedSet} + */ + copy: function() { + var Constructor = this.constructor; + var set = new Constructor(); - depsMeta = metaForDeps(meta); + set._silenceRemoveDeprecation = this._silenceRemoveDeprecation; + set.presenceSet = copyNull(this.presenceSet); + set.list = this.toArray(); + set.size = this.size; - 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); + return set; } - } + }; + + deprecateProperty(OrderedSet.prototype, 'length', 'size'); - __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 - */ + A Map stores values indexed by keys. Unlike JavaScript's + default Objects, the keys of a Map can be any JavaScript + object. - var Ember = __dependency1__["default"]; - var hasPropertyAccessors = __dependency2__.hasPropertyAccessors; - var defineProperty = __dependency3__.defineProperty; - var get = __dependency4__.get; - var set = __dependency5__.set; + Internally, a Map has two data structures: + 1. `keys`: an OrderedSet of all of the existing keys + 2. `values`: a JavaScript Object indexed by the `Ember.guidFor(key)` - /** - Used internally to allow changing properties in a backwards compatible way, and print a helpful - deprecation warning. + 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 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. + @class Map + @namespace Ember @private - @since 1.7.0 + @constructor */ - - function deprecateProperty(object, deprecatedKey, newKey) { - function deprecate() { - Ember.deprecate('Usage of `' + deprecatedKey + '` is deprecated, use `' + newKey + '` instead.'); - } - - if (hasPropertyAccessors) { - defineProperty(object, deprecatedKey, { - configurable: true, - enumerable: false, - set: function(value) { deprecate(); set(this, newKey, value); }, - get: function() { deprecate(); return get(this, newKey); } - }); + function Map() { + if (this instanceof this.constructor) { + this.keys = OrderedSet.create(); + this.keys._silenceRemoveDeprecation = true; + this.values = create(null); + this.size = 0; + } else { + missingNew("OrderedSet"); } } - __exports__.deprecateProperty = deprecateProperty; - }); -enifed("ember-metal/dictionary", - ["ember-metal/platform","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var create = __dependency1__.create; - - // 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; - - var splice = Array.prototype.splice; - - /** - * Defines some convenience methods for working with Enumerables. - * `Ember.EnumerableUtils` uses `Ember.ArrayPolyfills` when necessary. - * - * @class EnumerableUtils - * @namespace Ember - * @static - * */ + Ember.Map = Map; /** - * 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); - } - - __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); - } - - __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); - } - - __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); - } - - __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); - }); - } + @method create + @static + */ + Map.create = function() { + var Constructor = this; + return new Constructor(); + }; - __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); } - } + Map.prototype = { + constructor: Map, - __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); } - } + /** + This property will change as the number of objects in the map changes. - __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; + @since 1.8.0 + @property size + @type number + @default 0 + */ + size: 0, - while (args.length) { - count = ends > size ? size : ends; - if (count <= 0) { count = 0; } + /** + Retrieve the value associated with a given key. - chunk = args.splice(0, size); - chunk = [start, count].concat(chunk); + @method get + @param {*} key + @return {*} the value associated with the key, or `undefined` + */ + get: function(key) { + if (this.size === 0) { return; } - start += size; - ends -= count; + var values = this.values; + var guid = guidFor(key); - ret = ret.concat(splice.apply(array, chunk)); - } - return ret; - } + return values[guid]; + }, - __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); - } - } + /** + 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. - __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); - } - }); + @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); - return result; - } + // ensure we don't store -0 + var k = key === -0 ? 0 : key; - __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; + keys.add(k, guid); - var errorProps = [ - 'description', - 'fileName', - 'lineNumber', - 'message', - 'name', - 'number', - 'stack' - ]; + values[guid] = value; - /** - A subclass of the JavaScript Error object for use in Ember. + this.size = keys.size; - @class Error - @namespace Ember - @extends Error - @constructor - */ - function EmberError() { - var tmp = Error.apply(this, arguments); + return this; + }, - // 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]]; - } - } + /** + @deprecated see delete + Removes a value from the map for an associated key. - EmberError.prototype = create(Error.prototype); + @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.'); - __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 + return this["delete"](key); + }, - /** - @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; + /** + Removes a value from the map for an associated key. - var a_slice = [].slice; + @since 1.8.0 + @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); - /* listener flags */ - var ONCE = 1; - var SUSPENDED = 2; + if (keys["delete"](key, guid)) { + delete values[guid]; + this.size = keys.size; + return true; + } else { + return false; + } + }, + /** + Check whether a key is present. - /* - 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. + @method has + @param {*} key + @return {Boolean} true if the item was present, false otherwise + */ + has: function(key) { + return this.keys.has(key); + }, - The hashes are stored in the object's meta hash, and look like this: + /** + 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. - // Object's meta hash - { - listeners: { // variable name: `listenerSet` - "foo:changed": [ // variable name: `actions` - target, method, flags - ] - } - } + The keys are guaranteed to be iterated over in insertion order. - */ + @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); + } - 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; + if (this.size === 0) { return; } + + var length = arguments.length; + var map = this; + var cb, thisArg; + + 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); + }; } - } - return index; - } - function actionsFor(obj, eventName) { - var meta = metaFor(obj, true); - var actions; - var listeners = meta.listeners; + this.keys.forEach(cb); + }, - 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; + /** + @method clear + */ + clear: function() { + this.keys.clear(); + this.values = create(null); + this.size = 0; + }, + + /** + @method copy + @return {Ember.Map} + */ + copy: function() { + return copyMap(this, new Map()); } + }; - actions = listeners[eventName]; + deprecateProperty(Map.prototype, 'length', 'size'); - // 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; + /** + @class MapWithDefault + @namespace Ember + @extends Ember.Map + @private + @constructor + @param [options] + @param {*} [options.defaultValue] + */ + function MapWithDefault(options) { + this._super$constructor(); + this.defaultValue = options.defaultValue; + } + + /** + @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(); } + }; - return actions; - } + MapWithDefault.prototype = create(Map.prototype); + MapWithDefault.prototype.constructor = MapWithDefault; + MapWithDefault.prototype._super$constructor = Map; + MapWithDefault.prototype._super$get = Map.prototype.get; - function listenersUnion(obj, eventName, otherActions) { - var meta = obj['__ember_meta__']; - var actions = meta && meta.listeners && meta.listeners[eventName]; + /** + Retrieve the value associated with a given key. - if (!actions) { return; } - 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); + @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); - if (actionIndex === -1) { - otherActions.push(target, method, flags); - } + if (hasValue) { + return this._super$get(key); + } else { + var defaultValue = this.defaultValue(key); + this.set(key, defaultValue); + return defaultValue; } - } - - __exports__.listenersUnion = listenersUnion;function listenersDiff(obj, eventName, otherActions) { - var meta = obj['__ember_meta__']; - var actions = meta && meta.listeners && meta.listeners[eventName]; - var diffActions = []; + }; - if (!actions) { return; } - 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); + /** + @method copy + @return {Ember.MapWithDefault} + */ + MapWithDefault.prototype.copy = function() { + var Constructor = this.constructor; + return copyMap(this, new Constructor({ + defaultValue: this.defaultValue + })); + }; - if (actionIndex !== -1) { continue; } + __exports__["default"] = Map; - otherActions.push(target, method, flags); - diffActions.push(target, method, flags); - } + __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"]; - return diffActions; - } + /** + Merge the contents of two objects together into the first object. - __exports__.listenersDiff = listenersDiff;/** - Add an event listener + ```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'} + ``` - @method addListener + @method merge @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 + @param {Object} original The object to merge into + @param {Object} updates The object to copy properties from + @return {Object} */ - function addListener(obj, eventName, target, method, once) { - Ember.assert("You must pass at least an object and event name to Ember.addListener", !!obj && !!eventName); + __exports__["default"] = function merge(original, updates) { + if (!updates || typeof updates !== 'object') { + return original; + } - if (!method && 'function' === typeof target) { - method = target; - target = null; + var props = keys(updates); + var prop; + var length = props.length; + + for (var i = 0; i < length; i++) { + prop = props[i]; + original[prop] = updates[prop]; } - var actions = actionsFor(obj, eventName); - var actionIndex = indexOf(actions, target, method); - var flags = 0; + 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 - if (once) flags |= ONCE; + /** + @module ember + @submodule ember-metal + */ - if (actionIndex !== -1) { return; } + 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; - actions.push(target, method, flags); + var REQUIRED; + var a_slice = [].slice; - if ('function' === typeof obj.didAddListener) { - obj.didAddListener(eventName, target, method); + function superFunction(){ + var func = this.__nextSuper; + var ret; + + 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; } } - __exports__.addListener = addListener;/** - Remove an event listener - - Arguments should match those passed to `Ember.addListener`. + // 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 ) { } + }; - @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); + superFunction.call(primer); + superFunction.call(primer, 1); + superFunction.call(primer, 1, 2); + superFunction.call(primer, 1, 2, 3); - if (!method && 'function' === typeof target) { - method = target; - target = null; + 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; + } - function _removeListener(target, method) { - var actions = actionsFor(obj, eventName); - var actionIndex = indexOf(actions, target, method); - - // action doesn't exist, give up silently - if (actionIndex === -1) { return; } + function isMethod(obj) { + return 'function' === typeof obj && + obj.isMethod !== false && + obj !== Boolean && + obj !== Object && + obj !== Number && + obj !== Array && + obj !== Date && + obj !== String; + } - actions.splice(actionIndex, 3); + var CONTINUE = {}; - if ('function' === typeof obj.didRemoveListener) { - obj.didRemoveListener(eventName, target, method); - } - } + function mixinProperties(mixinsMeta, mixin) { + var guid; - if (method) { - _removeListener(target, method); + if (mixin instanceof Mixin) { + guid = guidFor(mixin); + if (mixinsMeta[guid]) { return CONTINUE; } + mixinsMeta[guid] = mixin; + return mixin.properties; } else { - var meta = obj['__ember_meta__']; - var actions = meta && meta.listeners && meta.listeners[eventName]; - - if (!actions) { return; } - for (var i = actions.length - 3; i >= 0; i -= 3) { - _removeListener(actions[i], actions[i+1]); - } + return mixin; // apply anonymous mixin properties } } - /** - Suspend listener during callback. + function concatenatedMixinProperties(concatProp, props, values, base) { + var concats; - 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. + // 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]; + } - @method suspendListener - @for Ember + return concats; + } - @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; + 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]; } - var actions = actionsFor(obj, eventName); - var actionIndex = indexOf(actions, target, method); + // If we didn't find the original descriptor in a parent mixin, find + // it on the original object. + superProperty = superProperty || meta.descs[key]; - if (actionIndex !== -1) { - actions[actionIndex+2] |= SUSPENDED; // mark the action as suspended + if (superProperty === undefined || !(superProperty instanceof ComputedProperty)) { + return property; } - function tryable() { return callback.call(target); } - function finalizer() { if (actionIndex !== -1) { actions[actionIndex+2] &= ~SUSPENDED; } } + // 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); - return tryFinally(tryable, finalizer); + return property; } - __exports__.suspendListener = suspendListener;/** - Suspends multiple listeners during a callback. - - @method suspendListeners - @for Ember + var sourceAvailable = (function() { + return this; + }).toString().indexOf('return this;') > -1; - @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; - } + function giveMethodSuper(obj, key, method, values, descs) { + var superMethod; - var suspendedActions = []; - var actionsList = []; - var eventName, actions, i, l; + // 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]; + } - for (i=0, l=eventNames.length; i -1; + method.__hasSuper = hasSuper; } } - return tryFinally(tryable, finalizer); + if (sourceAvailable === false || hasSuper) { + return wrap(method, superMethod); + } else { + return method; + } } - __exports__.suspendListeners = suspendListeners;/** - Return a list of currently watched events - - @private - @method watchedEvents - @for Ember - @param obj - */ - function watchedEvents(obj) { - var listeners = obj['__ember_meta__'].listeners, ret = []; + function applyConcatenatedProperties(obj, key, value, values) { + var baseValue = values[key] || obj[key]; - if (listeners) { - for (var eventName in listeners) { - if (eventName !== '__source__' && - listeners[eventName]) { - ret.push(eventName); + if (baseValue) { + if ('function' === typeof baseValue.concat) { + if (value === null || value === undefined) { + return baseValue; + } else { + return baseValue.concat(value); } + } else { + return makeArray(baseValue).concat(value); } + } else { + return makeArray(value); } - return ret; } - __exports__.watchedEvents = watchedEvents;/** - Send an event. The execution of suspended listeners - is skipped, and once listeners are removed. A listener without - a target is executed on the passed object. If an array of actions - is not passed, the actions stored on the passed object are invoked. + function applyMergedProperties(obj, key, value, values) { + var baseValue = values[key] || obj[key]; - @method sendEvent - @for Ember - @param obj - @param {String} eventName - @param {Array} params Optional parameters for each listener. - @param {Array} actions Optional array of actions (listeners). - @return true - */ - function sendEvent(obj, eventName, params, actions) { - // first give object a chance to handle it - if (obj !== Ember && 'function' === typeof obj.sendEvent) { - obj.sendEvent(eventName, params); - } + Ember.assert("You passed in `" + JSON.stringify(value) + "` as the value for `" + key + + "` but `" + key + "` cannot be an Array", !isArray(value)); - if (!actions) { - var meta = obj['__ember_meta__']; - actions = meta && meta.listeners && meta.listeners[eventName]; - } + if (!baseValue) { return value; } - if (!actions) { return; } + var newBase = merge({}, baseValue); + var hasFunction = false; - for (var i = actions.length - 3; 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](); - } + for (var prop in value) { + if (!value.hasOwnProperty(prop)) { continue; } + + var propValue = value[prop]; + if (isMethod(propValue)) { + // TODO: support for Computed Properties, etc? + hasFunction = true; + newBase[prop] = giveMethodSuper(obj, prop, propValue, baseValue, {}); } else { - if (params) { - apply(target, method, params); - } else { - method.call(target); - } + newBase[prop] = propValue; } } - return true; - } - __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]; + if (hasFunction) { + newBase._super = superFunction; + } - return !!(actions && actions.length); + return newBase; } - __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]; + function addNormalizedProperty(base, key, value, meta, descs, values, concats, mergings) { + if (value instanceof Descriptor) { + if (value === REQUIRED && descs[key]) { return CONTINUE; } - if (!actions) { return ret; } + // Wrap descriptor function to implement + // __nextSuper() if needed + if (value.func) { + value = giveDescriptorSuper(meta, key, value, values, descs); + } - for (var i = 0, l = actions.length; i < l; i += 3) { - var target = actions[i]; - var method = actions[i+1]; - ret.push([target, method]); - } + 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); + } - return ret; + descs[key] = undefined; + values[key] = value; + } } - __exports__.listenersFor = listenersFor;/** - Define a property as a function that should be executed when - a specified event or events are triggered. + function mergeMixins(mixins, m, descs, values, base, keys) { + var mixin, props, key, concats, mergings, meta; + + function removeKeys(keyName) { + delete descs[keyName]; + delete values[keyName]; + } + for(var i=0, l=mixins.length; i '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' - ``` + stream.subscribe(onNotify); - @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}`'); + if (obj._streamBindingSubscriptions === undefined) { + obj._streamBindingSubscriptions = o_create(null); } - - return newExpandProperties(pattern, callback); - } - - function oldExpandProperties(pattern, callback) { - var match, prefix, list; - - if (match = BRACE_EXPANSION.exec(pattern)) { - prefix = match[1]; - list = match[2]; - - forEach(list.split(','), function (suffix) { - callback(prefix + suffix); - }); - } else { - callback(pattern); - } + obj._streamBindingSubscriptions[key] = onNotify; } - function newExpandProperties(pattern, callback) { - if ('string' === Ember.typeOf(pattern)) { - var parts = pattern.split(SPLIT_REGEX); - var properties = [parts]; - - forEach(parts, function(part, index) { - if (part.indexOf(',') >= 0) { - properties = duplicateAndReplace(properties, part.split(','), index); + function connectBindings(obj, m) { + // TODO Mixin.apply(instance) should disconnect binding if exists + var bindings = m.bindings; + var key, binding, to; + if (bindings) { + for (key in bindings) { + binding = bindings[key]; + if (binding) { + to = key.slice(0, -7); // strip Binding off end + if (isStream(binding)) { + connectStreamBinding(obj, to, binding); + continue; + } else if (binding instanceof Binding) { + binding = binding.copy(); // copy prototypes' instance + binding.to(to); + } else { // binding is string path + binding = new Binding(to, binding); + } + binding.connect(obj); + obj[key] = binding; } - }); - - forEach(properties, function(property) { - callback(property.join('')); - }); - } else { - callback(pattern); + } + // mark as applied + m.bindings = {}; } } - function duplicateAndReplace(properties, currentParts, index) { - var all = []; - - forEach(properties, function(property) { - forEach(currentParts, function(part) { - var current = property.slice(0); - current[index] = part; - all.push(current); - }); - }); - - return all; + function finishPartial(obj, m) { + connectBindings(obj, m || metaFor(obj)); + return obj; } - }); -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; - /** - To get multiple properties at once, call `Ember.getProperties` - with an object followed by a list of strings or an array: + 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]; + } - ```javascript - Ember.getProperties(record, 'firstName', 'lastName', 'zipCode'); - // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } - ``` + return { desc: desc, value: value }; + } - is equivalent to: + function updateObserversAndListeners(obj, key, observerOrListener, pathsKey, updateMethod) { + var paths = observerOrListener[pathsKey]; - ```javascript - Ember.getProperties(record, ['firstName', 'lastName', 'zipCode']); - // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } - ``` + if (paths) { + for (var i=0, l=paths.length; i 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; + } - cache[name] = listeners; - return listeners; - }; + Mixin._apply = applyMixin; - var time = (function() { - var perf = 'undefined' !== typeof window ? window.performance || {} : {}; - var fn = perf.now || perf.mozNow || perf.webkitNow || perf.msNow || perf.oNow; - // fn.bind will be available in all the browsers that support the advanced window.performance... ;-) - return fn ? fn.bind(perf) : function() { return +new Date(); }; - })(); + Mixin.applyPartial = function(obj) { + var args = a_slice.call(arguments, 1); + return applyMixin(obj, args, true); + }; - /** - Notifies event's subscribers, calls `before` and `after` hooks. + Mixin.finishPartial = finishPartial; - @method instrument - @namespace Ember.Instrumentation + // ES6TODO: this relies on a global state? + Ember.anyUnprocessedMixins = false; - @param {String} [name] Namespaced event name. - @param {Object} payload - @param {Function} callback Function that you're instrumenting. - @param {Object} binding Context that instrument function is called with. + /** + @method create + @static + @param arguments* */ - function instrument(name, _payload, callback, binding) { - if (subscribers.length === 0) { - return callback.call(binding); - } - var payload = _payload || {}; - var finalizer = _instrumentStart(name, function () { - return payload; - }); - if (finalizer) { - var tryable = function _instrumenTryable() { - return callback.call(binding); - }; - var catchable = function _instrumentCatchable(e) { - payload.exception = e; - }; - return tryCatchFinally(tryable, catchable, finalizer); - } else { - return callback.call(binding); + 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__.instrument = instrument;// private for now - function _instrumentStart(name, _payload) { - var listeners = cache[name]; + var MixinPrototype = Mixin.prototype; - if (!listeners) { - listeners = populateListeners(name); - } + /** + @method reopen + @param arguments* + */ + MixinPrototype.reopen = function() { + var mixin; - if (listeners.length === 0) { - return; + if (this.properties) { + mixin = new Mixin(undefined, this.properties); + this.properties = undefined; + this.mixins = [mixin]; + } else if (!this.mixins) { + this.mixins = []; } - var payload = _payload(); - - var STRUCTURED_PROFILE = Ember.STRUCTURED_PROFILE; - var timeName; - if (STRUCTURED_PROFILE) { - timeName = name + ": " + payload.object; - console.time(timeName); - } + var len = arguments.length; + var mixins = this.mixins; + var idx; - var l = listeners.length; - var beforeValues = new Array(l); - var i, listener; - var timestamp = time(); - for (i=0; i= 0) { + if (_detect(mixins[loc], targetMixin, seen)) { return true; } } + return false; + } - regex = regex.join("\\."); - regex = regex + "(\\..*)?"; + /** + @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; + }; - var subscriber = { - pattern: pattern, - regex: new RegExp("^" + regex + "$"), - object: object - }; + MixinPrototype.without = function() { + var ret = new Mixin([this]); + ret._without = a_slice.call(arguments); + return ret; + }; - subscribers.push(subscriber); - cache = {}; + function _keys(ret, mixin, seen) { + if (seen[guidFor(mixin)]) { return; } + seen[guidFor(mixin)] = true; - return subscriber; + 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); }); + } } - __exports__.subscribe = subscribe;/** - Unsubscribes from a particular event or instrumented block of code. + 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; + }; - @method unsubscribe - @namespace Ember.Instrumentation + // 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 = []; - @param {Object} [subscriber] - */ - function unsubscribe(subscriber) { - var index; + if (!mixins) { return ret; } - for (var i=0, l=subscribers.length; i this.changingFrom ? 'green' : 'red'; + // logic + } + }), - var result = []; - var prop, i; + friendsDidChange: Ember.observer('friends.@each.name', function(obj, keyName) { + // some logic + // obj.get(keyName) returns friends array + }) + }); + ``` - for (prop in obj) { - if (prop !== '_super' && - prop.lastIndexOf('__',0) !== 0 && - hasOwnProperty.call(obj, prop)) { - result.push(prop); - } - } + Also available as `Function.prototype.observesBefore` if prototype extensions are + enabled. - if (hasDontEnumBug) { - for (i = 0; i < dontEnumsLength; i++) { - if (hasOwnProperty.call(obj, dontEnums[i])) { - result.push(dontEnums[i]); - } - } - } - return result; - }; - }()); - } + @method beforeObserver + @for Ember + @param {String} propertyNames* + @param {Function} func + @return func + */ + function beforeObserver() { + var func = a_slice.call(arguments, -1)[0]; + var paths; - __exports__["default"] = keys; - }); -enifed("ember-metal/libraries", - ["ember-metal/enumerable_utils","exports"], - function(__dependency1__, __exports__) { - "use strict"; - // Provides a way to register library versions with ember. - var forEach = __dependency1__.forEach; - var indexOf = __dependency1__.indexOf; + var addWatchedProperty = function(path) { paths.push(path); }; - var libraries = function() { - var _libraries = []; - var coreLibIndex = 0; + var _paths = a_slice.call(arguments, 0, -1); - var getLibrary = function(name) { - for (var i = 0; i < _libraries.length; i++) { - if (_libraries[i].name === name) { - return _libraries[i]; - } - } - }; + if (typeof func !== "function") { + // revert to old, soft-deprecated argument ordering - _libraries.register = function(name, version) { - if (!getLibrary(name)) { - _libraries.push({name: name, version: version}); - } - }; + func = arguments[0]; + _paths = a_slice.call(arguments, 1); + } - _libraries.registerCoreLibrary = function(name, version) { - if (!getLibrary(name)) { - _libraries.splice(coreLibIndex++, 0, {name: name, version: version}); - } - }; + paths = []; - _libraries.deRegister = function(name) { - var lib = getLibrary(name); - if (lib) _libraries.splice(indexOf(_libraries, lib), 1); - }; + for (var i=0; i<_paths.length; ++i) { + expandProperties(_paths[i], addWatchedProperty); + } - _libraries.each = function (callback) { - forEach(_libraries, function(lib) { - callback(lib.name, lib.version); - }); - }; + if (typeof func !== "function") { + throw new Ember.Error("Ember.beforeObserver called without a function"); + } - return _libraries; - }(); + func.__ember_observesBefore__ = paths; + return func; + } - __exports__["default"] = libraries; + __exports__.beforeObserver = beforeObserver;__exports__.IS_BINDING = IS_BINDING; + __exports__.Mixin = Mixin; }); -enifed("ember-metal/logger", - ["ember-metal/core","ember-metal/error","exports"], - function(__dependency1__, __dependency2__, __exports__) { +enifed("ember-metal/observer", + ["ember-metal/watching","ember-metal/array","ember-metal/events","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; - var Ember = __dependency1__["default"]; - var EmberError = __dependency2__["default"]; - - function consoleMethod(name) { - var consoleObj, logToConsole; - if (Ember.imports.console) { - consoleObj = Ember.imports.console; - } else if (typeof console !== 'undefined') { - consoleObj = console; - } + 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 + */ - var method = typeof consoleObj === 'object' ? consoleObj[name] : null; + var AFTER_OBSERVERS = ':change'; + var BEFORE_OBSERVERS = ':before'; - 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); - }; - } - } + function changeEvent(keyName) { + return keyName + AFTER_OBSERVERS; } - 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); - } - } + function beforeEvent(keyName) { + return keyName + BEFORE_OBSERVERS; } /** - Inside Ember-Metal, simply uses the methods from `imports.console`. - Override this to provide more robust logging functionality. + @method addObserver + @for Ember + @param obj + @param {String} path + @param {Object|Function} targetOrMethod + @param {Function|String} [method] + */ + function addObserver(obj, _path, target, method) { + addListener(obj, changeEvent(_path), target, method); + watch(obj, _path); - @class Logger - @namespace Ember + return this; + } + + __exports__.addObserver = addObserver;function observersFor(obj, path) { + return listenersFor(obj, changeEvent(path)); + } + + __exports__.observersFor = observersFor;/** + @method removeObserver + @for Ember + @param obj + @param {String} path + @param {Object|Function} target + @param {Function|String} [method] */ - __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. + function removeObserver(obj, path, target, method) { + unwatch(obj, path); + removeListener(obj, changeEvent(path), target, method); - ```javascript - var foo = 1; - Ember.Logger.log('log value of foo:', foo); - // "log value of foo: 1" will be printed to the console - ``` + return this; + } - @method log - @for Ember.Logger - @param {*} arguments - */ - log: consoleMethod('log') || Ember.K, + __exports__.removeObserver = removeObserver;/** + @method addBeforeObserver + @for Ember + @param obj + @param {String} path + @param {Object|Function} target + @param {Function|String} [method] + */ + function addBeforeObserver(obj, path, target, method) { + addListener(obj, beforeEvent(path), target, method); + watch(obj, path); - /** - 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. + return this; + } - ```javascript - Ember.Logger.warn('Something happened!'); - // "Something happened!" will be printed to the console with a warning icon. - ``` + __exports__.addBeforeObserver = addBeforeObserver;// 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); + } - @method warn - @for Ember.Logger - @param {*} arguments - */ - warn: consoleMethod('warn') || Ember.K, + __exports__._suspendBeforeObserver = _suspendBeforeObserver;function _suspendObserver(obj, path, target, method, callback) { + return suspendListener(obj, changeEvent(path), target, method, callback); + } - /** - 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. + __exports__._suspendObserver = _suspendObserver;function _suspendBeforeObservers(obj, paths, target, method, callback) { + var events = map.call(paths, beforeEvent); + return suspendListeners(obj, events, target, method, callback); + } - ```javascript - Ember.Logger.error('Danger! Danger!'); - // "Danger! Danger!" will be printed to the console in red text. - ``` + __exports__._suspendBeforeObservers = _suspendBeforeObservers;function _suspendObservers(obj, paths, target, method, callback) { + var events = map.call(paths, changeEvent); + return suspendListeners(obj, events, target, method, callback); + } - @method error - @for Ember.Logger - @param {*} arguments - */ - error: consoleMethod('error') || Ember.K, + __exports__._suspendObservers = _suspendObservers;function beforeObserversFor(obj, path) { + return listenersFor(obj, beforeEvent(path)); + } - /** - Logs the arguments to the console. - You can pass as many arguments as you want and they will be joined together with a space. + __exports__.beforeObserversFor = beforeObserversFor;/** + @method removeBeforeObserver + @for Ember + @param obj + @param {String} path + @param {Object|Function} target + @param {Function|String} [method] + */ + function removeBeforeObserver(obj, path, target, method) { + unwatch(obj, path); + removeListener(obj, beforeEvent(path), target, method); - ```javascript - var foo = 1; - Ember.Logger.info('log value of foo:', foo); - // "log value of foo: 1" will be printed to the console - ``` + return this; + } - @method info - @for Ember.Logger - @param {*} arguments - */ - info: consoleMethod('info') || Ember.K, + __exports__.removeBeforeObserver = removeBeforeObserver; + }); +enifed("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; - /** - 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. + /* + this.observerSet = { + [senderGuid]: { // variable name: `keySet` + [keyName]: listIndex + } + }, + this.observers = [ + { + sender: obj, + keyName: keyName, + eventName: eventName, + listeners: [ + [target, method, flags] + ] + }, + ... + ] + */ + __exports__["default"] = ObserverSet; + function ObserverSet() { + this.clear(); + } - ```javascript - var foo = 1; - Ember.Logger.debug('log value of foo:', foo); - // "log value of foo: 1" will be printed to the console - ``` - @method debug - @for Ember.Logger - @param {*} arguments - */ - debug: consoleMethod('debug') || consoleMethod('info') || Ember.K, + ObserverSet.prototype.add = function(sender, keyName, eventName) { + var observerSet = this.observerSet; + var observers = this.observers; + var senderGuid = guidFor(sender); + var keySet = observerSet[senderGuid]; + var index; - /** - If the value passed into `Ember.Logger.assert` is not truthy it will throw an error with a stack trace. + 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; + }; - ```javascript - Ember.Logger.assert(true); // undefined - Ember.Logger.assert(true === false); // Throws an Assertion failed error. - ``` + ObserverSet.prototype.flush = function() { + var observers = this.observers; + var 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); + } + }; - @method assert - @for Ember.Logger - @param {Boolean} bool Value to test - */ - assert: consoleMethod('assert') || assertPolyfill + ObserverSet.prototype.clear = function() { + this.observerSet = {}; + this.observers = []; }; }); -enifed("ember-metal/map", - ["ember-metal/utils","ember-metal/array","ember-metal/platform","ember-metal/deprecate_property","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { +enifed("ember-metal/path_cache", + ["ember-metal/cache","exports"], + function(__dependency1__, __exports__) { "use strict"; - /** - @module ember-metal - */ + var Cache = __dependency1__["default"]; - /* - JavaScript (before ES6) does not have a Map implementation. Objects, - which are often used as dictionaries, may only have Strings as keys. + var IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/; + var IS_GLOBAL_PATH = /^([A-Z$]|([0-9][A-Z$])).*[\.]/; + var HAS_THIS = 'this.'; - 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. + var isGlobalCache = new Cache(1000, function(key) { return IS_GLOBAL.test(key); }); + var isGlobalPathCache = new Cache(1000, function(key) { return IS_GLOBAL_PATH.test(key); }); + var hasThisCache = new Cache(1000, function(key) { return key.lastIndexOf(HAS_THIS, 0) === 0; }); + var firstDotIndexCache = new Cache(1000, function(key) { return key.indexOf('.'); }); - 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. + var firstKeyCache = new Cache(1000, function(path) { + var index = firstDotIndexCache.get(path); + if (index === -1) { + return path; + } else { + return path.slice(0, index); + } + }); - Map is mocked out to look like an Ember object, so you can do - `Ember.Map.create()` for symmetry with other Ember classes. - */ + var tailPathCache = new Cache(1000, function(path) { + var index = firstDotIndexCache.get(path); + if (index !== -1) { + return path.slice(index + 1); + } + }); - var guidFor = __dependency1__.guidFor; - var indexOf = __dependency2__.indexOf; - var create = __dependency3__.create; - var deprecateProperty = __dependency4__.deprecateProperty; + var caches = { + isGlobalCache: isGlobalCache, + isGlobalPathCache: isGlobalPathCache, + hasThisCache: hasThisCache, + firstDotIndexCache: firstDotIndexCache, + firstKeyCache: firstKeyCache, + tailPathCache: tailPathCache + }; + __exports__.caches = caches; + function isGlobal(path) { + return isGlobalCache.get(path); + } - function missingFunction(fn) { - throw new TypeError('' + Object.prototype.toString.call(fn) + " is not a function"); + __exports__.isGlobal = isGlobal;function isGlobalPath(path) { + return isGlobalPathCache.get(path); } - function missingNew(name) { - throw new TypeError("Constructor " + name + "requires 'new'"); + __exports__.isGlobalPath = isGlobalPath;function hasThis(path) { + return hasThisCache.get(path); } - function copyNull(obj) { - var output = create(null); + __exports__.hasThis = hasThis;function isPath(path) { + return firstDotIndexCache.get(path) !== -1; + } - for (var prop in obj) { - // hasOwnPropery is not needed because obj is Object.create(null); - output[prop] = obj[prop]; - } + __exports__.isPath = isPath;function getFirstKey(path) { + return firstKeyCache.get(path); + } - return output; + __exports__.getFirstKey = getFirstKey;function getTailPath(path) { + return tailPathCache.get(path); } - function copyMap(original, newObject) { - var keys = original.keys.copy(); - var values = copyNull(original.values); + __exports__.getTailPath = getTailPath; + }); +enifed("ember-metal/platform", + ["ember-metal/platform/define_property","ember-metal/platform/define_properties","ember-metal/platform/create","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var hasES5CompliantDefineProperty = __dependency1__.hasES5CompliantDefineProperty; + var defineProperty = __dependency1__.defineProperty; + var defineProperties = __dependency2__["default"]; + var create = __dependency3__["default"]; - newObject.keys = keys; - newObject.values = values; - newObject.size = original.size; + /** + @module ember-metal + */ - return newObject; - } + var hasPropertyAccessors = hasES5CompliantDefineProperty; + var canDefineNonEnumerableProperties = hasES5CompliantDefineProperty; /** - 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. + Platform specific methods and feature detectors needed by the framework. - @class OrderedSet + @class platform @namespace Ember - @constructor - @private + @static */ - function OrderedSet() { - if (this instanceof OrderedSet) { - this.clear(); - this._silenceRemoveDeprecation = false; - } else { - missingNew("OrderedSet"); - } - } + __exports__.create = create; + __exports__.defineProperty = defineProperty; + __exports__.defineProperties = defineProperties; + __exports__.hasPropertyAccessors = hasPropertyAccessors; + __exports__.canDefineNonEnumerableProperties = canDefineNonEnumerableProperties; + }); +enifed("ember-metal/platform/create", + ["ember-metal/platform/define_properties","exports"], + function(__dependency1__, __exports__) { + // Remove "use strict"; from transpiled module until + // https://bugs.webkit.org/show_bug.cgi?id=138038 is fixed + // + // REMOVE_USE_STRICT: true + // + + var defineProperties = __dependency1__["default"]; + + /** + @class platform + @namespace Ember + @static + */ /** + Identical to `Object.create()`. Implements if not available natively. + + @since 1.8.0 @method create - @static - @return {Ember.OrderedSet} + @for Ember */ - OrderedSet.create = function() { - var Constructor = this; + 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; - return new Constructor(); - }; + function Empty() {} + Empty.prototype = empty; + // short-circuit future calls + createEmpty = function () { + return new Empty(); + }; + return new Empty(); + }; + } - OrderedSet.prototype = { - constructor: OrderedSet, - /** - @method clear - */ - clear: function() { - this.presenceSet = create(null); - this.list = []; - this.size = 0; - }, + create = Object.create = function create(prototype, properties) { - /** - @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; + var object; + function Type() {} // An empty constructor. - if (presenceSet[guid] === true) { - return; - } + 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 + } - presenceSet[guid] = true; - this.size = list.push(obj); + Type.prototype = prototype; - return this; - }, + object = new Type(); + } - /** - @deprecated + if (properties !== undefined) { + defineProperties(object, properties); + } - @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); + return object; + }; + } else { + create = Object.create; + } - return this["delete"](obj, _guid); - }, + __exports__["default"] = create; + }); +enifed("ember-metal/platform/define_properties", + ["ember-metal/platform/define_property","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var defineProperty = __dependency1__.defineProperty; - /** - @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; + var defineProperties = Object.defineProperties; - if (presenceSet[guid] === true) { - delete presenceSet[guid]; - var index = indexOf.call(list, obj); - if (index > -1) { - list.splice(index, 1); + // 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]); } - this.size = list.length; - return true; - } else { - return false; } - }, - - /** - @method isEmpty - @return {Boolean} - */ - isEmpty: function() { - return this.size === 0; - }, + return object; + }; - /** - @method has - @param obj - @return {Boolean} - */ - has: function(obj) { - if (this.size === 0) { return false; } + Object.defineProperties = defineProperties; + } - var guid = guidFor(obj); - var presenceSet = this.presenceSet; + __exports__["default"] = defineProperties; + }); +enifed("ember-metal/platform/define_property", + ["exports"], + function(__exports__) { + "use strict"; + /*globals Node */ - return presenceSet[guid] === true; - }, + /** + @class platform + @namespace Ember + @static + */ - /** - @method forEach - @param {Function} fn - @param self - */ - forEach: function(fn /*, thisArg*/) { - if (typeof fn !== 'function') { - missingFunction(fn); - } + /** + Set to true if the platform supports native getters and setters. - if (this.size === 0) { return; } + @property hasPropertyAccessors + @final + */ - var list = this.list; - var length = arguments.length; - var i; + /** + Identical to `Object.defineProperty()`. Implements as much functionality + as possible if not available natively. - 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]); + @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; - /** - @method toArray - @return {Array} - */ - toArray: function() { - return this.list.slice(); - }, + // check non-enumerability + defineProperty(obj, 'a', { + configurable: true, + enumerable: false, + writable: true, + value: true + }); + for (var key in obj) { + if (key === 'a') return; + } - /** - @method copy - @return {Ember.OrderedSet} - */ - copy: function() { - var Constructor = this.constructor; - var set = new Constructor(); + // 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; - set._silenceRemoveDeprecation = this._silenceRemoveDeprecation; - set.presenceSet = copyNull(this.presenceSet); - set.list = this.toArray(); - set.size = this.size; + // 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; - return set; + // defineProperty is compliant + return defineProperty; + } catch (e) { + // IE8 defines Object.defineProperty but calling it on an Object throws + return; } - }; + })(Object.defineProperty); - deprecateProperty(OrderedSet.prototype, 'length', 'size'); + var hasES5CompliantDefineProperty = !!defineProperty; - /** - A Map stores values indexed by keys. Unlike JavaScript's - default Objects, the keys of a Map can be any JavaScript - 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) { } - Internally, a Map has two data structures: + return false; + })(); - 1. `keys`: an OrderedSet of all of the existing keys - 2. `values`: a JavaScript Object indexed by the `Ember.guidFor(key)` + if (!canDefinePropertyOnDOM) { + defineProperty = function(obj, keyName, desc) { + var isNode; - 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`. + if (typeof Node === "object") { + isNode = obj instanceof Node; + } else { + isNode = typeof obj === "object" && typeof obj.nodeType === "number" && typeof obj.nodeName === "string"; + } - @class Map - @namespace Ember - @private - @constructor - */ - function Map() { - if (this instanceof this.constructor) { - this.keys = OrderedSet.create(); - this.keys._silenceRemoveDeprecation = true; - this.values = create(null); - this.size = 0; - } else { - missingNew("OrderedSet"); + if (isNode) { + // TODO: Should we have a warning here? + return (obj[keyName] = desc.value); + } else { + return Object.defineProperty(obj, keyName, desc); + } + }; } } - Ember.Map = Map; + if (!hasES5CompliantDefineProperty) { + defineProperty = function defineProperty(obj, keyName, desc) { + if (!desc.get) { obj[keyName] = desc.value; } + }; + } + __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"; /** - @method create - @static - */ - Map.create = function() { - var Constructor = this; - return new Constructor(); - }; - - Map.prototype = { - constructor: Map, - - /** - This property will change as the number of objects in the map changes. - - @property size - @type number - @default 0 - */ - size: 0, - - /** - Retrieve the value associated with a given key. - - @method get - @param {*} key - @return {*} the value associated with the key, or `undefined` - */ - get: function(key) { - if (this.size === 0) { return; } - - var values = this.values; - var guid = guidFor(key); - - return values[guid]; - }, - - /** - 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 - @return {Ember.Map} - */ - set: function(key, value) { - var keys = this.keys; - var values = this.values; - var guid = guidFor(key); + @module ember-metal + */ - // ensure we don't store -0 - var k = key === -0 ? 0 : key; + var Ember = __dependency1__["default"]; + var metaFor = __dependency2__.meta; + var objectDefineProperty = __dependency3__.defineProperty; + var hasPropertyAccessors = __dependency3__.hasPropertyAccessors; + var overrideChains = __dependency4__.overrideChains; + // .......................................................... + // DESCRIPTOR + // - keys.add(k, guid); + /** + Objects of this type can implement an interface to respond to requests to + get and set. The default implementation handles simple properties. - values[guid] = value; + You generally won't need to create or subclass this directly. - this.size = keys.size; + @class Descriptor + @namespace Ember + @private + @constructor + */ + function Descriptor() {} - return this; - }, + __exports__.Descriptor = Descriptor;// .......................................................... + // DEFINING PROPERTIES API + // - /** - @deprecated see delete - Removes a value from the map for an associated key. + 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); + }; + } - @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.'); + __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]; + }; + } - return this["delete"](key); - }, + __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. - /** - Removes a value from the map for an associated key. + 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. - @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); + 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 (keys["delete"](key, guid)) { - delete values[guid]; - this.size = keys.size; - return true; - } else { - return false; - } - }, + ## Examples - /** - Check whether a key is present. + ```javascript + // ES5 compatible mode + Ember.defineProperty(contact, 'firstName', { + writable: true, + configurable: false, + enumerable: true, + value: 'Charles' + }); - @method has - @param {*} key - @return {Boolean} true if the item was present, false otherwise - */ - has: function(key) { - return this.keys.has(key); - }, + // define a simple property + Ember.defineProperty(contact, 'lastName', undefined, 'Jolley'); - /** - 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. + // define a computed property + Ember.defineProperty(contact, 'fullName', Ember.computed(function() { + return this.firstName+' '+this.lastName; + }).property('firstName', 'lastName')); + ``` - The keys are guaranteed to be iterated over in insertion order. + @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; - @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 (!meta) meta = metaFor(obj); + descs = meta.descs; + existingDesc = meta.descs[keyName]; + var watchEntry = meta.watching[keyName]; - if (this.size === 0) { return; } + watching = watchEntry !== undefined && watchEntry > 0; - var length = arguments.length; - var map = this; - var cb, thisArg; + if (existingDesc instanceof Descriptor) { + existingDesc.teardown(obj, keyName); + } - 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); - }; - } + if (desc instanceof Descriptor) { + value = desc; - this.keys.forEach(cb); - }, + descs[keyName] = desc; + + if (watching && hasPropertyAccessors) { + objectDefineProperty(obj, keyName, { + configurable: true, + enumerable: true, + writable: true, + value: undefined // make enumerable + }); + } else { + 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; - /** - @method clear - */ - clear: function() { - this.keys.clear(); - this.values = create(null); - this.size = 0; - }, + + 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; - /** - @method copy - @return {Ember.Map} - */ - copy: function() { - return copyMap(this, new Map()); + // compatibility with ES5 + objectDefineProperty(obj, keyName, desc); + } } - }; - deprecateProperty(Map.prototype, 'length', 'size'); + // if key is being watched, override chains that + // were initialized with the prototype + if (watching) { overrideChains(obj, keyName, meta); } - /** - @class MapWithDefault - @namespace Ember - @extends Ember.Map - @private - @constructor - @param [options] - @param {*} [options.defaultValue] - */ - function MapWithDefault(options) { - this._super$constructor(); - this.defaultValue = options.defaultValue; + // 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; } - /** - @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(); - } - }; + __exports__.defineProperty = defineProperty; + }); +enifed("ember-metal/property_events", + ["ember-metal/utils","ember-metal/events","ember-metal/observer_set","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var guidFor = __dependency1__.guidFor; + var tryFinally = __dependency1__.tryFinally; + var sendEvent = __dependency2__.sendEvent; + var accumulateListeners = __dependency2__.accumulateListeners; + var ObserverSet = __dependency3__["default"]; - MapWithDefault.prototype = create(Map.prototype); - MapWithDefault.prototype.constructor = MapWithDefault; - MapWithDefault.prototype._super$constructor = Map; - MapWithDefault.prototype._super$get = Map.prototype.get; + var beforeObserverSet = new ObserverSet(); + var observerSet = new ObserverSet(); + var deferred = 0; + + // .......................................................... + // PROPERTY CHANGES + // /** - Retrieve the value associated with a given key. + 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. - @method get - @param {*} key - @return {*} the value associated with the key, or the default value + 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. + + @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} */ - MapWithDefault.prototype.get = function(key) { - var hasValue = this.has(key); + 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]; - if (hasValue) { - return this._super$get(key); - } else { - var defaultValue = this.defaultValue(key); - this.set(key, defaultValue); - return defaultValue; + if (!watching) { + return; } - }; - /** - @method copy - @return {Ember.MapWithDefault} - */ - MapWithDefault.prototype.copy = function() { - var Constructor = this.constructor; - return copyMap(this, new Constructor({ - defaultValue: this.defaultValue - })); - }; + if (proto === obj) { + return; + } - __exports__["default"] = Map; + if (desc && desc.willChange) { + desc.willChange(obj, keyName); + } - __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"]; + dependentKeysWillChange(obj, keyName, m); + chainsWillChange(obj, keyName, m); + notifyBeforeObservers(obj, keyName); + } /** - Merge the contents of two objects together into the first object. + This function is called just after an object property has changed. + It will notify any observers and clear caches among other things. - ```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'} - ``` + 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. - @method merge + @method propertyDidChange @for Ember - @param {Object} original The object to merge into - @param {Object} updates The object to copy properties from - @return {Object} + @param {Object} obj The object with the property that will change + @param {String} keyName The property key (or path) that will change. + @return {void} */ - __exports__["default"] = function merge(original, updates) { - if (!updates || typeof updates !== 'object') { - return original; + 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]; + + if (proto === obj) { + return; } - var props = keys(updates); - var prop; - var length = props.length; + // shouldn't this mean that we're watching this key? + if (desc && desc.didChange) { + desc.didChange(obj, keyName); + } - for (var i = 0; i < length; i++) { - prop = props[i]; - original[prop] = updates[prop]; + if (!watching && keyName !== 'length') { + return; } - return original; + if (m && m.deps && m.deps[keyName]) { + dependentKeysDidChange(obj, keyName, m); + } + + chainsDidChange(obj, keyName, m, false); + notifyObservers(obj, keyName); } - }); -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","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __exports__) { - // Remove "use strict"; from transpiled module until - // https://bugs.webkit.org/show_bug.cgi?id=138038 is fixed - // - // REMOVE_USE_STRICT: true - /** - @module ember - @submodule ember-metal - */ + 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; } - 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 apply = __dependency7__.apply; - 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 deps; + if (meta && meta.deps && (deps = meta.deps[depKey])) { + var seen = WILL_SEEN; + var top = !seen; - var REQUIRED; - var a_slice = [].slice; + if (top) { + seen = WILL_SEEN = {}; + } - function superFunction(){ - var func = this.__nextSuper; - var ret; - if (func) { - var args = new Array(arguments.length); - for (var i = 0, l = args.length; i < l; i++) { - args[i] = arguments[i]; + iterDeps(propertyWillChange, obj, deps, depKey, seen, meta); + + if (top) { + WILL_SEEN = null; } - this.__nextSuper = null; - ret = apply(this, func, args); - this.__nextSuper = func; } - return ret; } - 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); + // called whenever a property has just changed to update dependent keys + function dependentKeysDidChange(obj, depKey, meta) { + if (obj.isDestroying) { return; } + + var deps; + if (meta && meta.deps && (deps = meta.deps[depKey])) { + var seen = DID_SEEN; + var top = !seen; + + if (top) { + seen = DID_SEEN = {}; + } + + iterDeps(propertyDidChange, obj, deps, depKey, seen, meta); + + if (top) { + DID_SEEN = null; + } } - return ret; } - function isMethod(obj) { - return 'function' === typeof obj && - obj.isMethod !== false && - obj !== Boolean && - obj !== Object && - obj !== Number && - obj !== Array && - obj !== Date && - obj !== String; + function keysOf(obj) { + var keys = []; + + for (var key in obj) { + keys.push(key); + } + + return keys; } - var CONTINUE = {}; + function iterDeps(method, obj, deps, depKey, seen, meta) { + var keys, key, i, desc; + var guid = guidFor(obj); + var current = seen[guid]; - function mixinProperties(mixinsMeta, mixin) { - var guid; + if (!current) { + current = seen[guid] = {}; + } - 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 + if (current[depKey]) { + return; + } + + current[depKey] = true; + + if (deps) { + keys = keysOf(deps); + var descs = meta.descs; + for (i=0; i -1; + function overrideChains(obj, keyName, m) { + chainsDidChange(obj, keyName, m, true); + } - function giveMethodSuper(obj, key, method, values, descs) { - var superMethod; + /** + @method beginPropertyChanges + @chainable + @private + */ + function beginPropertyChanges() { + deferred++; + } - // 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]; + /** + @method endPropertyChanges + @private + */ + function endPropertyChanges() { + deferred--; + if (deferred<=0) { + beforeObserverSet.clear(); + observerSet.flush(); } + } - // If we didn't find the original value in a parent mixin, find it in - // the original object - superMethod = superMethod || obj[key]; + /** + Make a series of property changes together in an + exception-safe way. - // Only wrap the new method if the original method was a function - if (superMethod === undefined || 'function' !== typeof superMethod) { - return method; - } + ```javascript + Ember.changeProperties(function() { + obj1.set('foo', mayBlowUpWhenSet); + obj2.set('bar', baz); + }); + ``` - var hasSuper; - if (sourceAvailable) { - hasSuper = method.__hasSuper; + @method changeProperties + @param {Function} callback + @param [binding] + */ + function changeProperties(callback, binding) { + beginPropertyChanges(); + tryFinally(callback, endPropertyChanges, binding); + } - if (hasSuper === undefined) { - hasSuper = method.toString().indexOf('_super') > -1; - method.__hasSuper = hasSuper; - } - } + function notifyBeforeObservers(obj, keyName) { + if (obj.isDestroying) { return; } - if (sourceAvailable === false || hasSuper) { - return wrap(method, superMethod); + 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 { - return method; + sendEvent(obj, eventName, [obj, keyName]); } } - function applyConcatenatedProperties(obj, key, value, values) { - var baseValue = values[key] || obj[key]; + function notifyObservers(obj, keyName) { + if (obj.isDestroying) { return; } - if (baseValue) { - if ('function' === typeof baseValue.concat) { - if (value === null || value === undefined) { - return baseValue; - } else { - return baseValue.concat(value); - } - } else { - return makeArray(baseValue).concat(value); - } + var eventName = keyName + ':change'; + var listeners; + if (deferred) { + listeners = observerSet.add(obj, keyName, eventName); + accumulateListeners(obj, eventName, listeners); } else { - return makeArray(value); + sendEvent(obj, eventName, [obj, keyName]); } } - function applyMergedProperties(obj, key, value, values) { - var baseValue = values[key] || obj[key]; + __exports__.propertyWillChange = propertyWillChange; + __exports__.propertyDidChange = propertyDidChange; + __exports__.overrideChains = overrideChains; + __exports__.beginPropertyChanges = beginPropertyChanges; + __exports__.endPropertyChanges = endPropertyChanges; + __exports__.changeProperties = changeProperties; + }); +enifed("ember-metal/property_get", + ["ember-metal/core","ember-metal/error","ember-metal/path_cache","ember-metal/platform","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + /** + @module ember-metal + */ - 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 EmberError = __dependency2__["default"]; + var isGlobalPath = __dependency3__.isGlobalPath; + var isPath = __dependency3__.isPath; + var pathHasThis = __dependency3__.hasThis; + var hasPropertyAccessors = __dependency4__.hasPropertyAccessors; - if (!baseValue) { return value; } + var FIRST_KEY = /^([^\.]+)/; - var newBase = merge({}, baseValue); - var hasFunction = false; + // .......................................................... + // 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. - for (var prop in value) { - if (!value.hasOwnProperty(prop)) { continue; } + /** + 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. - 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; - } - } + 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.) - if (hasFunction) { - newBase._super = superFunction; - } + 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. - return newBase; - } + Note that if the object itself is `undefined`, this method will throw + an error. - function addNormalizedProperty(base, key, value, meta, descs, values, concats, mergings) { - if (value instanceof Descriptor) { - if (value === REQUIRED && descs[key]) { return CONTINUE; } + @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`. + */ + var get = function get(obj, keyName) { + // Helpers that operate with 'this' within an #each + if (keyName === '') { + return obj; + } - // Wrap descriptor function to implement - // __nextSuper() if needed - if (value.func) { - value = giveDescriptorSuper(meta, key, value, values, descs); - } + if (!keyName && 'string' === typeof obj) { + keyName = obj; + obj = null; + } - 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); - } + Ember.assert("Cannot call get with "+ keyName +" key.", !!keyName); + Ember.assert("Cannot call get with '"+ keyName +"' on an undefined object.", obj !== undefined); - descs[key] = undefined; - values[key] = value; + if (obj === null) { + var value = _getPath(obj, keyName); + Ember.deprecate( + "Ember.get fetched '"+keyName+"' from the global context. This behavior will change in the future (issue #3852)", + !value || (obj && obj !== Ember.lookup) || isPath(keyName) || isGlobalPath(keyName+".") // Add a . to ensure simple paths are matched. + ); + return value; } - } - function mergeMixins(mixins, m, descs, values, base, keys) { - var mixin, props, key, concats, mergings, meta; + var meta = obj['__ember_meta__']; + var desc = meta && meta.descs[keyName]; + var ret; - function removeKeys(keyName) { - delete descs[keyName]; - delete values[keyName]; + if (desc === undefined && isPath(keyName)) { + return _getPath(obj, keyName); } - for(var i=0, l=mixins.length; i 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); + } - props = mixinProperties(m, mixin); - if (props === CONTINUE) { continue; } + return ret; + } + }; - if (props) { - meta = metaFor(base); - if (base.willMergeMixin) { base.willMergeMixin(props); } - concats = concatenatedMixinProperties('concatenatedProperties', props, values, base); - mergings = concatenatedMixinProperties('mergedProperties', props, values, base); + /** + 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). - for (key in props) { - if (!props.hasOwnProperty(key)) { continue; } - keys.push(key); - addNormalizedProperty(base, key, props[key], meta, descs, values, concats, mergings); - } + @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; - // manually copy toString() because some JS engines do not enumerate it - if (props.hasOwnProperty('toString')) { base.toString = props.toString; } - } else if (mixin.mixins) { - mergeMixins(mixin.mixins, m, descs, values, base, keys); - if (mixin._without) { a_forEach.call(mixin._without, removeKeys); } - } - } - } + if (!target || isGlobal) target = Ember.lookup; + if (hasThis) path = path.slice(5); - var IS_BINDING = /^.+Binding$/; + 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+'.') + ); - function detectBinding(obj, key, value, m) { - if (IS_BINDING.test(key)) { - var bindings = m.bindings; - if (!bindings) { - bindings = m.bindings = {}; - } else if (!m.hasOwnProperty('bindings')) { - bindings = m.bindings = o_create(m.bindings); - } - bindings[key] = value; + if (target === Ember.lookup) { + key = path.match(FIRST_KEY)[0]; + target = get(target, key); + path = path.slice(key.length+1); } - } - function connectStreamBinding(obj, key, stream) { - var onNotify = function(stream) { - _suspendObserver(obj, key, null, didChange, function() { - trySet(obj, key, stream.value()); - }); - }; + // 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'); - var didChange = function() { - stream.setValue(get(obj, key), onNotify); - }; + return [ target, path ]; + } - // Initialize value - set(obj, key, stream.value()); + function _getPath(root, path) { + var hasThis, parts, tuple, idx, len; - addObserver(obj, key, null, didChange); + // 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); + } - stream.subscribe(onNotify); + // detect complicated paths and normalize them + hasThis = pathHasThis(path); - if (obj._streamBindingSubscriptions === undefined) { - obj._streamBindingSubscriptions = o_create(null); + if (!root || hasThis) { + tuple = normalizeTuple(root, path); + root = tuple[0]; + path = tuple[1]; + tuple.length = 0; } - obj._streamBindingSubscriptions[key] = onNotify; - } - - function connectBindings(obj, m) { - // TODO Mixin.apply(instance) should disconnect binding if exists - var bindings = m.bindings; - var key, binding, to; - if (bindings) { - for (key in bindings) { - binding = bindings[key]; - if (binding) { - to = key.slice(0, -7); // strip Binding off end - if (binding.isStream) { - connectStreamBinding(obj, to, binding); - continue; - } else if (binding instanceof Binding) { - binding = binding.copy(); // copy prototypes' instance - binding.to(to); - } else { // binding is string path - binding = new Binding(to, binding); - } - binding.connect(obj); - obj[key] = binding; - } - } - // mark as applied - m.bindings = {}; + 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; } - function finishPartial(obj, m) { - connectBindings(obj, m || metaFor(obj)); - return obj; + function getWithDefault(root, key, defaultValue) { + var value = get(root, key); + + if (value === undefined) { return defaultValue; } + return value; } - 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]; - } + __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; - return { desc: desc, value: value }; - } + var IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/; - function updateObserversAndListeners(obj, key, observerOrListener, pathsKey, updateMethod) { - var paths = observerOrListener[pathsKey]; + /** + 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. - if (paths) { - for (var i=0, l=paths.length; i 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; + }; - desc = descs[key]; - value = values[key]; + function setPath(root, path, value, tolerant) { + var keyName; - if (desc === REQUIRED) { continue; } + // get the last part of the path + keyName = path.slice(path.lastIndexOf('.') + 1); - while (desc && desc instanceof Alias) { - var followed = followAlias(obj, desc, m, descs, values); - desc = followed.desc; - value = followed.value; - } + // get the first part of the part + path = (path === keyName) ? keyName : path.slice(0, path.length-(keyName.length+1)); - if (desc === undefined && value === undefined) { continue; } + // unless the path is this, look up the first part to + // get the root + if (path !== 'this') { + root = getPath(root, path); + } - replaceObserversAndListeners(obj, key, value); - detectBinding(obj, key, value, m); - defineProperty(obj, key, desc, value, m); + if (!keyName || keyName.length === 0) { + throw new EmberError('Property set failed: You passed an empty path'); } - if (!partial) { // don't apply to prototype - finishPartial(obj, m); + if (!root) { + if (tolerant) { return; } + else { throw new EmberError('Property set failed: object in path "'+path+'" could not be found or was destroyed.'); } } - return obj; + return set(root, keyName, value); } /** - @method mixin + 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 obj - @param mixins* - @return obj + @param {Object} obj The object to modify. + @param {String} path The property path to set + @param {Object} value The value to set */ - function mixin(obj) { - var args = a_slice.call(arguments, 1); - applyMixin(obj, args, false); - return obj; + function trySet(root, path, value) { + return set(root, path, value, true); } - __exports__.mixin = mixin;/** - The `Ember.Mixin` class allows you to create mixins, whose properties can be - added to other classes. For instance, - - ```javascript - App.Editable = Ember.Mixin.create({ - edit: function() { - console.log('starting to edit'); - this.set('isEditing', true); - }, - isEditing: false - }); + __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"]; - // 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}}') - }); + function onBegin(current) { + run.currentRunLoop = current; + } - commentView = App.CommentView.create(); - commentView.edit(); // outputs 'starting to edit' - ``` + function onEnd(current, next) { + run.currentRunLoop = next; + } - Note that Mixins are created with `Ember.Mixin.create`, not - `Ember.Mixin.extend`. + // 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' + }); + var slice = [].slice; - 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. + // .......................................................... + // run - this is ideally the only public API the dev sees + // - ```javascript - //filters array will be shared amongst any object implementing mixin - App.Filterable = Ember.Mixin.create({ - filters: Ember.A() - }); + /** + Runs the passed target and method inside of a RunLoop, ensuring any + deferred actions including bindings and views updates are flushed at the + end. - //filters will be a separate array for every object implementing the mixin - App.Filterable = Ember.Mixin.create({ - filters: Ember.computed(function(){return Ember.A();}) - }); + 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. - //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()); - } + ```javascript + run(function() { + // code to be executed within a RunLoop }); ``` - @class Mixin + @class run @namespace Ember - */ - __exports__["default"] = Mixin; - function Mixin(args, properties) { - this.properties = properties; - - var length = args && args.length; - - 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; - - /** - @method create @static - @param arguments* + @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. */ - 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); - }; - - var MixinPrototype = Mixin.prototype; + __exports__["default"] = run; + function run() { + return backburner.run.apply(backburner, arguments); + } /** - @method reopen - @param arguments* - */ - MixinPrototype.reopen = function() { - var mixin; + 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. - if (this.properties) { - mixin = new Mixin(undefined, this.properties); - this.properties = undefined; - this.mixins = [mixin]; - } else if (!this.mixins) { - this.mixins = []; - } + Please note: This is not for normal usage, and should be used sparingly. - var len = arguments.length; - var mixins = this.mixins; - var idx; + If invoked when not within a run loop: - 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]'); + ```javascript + run.join(function() { + // creates a new run-loop + }); + ``` - if (mixin instanceof Mixin) { - mixins.push(mixin); - } else { - mixins.push(new Mixin(undefined, mixin)); - } - } + Alternatively, if called within an existing run loop: - return this; - }; + ```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. + }); + }); + ``` - /** - @method apply - @param obj - @return applied object + @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. */ - MixinPrototype.apply = function(obj) { - return applyMixin(obj, [this], false); - }; - - MixinPrototype.applyPartial = function(obj) { - return applyMixin(obj, [this], true); + run.join = function() { + return backburner.join.apply(backburner, arguments); }; - function _detect(curMixin, targetMixin, seen) { - var guid = guidFor(curMixin); - - if (seen[guid]) { return false; } - seen[guid] = true; - - 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; - } - /** - @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; - }; + 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. - MixinPrototype.without = function() { - var ret = new Mixin([this]); - ret._without = a_slice.call(arguments); - return ret; - }; + `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. - function _keys(ret, mixin, seen) { - if (seen[guidFor(mixin)]) { return; } - seen[guidFor(mixin)] = true; + 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: - 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); }); - } - } + ```javascript + App.RichTextEditorComponent = Ember.Component.extend({ + initializeTinyMCE: function(){ + tinymce.init({ + selector: '#' + this.$().prop('id'), + setup: Ember.run.bind(this, this.setupEditor) + }); + }.on('didInsertElement'), - 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); + setupEditor: function(editor) { + this.set('editor', editor); + editor.on('change', function(){ console.log('content changed!')} ); } - } - return 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); } - } + 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. - return ret; + @method bind + @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 {Function} returns a new function that will always have a particular context + @since 1.4.0 + */ + run.bind = function(target, method /* args */) { + var args = slice.call(arguments); + return function() { + return run.join.apply(run, args.concat(slice.call(arguments))); + }; }; - REQUIRED = new Descriptor(); - REQUIRED.toString = function() { return '(Required Property)'; }; + run.backburner = backburner; + run.currentRunLoop = null; + run.queues = backburner.queueNames; /** - Denotes a required property for a mixin - - @method required - @for Ember - */ - function required() { - return REQUIRED; - } + 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()`. - __exports__.required = required;function Alias(methodName) { - this.methodName = methodName; - } + ```javascript + run.begin(); + // code to be executed within a RunLoop + run.end(); + ``` - Alias.prototype = new Descriptor(); + @method begin + @return {void} + */ + run.begin = function() { + backburner.begin(); + }; /** - Makes a method available via an additional name. + 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()`. ```javascript - 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' + run.begin(); + // code to be executed within a RunLoop + run.end(); ``` - @method aliasMethod - @for Ember - @param {String} methodName name of the method to alias - @return {Ember.Descriptor} + @method end + @return {void} */ - function aliasMethod(methodName) { - return new Alias(methodName); - } + run.end = function() { + backburner.end(); + }; - __exports__.aliasMethod = aliasMethod;// .......................................................... - // OBSERVER HELPER - // + /** + 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. + + @property queues + @type Array + @default ['sync', 'actions', 'destroy'] + */ /** - Specify a method that observes property changes. + 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. + + 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. ```javascript - Ember.Object.extend({ - valueObserver: Ember.observer('value', function() { - // Executes whenever the "value" property changes - }) + run.schedule('sync', this, function() { + // this will be executed in the first RunLoop queue, when bindings are synced + console.log("scheduled on sync queue"); }); - ``` - In the future this method may become asynchronous. If you want to ensure - synchronous behavior, use `immediateObserver`. + run.schedule('actions', this, function() { + // this will be executed in the 'actions' queue, after bindings have synced. + console.log("scheduled on actions queue"); + }); - Also available as `Function.prototype.observes` if prototype extensions are - enabled. + // 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 + ``` - @method observer - @for Ember - @param {String} propertyNames* - @param {Function} func - @return func + @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} */ - function observer() { - var func = a_slice.call(arguments, -1)[0]; - var paths; + run.schedule = function(queue, target, method) { + checkAutoRun(); + backburner.schedule.apply(backburner, arguments); + }; - var addWatchedProperty = function (path) { paths.push(path); }; - var _paths = a_slice.call(arguments, 0, -1); + // Used by global test teardown + run.hasScheduledTimers = function() { + return backburner.hasTimers(); + }; - if (typeof func !== "function") { - // revert to old, soft-deprecated argument ordering + // Used by global test teardown + run.cancelTimers = function () { + backburner.cancelTimers(); + }; - func = arguments[0]; - _paths = a_slice.call(arguments, 1); - } + /** + 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. - paths = []; + 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). - for (var i=0; i<_paths.length; ++i) { - expandProperties(_paths[i], addWatchedProperty); - } + ```javascript + run.sync(); + ``` - if (typeof func !== "function") { - throw new Ember.Error("Ember.observer called without a function"); + @method sync + @return {void} + */ + run.sync = function() { + if (backburner.currentInstance) { + backburner.currentInstance.queues.sync.flush(); } + }; - func.__ember_observes__ = paths; - return func; - } + /** + 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. - __exports__.observer = observer;/** - Specify a method that observes property changes. + 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. ```javascript - Ember.Object.extend({ - valueObserver: Ember.immediateObserver('value', function() { - // Executes whenever the "value" property changes - }) - }); + run.later(myContext, function() { + // code here will execute within a RunLoop in about 500ms with this == myContext + }, 500); ``` - In the future, `Ember.observer` may become asynchronous. In this event, - `Ember.immediateObserver` will maintain the synchronous behavior. + @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); + }; - Also available as `Function.prototype.observesImmediately` if prototype extensions are - enabled. + /** + Schedule a function to run one time during the current RunLoop. This is equivalent + to calling `scheduleOnce` with the "actions" queue. - @method immediateObserver - @for Ember - @param {String} propertyNames* - @param {Function} func - @return func + @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`. */ - function immediateObserver() { - for (var i=0, l=arguments.length; i this.changingFrom ? 'green' : 'red'; - // logic - } - }), + 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.: - friendsDidChange: Ember.observer('friends.@each.name', function(obj, keyName) { - // some logic - // obj.get(keyName) returns friends array - }) - }); + ```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. ``` - Also available as `Function.prototype.observesBefore` if prototype extensions are - enabled. + Available queues, and their order, can be found at `run.queues` - @method beforeObserver - @for Ember - @param {String} propertyNames* - @param {Function} func - @return func + @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`. */ - function beforeObserver() { - var func = a_slice.call(arguments, -1)[0]; - var paths; + run.scheduleOnce = function(/*queue, target, method*/) { + checkAutoRun(); + return backburner.scheduleOnce.apply(backburner, arguments); + }; - var addWatchedProperty = function(path) { paths.push(path); }; + /** + 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. - var _paths = a_slice.call(arguments, 0, -1); + ```javascript + run.next(myContext, function() { + // code to be executed in the next run loop, + // which will be scheduled after the current one + }); + ``` - if (typeof func !== "function") { - // revert to old, soft-deprecated argument ordering + 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. - func = arguments[0]; - _paths = a_slice.call(arguments, 1); - } + 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: - paths = []; + ```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. + } + }); + ``` - for (var i=0; i<_paths.length; ++i) { - expandProperties(_paths[i], addWatchedProperty); - } + 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. - if (typeof func !== "function") { - throw new Ember.Error("Ember.beforeObserver called without a function"); - } + 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`. - func.__ember_observesBefore__ = paths; - return func; - } + @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); + }; - __exports__.beforeObserver = beforeObserver;__exports__.IS_BINDING = IS_BINDING; - __exports__.Mixin = Mixin; - }); -enifed("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 - */ + Cancels a scheduled item. Must be a value returned by `run.later()`, + `run.once()`, `run.next()`, `run.debounce()`, or + `run.throttle()`. - var AFTER_OBSERVERS = ':change'; - var BEFORE_OBSERVERS = ':before'; + ```javascript + var runNext = run.next(myContext, function() { + // will not be executed + }); + run.cancel(runNext); - function changeEvent(keyName) { - return keyName + AFTER_OBSERVERS; - } + var runLater = run.later(myContext, function() { + // will not be executed + }, 500); + run.cancel(runLater); - function beforeEvent(keyName) { - return keyName + BEFORE_OBSERVERS; - } + var runOnce = run.once(myContext, function() { + // will not be executed + }); + run.cancel(runOnce); - /** - @method addObserver - @for Ember - @param obj - @param {String} path - @param {Object|Function} targetOrMethod - @param {Function|String} [method] - */ - function addObserver(obj, _path, target, method) { - addListener(obj, changeEvent(_path), target, method); - watch(obj, _path); + var throttle = run.throttle(myContext, function() { + // will not be executed + }, 1, false); + run.cancel(throttle); - return this; - } + var debounce = run.debounce(myContext, function() { + // will not be executed + }, 1); + run.cancel(debounce); - __exports__.addObserver = addObserver;function observersFor(obj, path) { - return listenersFor(obj, changeEvent(path)); - } + 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); + ``` - __exports__.observersFor = observersFor;/** - @method removeObserver - @for Ember - @param obj - @param {String} path - @param {Object|Function} target - @param {Function|String} [method] + @method cancel + @param {Object} timer Timer object to cancel + @return {Boolean} true if cancelled or false/undefined if it wasn't found */ - function removeObserver(obj, path, target, method) { - unwatch(obj, path); - removeListener(obj, changeEvent(path), target, method); + run.cancel = function(timer) { + return backburner.cancel(timer); + }; - return this; - } + /** + 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. - __exports__.removeObserver = removeObserver;/** - @method addBeforeObserver - @for Ember - @param obj - @param {String} path - @param {Object|Function} target - @param {Function|String} [method] - */ - function addBeforeObserver(obj, path, target, method) { - addListener(obj, beforeEvent(path), target, method); - watch(obj, path); + 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. - return this; - } + ```javascript + var myFunc = function() { console.log(this.name + ' ran.'); }; + var myContext = {name: 'debounce'}; - __exports__.addBeforeObserver = addBeforeObserver;// 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); - } + run.debounce(myContext, myFunc, 150); - __exports__._suspendBeforeObserver = _suspendBeforeObserver;function _suspendObserver(obj, path, target, method, callback) { - return suspendListener(obj, changeEvent(path), target, method, callback); - } + // less than 150ms passes - __exports__._suspendObserver = _suspendObserver;function _suspendBeforeObservers(obj, paths, target, method, callback) { - var events = map.call(paths, beforeEvent); - return suspendListeners(obj, events, target, method, callback); - } + run.debounce(myContext, myFunc, 150); - __exports__._suspendBeforeObservers = _suspendBeforeObservers;function _suspendObservers(obj, paths, target, method, callback) { - var events = map.call(paths, changeEvent); - return suspendListeners(obj, events, target, method, callback); - } + // 150ms passes + // myFunc is invoked with context myContext + // console logs 'debounce ran.' one time. + ``` - __exports__._suspendObservers = _suspendObservers;function beforeObserversFor(obj, path) { - return listenersFor(obj, beforeEvent(path)); - } + 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. - __exports__.beforeObserversFor = beforeObserversFor;/** - @method removeBeforeObserver - @for Ember - @param obj - @param {String} path - @param {Object|Function} target - @param {Function|String} [method] - */ - function removeBeforeObserver(obj, path, target, method) { - unwatch(obj, path); - removeListener(obj, beforeEvent(path), target, method); + ```javascript + var myFunc = function() { console.log(this.name + ' ran.'); }; + var myContext = {name: 'debounce'}; - return this; - } + run.debounce(myContext, myFunc, 150, true); - __exports__.removeBeforeObserver = removeBeforeObserver; - }); -enifed("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; + // console logs 'debounce ran.' one time immediately. + // 100ms passes - /* - this.observerSet = { - [senderGuid]: { // variable name: `keySet` - [keyName]: listIndex - } - }, - this.observers = [ - { - sender: obj, - keyName: keyName, - eventName: eventName, - listeners: [ - [target, method, flags] - ] - }, - ... - ] - */ - __exports__["default"] = ObserverSet; - function ObserverSet() { - this.clear(); - } + run.debounce(myContext, myFunc, 150, true); + // 150ms passes and nothing else is logged to the console and + // the debouncee is no longer being watched - ObserverSet.prototype.add = function(sender, keyName, eventName) { - var observerSet = this.observerSet; - var observers = this.observers; - var senderGuid = guidFor(sender); - var keySet = observerSet[senderGuid]; - var index; + run.debounce(myContext, myFunc, 150, true); - 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; - }; + // 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 - ObserverSet.prototype.flush = function() { - var observers = this.observers; - var 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); - } - }; + ``` - ObserverSet.prototype.clear = function() { - this.observerSet = {}; - this.observers = []; + @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); }; - }); -enifed("ember-metal/path_cache", - ["ember-metal/cache","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Cache = __dependency1__["default"]; - var IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/; - var IS_GLOBAL_PATH = /^([A-Z$]|([0-9][A-Z$])).*[\.]/; - var HAS_THIS = 'this.'; + /** + Ensure that the target method is never called more frequently than + the specified spacing period. The target method is called immediately. - var isGlobalCache = new Cache(1000, function(key) { return IS_GLOBAL.test(key); }); - var isGlobalPathCache = new Cache(1000, function(key) { return IS_GLOBAL_PATH.test(key); }); - var hasThisCache = new Cache(1000, function(key) { return key.indexOf(HAS_THIS) !== -1; }); - var firstDotIndexCache = new Cache(1000, function(key) { return key.indexOf('.'); }); + ```javascript + var myFunc = function() { console.log(this.name + ' ran.'); }; + var myContext = {name: 'throttle'}; - var firstKeyCache = new Cache(1000, function(path) { - var index = firstDotIndexCache.get(path); - if (index === -1) { - return path; - } else { - return path.slice(0, index); - } - }); + run.throttle(myContext, myFunc, 150); + // myFunc is invoked with context myContext + // console logs 'throttle ran.' - var tailPathCache = new Cache(1000, function(path) { - var index = firstDotIndexCache.get(path); - if (index !== -1) { - return path.slice(index + 1); - } - }); + // 50ms passes + run.throttle(myContext, myFunc, 150); - var caches = { - isGlobalCache: isGlobalCache, - isGlobalPathCache: isGlobalPathCache, - hasThisCache: hasThisCache, - firstDotIndexCache: firstDotIndexCache, - firstKeyCache: firstKeyCache, - tailPathCache: tailPathCache - }; - __exports__.caches = caches; - function isGlobal(path) { - return isGlobalCache.get(path); - } + // 50ms passes + run.throttle(myContext, myFunc, 150); - __exports__.isGlobal = isGlobal;function isGlobalPath(path) { - return isGlobalPathCache.get(path); - } + // 150ms passes + run.throttle(myContext, myFunc, 150); + // myFunc is invoked with context myContext + // console logs 'throttle ran.' + ``` - __exports__.isGlobalPath = isGlobalPath;function hasThis(path) { - return hasThisCache.get(path); - } + @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); + }; - __exports__.hasThis = hasThis;function isPath(path) { - return firstDotIndexCache.get(path) !== -1; + // 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 a run", !Ember.testing); + } } - __exports__.isPath = isPath;function getFirstKey(path) { - return firstKeyCache.get(path); - } + /** + Add a new named queue after the specified queue. - __exports__.getFirstKey = getFirstKey;function getTailPath(path) { - return tailPathCache.get(path); - } + The queue to add will only be added once. - __exports__.getTailPath = getTailPath; + @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/platform", - ["ember-metal/platform/define_property","ember-metal/platform/define_properties","ember-metal/platform/create","exports"], +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 hasES5CompliantDefineProperty = __dependency1__.hasES5CompliantDefineProperty; - var defineProperty = __dependency1__.defineProperty; - var defineProperties = __dependency2__["default"]; - var create = __dependency3__["default"]; + var changeProperties = __dependency1__.changeProperties; + var set = __dependency2__.set; + var keys = __dependency3__["default"]; /** - @module ember-metal - */ + Set a list of properties on an object. These properties are set inside + a single `beginPropertyChanges` and `endPropertyChanges` batch, so + observers will be buffered. - var hasPropertyAccessors = hasES5CompliantDefineProperty; - var canDefineNonEnumerableProperties = hasES5CompliantDefineProperty; + ```javascript + var anObject = Ember.Object.create(); - /** - Platform specific methods and feature detectors needed by the framework. + anObject.setProperties({ + firstName: 'Stanley', + lastName: 'Stuart', + age: 21 + }); + ``` - @class platform - @namespace Ember - @static + @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; - __exports__.create = create; - __exports__.defineProperty = defineProperty; - __exports__.defineProperties = defineProperties; - __exports__.hasPropertyAccessors = hasPropertyAccessors; - __exports__.canDefineNonEnumerableProperties = canDefineNonEnumerableProperties; + for (var i = 0, l = props.length; i < l; i++) { + propertyName = props[i]; + + set(obj, propertyName, properties[propertyName]); + } + }); + return obj; + } }); -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 +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; - /** - @class platform - @namespace Ember - @static - */ + function SimpleStream(source) { + this.init(); + this.source = source; - /** - Identical to `Object.create()`. Implements if not available natively. + if (isStream(source)) { + source.subscribe(this._didChange, this); + } + } - @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; + SimpleStream.prototype = create(Stream.prototype); - function Empty() {} - Empty.prototype = empty; - // short-circuit future calls - createEmpty = function () { - return new Empty(); - }; - return new Empty(); - }; - } + merge(SimpleStream.prototype, { + valueFn: function() { + return read(this.source); + }, - create = Object.create = function create(prototype, properties) { + setValue: function(value) { + var source = this.source; - var object; - function Type() {} // An empty constructor. + if (isStream(source)) { + source.setValue(value); + } + }, - 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 + setSource: function(nextSource) { + var prevSource = this.source; + if (nextSource !== prevSource) { + if (isStream(prevSource)) { + prevSource.unsubscribe(this._didChange, this); } - Type.prototype = prototype; + if (isStream(nextSource)) { + nextSource.subscribe(this._didChange, this); + } - object = new Type(); + this.source = nextSource; + this.notify(); } + }, - if (properties !== undefined) { - Object.defineProperties(object, properties); - } + _didChange: function() { + this.notify(); + }, - return object; - }; - } else { - create = Object.create; - } + _super$destroy: Stream.prototype.destroy, - __exports__["default"] = create; + 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/platform/define_properties", - ["ember-metal/platform/define_property","exports"], - function(__dependency1__, __exports__) { +enifed("ember-metal/streams/stream", + ["ember-metal/platform","ember-metal/path_cache","exports"], + function(__dependency1__, __dependency2__, __exports__) { "use strict"; - var defineProperty = __dependency1__.defineProperty; + var create = __dependency1__.create; + var getFirstKey = __dependency2__.getFirstKey; + var getTailPath = __dependency2__.getTailPath; - var defineProperties = Object.defineProperties; + function Stream(fn) { + this.init(); + this.valueFn = fn; + } - // 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]); - } + 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); } - return object; - }; - Object.defineProperties = defineProperties; - } + var keyStream = this.children[firstKey]; - __exports__["default"] = defineProperties; - }); -enifed("ember-metal/platform/define_property", - ["exports"], - function(__exports__) { - "use strict"; - /*globals Node */ + if (keyStream === undefined) { + keyStream = this._makeChildStream(firstKey, path); + this.children[firstKey] = keyStream; + } - /** - @class platform - @namespace Ember - @static - */ + if (tailPath === undefined) { + return keyStream; + } else { + return keyStream.get(tailPath); + } + }, - /** - Set to true if the platform supports native getters and setters. + 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); + }, - @property hasPropertyAccessors - @final - */ + valueFn: function() { + throw new Error("Stream error: valueFn not implemented"); + }, - /** - Identical to `Object.defineProperty()`. Implements as much functionality - as possible if not available natively. + setValue: function() { + throw new Error("Stream error: setValue not implemented"); + }, - @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; + notify: function() { + this.notifyExcept(); + }, - // check non-enumerability - defineProperty(obj, 'a', { - configurable: true, - enumerable: false, - writable: true, - value: true - }); - for (var key in obj) { - if (key === 'a') return; + notifyExcept: function(callbackToSkip, contextToSkip) { + if (this.state === 'clean') { + this.state = 'dirty'; + this._notifySubscribers(callbackToSkip, contextToSkip); } + }, - // 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; + subscribe: function(callback, context) { + if (this.subscribers === undefined) { + this.subscribers = [callback, context]; + } else { + this.subscribers.push(callback, context); + } + }, - // defineProperty is compliant - return defineProperty; - } catch (e) { - // IE8 defines Object.defineProperty but calling it on an Object throws - return; - } - })(Object.defineProperty); + unsubscribe: function(callback, context) { + var subscribers = this.subscribers; - var hasES5CompliantDefineProperty = !!defineProperty; + 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; + } + } + } + }, - 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) { } + _notifySubscribers: function(callbackToSkip, contextToSkip) { + var subscribers = this.subscribers; - return false; - })(); + if (subscribers !== undefined) { + for (var i = 0, l = subscribers.length; i < l; i += 2) { + var callback = subscribers[i]; + var context = subscribers[i+1]; - if (!canDefinePropertyOnDOM) { - defineProperty = function(obj, keyName, desc) { - var isNode; + if (callback === callbackToSkip && context === contextToSkip) { + continue; + } - if (typeof Node === "object") { - isNode = obj instanceof Node; - } else { - isNode = typeof obj === "object" && typeof obj.nodeType === "number" && typeof obj.nodeName === "string"; + 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(); } - if (isNode) { - // TODO: Should we have a warning here? - return (obj[keyName] = desc.value); - } else { - return Object.defineProperty(obj, keyName, desc); + return true; + } + }, + + isGlobal: function() { + var stream = this; + while (stream !== undefined) { + if (stream._isRoot) { + return stream._isGlobal; } - }; + stream = stream.source; + } } - } - - if (!hasES5CompliantDefineProperty) { - defineProperty = function defineProperty(obj, keyName, desc) { - if (!desc.get) { obj[keyName] = desc.value; } - }; - } + }; - __exports__.hasES5CompliantDefineProperty = hasES5CompliantDefineProperty; - __exports__.defineProperty = defineProperty; + __exports__["default"] = Stream; }); -enifed("ember-metal/properties", - ["ember-metal/core","ember-metal/utils","ember-metal/platform","ember-metal/property_events","exports"], +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"; - /** - @module ember-metal - */ - - var Ember = __dependency1__["default"]; - var metaFor = __dependency2__.meta; - var objectDefineProperty = __dependency3__.defineProperty; - var hasPropertyAccessors = __dependency3__.hasPropertyAccessors; - var overrideChains = __dependency4__.overrideChains; - // .......................................................... - // DESCRIPTOR - // - - /** - Objects of this type can implement an interface to respond to requests to - get and set. The default implementation handles simple properties. - - You generally won't need to create or subclass this directly. - - @class Descriptor - @namespace Ember - @private - @constructor - */ - function Descriptor() {} + var create = __dependency1__.create; + var merge = __dependency2__["default"]; + var run = __dependency3__["default"]; + var Stream = __dependency4__["default"]; - __exports__.Descriptor = Descriptor;// .......................................................... - // DEFINING PROPERTIES API - // + function StreamBinding(stream) { + Ember.assert("StreamBinding error: tried to bind to object that is not a stream", stream && stream.isStream); - 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); - }; - } + this.init(); + this.stream = stream; + this.senderCallback = undefined; + this.senderContext = undefined; + this.senderValue = undefined; - __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]; - }; + stream.subscribe(this._onNotify, this); } - __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. - - 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. - - 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. - - ## Examples + StreamBinding.prototype = create(Stream.prototype); - ```javascript - // ES5 compatible mode - Ember.defineProperty(contact, 'firstName', { - writable: true, - configurable: false, - enumerable: true, - value: 'Charles' - }); + merge(StreamBinding.prototype, { + valueFn: function() { + return this.stream.value(); + }, - // define a simple property - Ember.defineProperty(contact, 'lastName', undefined, 'Jolley'); + _onNotify: function() { + this._scheduleSync(undefined, undefined, this); + }, - // define a computed property - Ember.defineProperty(contact, 'fullName', Ember.computed(function() { - return this.firstName+' '+this.lastName; - }).property('firstName', 'lastName')); - ``` + setValue: function(value, callback, context) { + this._scheduleSync(value, callback, context); + }, - @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; + _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; + } + }, - if (!meta) meta = metaFor(obj); - descs = meta.descs; - existingDesc = meta.descs[keyName]; - var watchEntry = meta.watching[keyName]; + _sync: function() { + if (this.state === 'destroyed') { + return; + } - watching = watchEntry !== undefined && watchEntry > 0; + if (this.senderContext !== this) { + this.stream.setValue(this.senderValue); + } - if (existingDesc instanceof Descriptor) { - existingDesc.teardown(obj, keyName); - } + var senderCallback = this.senderCallback; + var senderContext = this.senderContext; + this.senderCallback = undefined; + this.senderContext = undefined; + this.senderValue = undefined; - if (desc instanceof Descriptor) { - value = desc; + // Force StreamBindings to always notify + this.state = 'clean'; - descs[keyName] = desc; - - if (watching && hasPropertyAccessors) { - objectDefineProperty(obj, keyName, { - configurable: true, - enumerable: true, - writable: true, - value: undefined // make enumerable - }); - } else { - 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; + this.notifyExcept(senderCallback, senderContext); + }, - - 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; + _super$destroy: Stream.prototype.destroy, - // compatibility with ES5 - objectDefineProperty(obj, keyName, desc); + destroy: function() { + if (this._super$destroy()) { + this.stream.unsubscribe(this._onNotify, this); + return true; } } + }); - // 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; + __exports__["default"] = StreamBinding; }); -enifed("ember-metal/property_events", - ["ember-metal/utils","ember-metal/events","ember-metal/observer_set","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { +enifed("ember-metal/streams/utils", + ["./stream","exports"], + function(__dependency1__, __exports__) { "use strict"; - var guidFor = __dependency1__.guidFor; - var tryFinally = __dependency1__.tryFinally; - var sendEvent = __dependency2__.sendEvent; - var listenersUnion = __dependency2__.listenersUnion; - var listenersDiff = __dependency2__.listenersDiff; - var ObserverSet = __dependency3__["default"]; - - var beforeObserverSet = new ObserverSet(); - var observerSet = new ObserverSet(); - var deferred = 0; - - // .......................................................... - // PROPERTY CHANGES - // + var Stream = __dependency1__["default"]; /** - 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. - - 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. + Check whether an object is a stream or not - @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} + @private + @function isStream + @param {Object|Stream} object object to check whether it is a stream + @return {Boolean} `true` if the object is a stream, `false` otherwise */ - 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]; + function isStream(object) { + return object && object.isStream; + } - if (!watching) { - return; - } + __exports__.isStream = isStream;/** + A method of subscribing to a stream which is safe for use with a non-stream + object. If a non-stream object is passed, the function does nothing. - if (proto === obj) { - return; + @private + @function subscribe + @param {Object|Stream} object object or stream to potentially subscribe to + @param {Function} callback function to run when stream value changes + @param {Object} [context] the callback will be executed with this context if it + is provided + */ + function subscribe(object, callback, context) { + if (object && object.isStream) { + object.subscribe(callback, context); } + } - if (desc && desc.willChange) { - desc.willChange(obj, keyName); + __exports__.subscribe = subscribe;/** + A method of unsubscribing from a stream which is safe for use with a non-stream + object. If a non-stream object is passed, the function does nothing. + + @private + @function unsubscribe + @param {Object|Stream} object object or stream to potentially unsubscribe from + @param {Function} callback function originally passed to `subscribe()` + @param {Object} [context] object originally passed to `subscribe()` + */ + function unsubscribe(object, callback, context) { + if (object && object.isStream) { + object.unsubscribe(callback, context); } - - dependentKeysWillChange(obj, keyName, m); - chainsWillChange(obj, keyName, m); - notifyBeforeObservers(obj, keyName); } - /** - This function is called just after an object property has changed. - It will notify any observers and clear caches among other things. - - 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. - - @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['__ember_meta__']; - var watching = (m && m.watching[keyName] > 0) || keyName === 'length'; - var proto = m && m.proto; - var desc = m && m.descs[keyName]; + __exports__.unsubscribe = unsubscribe;/** + Retrieve the value of a stream, or in the case a non-stream object is passed, + return the object itself. - if (proto === obj) { - return; + @private + @function read + @param {Object|Stream} object object to return the value of + @return the stream's current value, or the non-stream object itself + */ + function read(object) { + if (object && object.isStream) { + return object.value(); + } else { + return object; } + } - // shouldn't this mean that we're watching this key? - if (desc && desc.didChange) { - desc.didChange(obj, keyName); - } + __exports__.read = read;/** + Map an array, replacing any streams with their values. - if (!watching && keyName !== 'length') { - return; + @private + @function readArray + @param {Array} array The array to read values from + @return {Array} a new array of the same length with the values of non-stream + objects mapped from their original positions untouched, and + the values of stream objects retaining their original position + and replaced with the stream's current value. + */ + 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; + } - if (m && m.deps && m.deps[keyName]) { - dependentKeysDidChange(obj, keyName, m); - } + __exports__.readArray = readArray;/** + Map a hash, replacing any stream property values with the current value of that + stream. - chainsDidChange(obj, keyName, m, false); - notifyObservers(obj, keyName); + @private + @function readHash + @param {Object} object The hash to read keys and values from + @return {Object} a new object with the same keys as the passed object. The + property values in the new object are the original values in + the case of non-stream objects, and the streams' current + values in the case of stream objects. + */ + function readHash(object) { + var ret = {}; + for (var key in object) { + ret[key] = read(object[key]); + } + return ret; } - 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; } + __exports__.readHash = readHash;/** + Check whether an array contains any stream values - var deps; - if (meta && meta.deps && (deps = meta.deps[depKey])) { - var seen = WILL_SEEN; - var top = !seen; + @private + @function scanArray + @param {Array} array array given to a handlebars helper + @return {Boolean} `true` if the array contains a stream/bound value, `false` + otherwise + */ + function scanArray(array) { + var length = array.length; + var containsStream = false; - if (top) { - seen = WILL_SEEN = {}; + for (var i = 0; i < length; i++){ + if (isStream(array[i])) { + containsStream = true; + break; } + } - iterDeps(propertyWillChange, obj, deps, depKey, seen, meta); + return containsStream; + } - if (top) { - WILL_SEEN = null; + __exports__.scanArray = scanArray;/** + Check whether a hash has any stream property values + + @private + @function scanHash + @param {Object} hash "hash" argument given to a handlebars helper + @return {Boolean} `true` if the object contains a stream/bound value, `false` + otherwise + */ + function scanHash(hash) { + var containsStream = false; + + for (var prop in hash) { + if (isStream(hash[prop])) { + containsStream = true; + break; } } + + return containsStream; } - // called whenever a property has just changed to update dependent keys - function dependentKeysDidChange(obj, depKey, meta) { - if (obj.isDestroying) { return; } + __exports__.scanHash = scanHash;/** + Join an array, with any streams replaced by their current values - var deps; - if (meta && meta.deps && (deps = meta.deps[depKey])) { - var seen = DID_SEEN; - var top = !seen; + @private + @function concat + @param {Array} array An array containing zero or more stream objects and + zero or more non-stream objects + @param {String} separator string to be used to join array elements + @return {String} String with array elements concatenated and joined by the + provided separator, and any stream array members having been + replaced by the current value of the stream + */ + function concat(array, separator) { + // TODO: Create subclass ConcatStream < Stream. Defer + // subscribing to streams until the value() is called. + var hasStream = scanArray(array); + if (hasStream) { + var i, l; + var stream = new Stream(function() { + return readArray(array).join(separator); + }); - if (top) { - seen = DID_SEEN = {}; + for (i = 0, l=array.length; i < l; i++) { + subscribe(array[i], stream.notify, stream); } - iterDeps(propertyDidChange, obj, deps, depKey, seen, meta); - - if (top) { - DID_SEEN = null; - } + return stream; + } else { + return array.join(separator); } } - function keysOf(obj) { - var keys = []; + __exports__.concat = concat;/** + Generate a new stream by providing a source stream and a function that can + be used to transform the stream's value. In the case of a non-stream object, + returns the result of the function. - for (var key in obj) { - keys.push(key); - } + The value to transform would typically be available to the function you pass + to `chain()` via scope. For example: - return keys; + ```javascript + var source = ...; // stream returning a number + // or a numeric (non-stream) object + var result = chain(source, function(){ + var currentValue = read(source); + return currentValue + 1; + }); + ``` + + In the example, result is a stream if source is a stream, or a number of + source was numeric. + + @private + @function chain + @param {Object|Stream} value A stream or non-stream object + @param {Function} fn function to be run when the stream value changes, or to + be run once in the case of a non-stream object + @return {Object|Stream} In the case of a stream `value` parameter, a new + stream that will be updated with the return value of + the provided function `fn`. In the case of a + non-stream object, the return value of the provided + function `fn`. + */ + function chain(value, fn) { + if (isStream(value)) { + var stream = new Stream(fn); + subscribe(value, stream.notify, stream); + return stream; + } else { + return fn(); + } } - function iterDeps(method, obj, deps, depKey, seen, meta) { - var keys, key, i, desc; - var guid = guidFor(obj); - var current = seen[guid]; + __exports__.chain = chain; + }); +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 - if (!current) { - current = seen[guid] = {}; - } + var Ember = __dependency1__["default"]; + var o_defineProperty = __dependency2__.defineProperty; + var canDefineNonEnumerableProperties = __dependency2__.canDefineNonEnumerableProperties; + var hasPropertyAccessors = __dependency2__.hasPropertyAccessors; + var o_create = __dependency2__.create; - if (current[depKey]) { - return; - } + var forEach = __dependency3__.forEach; - current[depKey] = true; + /** + @module ember-metal + */ - if (deps) { - keys = keysOf(deps); - var descs = meta.descs; - for (i=0; i 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); - } + 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; + } - return ret; - } + Meta.prototype = { + chainWatchers: null }; - // Currently used only by Ember Data tests - if (Ember.config.overrideAccessors) { - Ember.get = get; - Ember.config.overrideAccessors(); - get = Ember.get; + 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 = {}; + } + + /** - 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). + Retrieves the meta hash for an object. If `writable` is true ensures the + hash is writable for this object as well. - @private - @method normalizeTuple + 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 - @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; + @private - if (!target || isGlobal) target = Ember.lookup; - if (hasThis) path = path.slice(5); + @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; - 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+'.') - ); + if (!ret) { + if (canDefineNonEnumerableProperties) o_defineProperty(obj, '__ember_meta__', META_DESC); - if (target === Ember.lookup) { - key = path.match(FIRST_KEY)[0]; - target = get(target, key); - path = path.slice(key.length+1); - } + ret = new Meta(obj); - // 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'); + + if (hasPropertyAccessors) { + ret.values = {}; + } + - return [ target, path ]; - } + obj['__ember_meta__'] = ret; - function _getPath(root, path) { - var hasThis, parts, tuple, idx, len; + // make sure we don't accidentally try to create constructor like desc + ret.descs.constructor = null; - // 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); - } + } else if (ret.source !== obj) { + if (canDefineNonEnumerableProperties) o_defineProperty(obj, '__ember_meta__', META_DESC); - // detect complicated paths and normalize them - hasThis = pathHasThis(path); + ret = o_create(ret); + ret.descs = o_create(ret.descs); + ret.watching = o_create(ret.watching); + ret.cache = {}; + ret.cacheMeta = {}; + ret.source = obj; - if (!root || hasThis) { - tuple = normalizeTuple(root, path); - root = tuple[0]; - path = tuple[1]; - tuple.length = 0; - } + + if (hasPropertyAccessors) { + ret.values = o_create(ret.values); + } + - 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; } + obj['__ember_meta__'] = ret; } - return root; + return ret; } - function getWithDefault(root, key, defaultValue) { - var value = get(root, key); + function getMeta(obj, property) { + var _meta = meta(obj, false); + return _meta[property]; + } - if (value === undefined) { return defaultValue; } + __exports__.getMeta = getMeta;function setMeta(obj, property, value) { + var _meta = meta(obj, true); + _meta[property] = value; return value; } - __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; - - var IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/; - - /** - 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. + __exports__.setMeta = setMeta;/** + @deprecated + @private - @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 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; - } + 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. - Ember.assert("Cannot call set with "+ keyName +" key.", !!keyName); + 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. - if (!obj) { - return setPath(obj, keyName, value, tolerant); - } + 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. - var meta = obj['__ember_meta__']; - var desc = meta && meta.descs[keyName]; - var isUnknown, currentValue; + 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. - if (desc === undefined && isPath(keyName)) { - return setPath(obj, keyName, value, tolerant); - } + You can also pass false for `writable`, which will simply return + undefined if `prepareMetaPath` discovers any part of the path that + shared or undefined. - 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 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; - if (desc !== undefined) { - desc.set(obj, keyName, value); - } else { + for (var i=0, l=path.length; i 0) { - - 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; - } + _meta = value; } + return value; - }; + } + + __exports__.metaPath = metaPath;/** + Wraps the passed function so that `this._super` will point to the superFunc + when the function is invoked. This is the primitive we use to implement + calls to super. - // Currently used only by Ember Data tests - // ES6TODO: Verify still true - if (Ember.config.overrideAccessors) { - Ember.set = set; - Ember.config.overrideAccessors(); - set = Ember.set; - } + @private + @method wrap + @for Ember + @param {Function} func The function to call + @param {Function} superFunc The super function. + @return {Function} wrapped function. + */ - function setPath(root, path, value, tolerant) { - var keyName; + function wrap(func, superFunc) { + function superWrapper() { + var ret; + var sup = this && this.__nextSuper; + var length = arguments.length; - // get the last part of the path - keyName = path.slice(path.lastIndexOf('.') + 1); + if (this) { + this.__nextSuper = superFunc; + } - // get the first part of the part - path = (path === keyName) ? keyName : path.slice(0, path.length-(keyName.length+1)); + 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 { + var args = new Array(length); + for (var i = 0; i < length; i++) { + args[i] = arguments[i]; + } + ret = apply(this, func, args); + } - // unless the path is this, look up the first part to - // get the root - if (path !== 'this') { - root = getPath(root, path); - } + if (this) { + this.__nextSuper = sup; + } - if (!keyName || keyName.length === 0) { - throw new EmberError('Property set failed: You passed an empty path'); + return ret; } - if (!root) { - if (tolerant) { return; } - else { throw new EmberError('Property set failed: object in path "'+path+'" could not be found or was destroyed.'); } - } + superWrapper.wrappedFunction = func; + superWrapper.wrappedFunction.__ember_arity__ = func.length; + superWrapper.__ember_observes__ = func.__ember_observes__; + superWrapper.__ember_observesBefore__ = func.__ember_observesBefore__; + superWrapper.__ember_listens__ = func.__ember_listens__; - return set(root, keyName, value); + return superWrapper; } + __exports__.wrap = wrap;var EmberArray; + /** - Error-tolerant form of `Ember.set`. Will not blow up if any part of the - chain is `undefined`, `null`, or destroyed. + Returns true if the passed object is an array or Array-like. - This is primarily used when syncing bindings, which may try to update after - an object has been destroyed. + Ember Array Protocol: - @method trySet + - the object has an objectAt property + - the object is a native Array + - the object is an Object, and has a length property + + Unlike `Ember.typeOf` this method returns true even if the passed object is + not formally array but appears to be array-like (i.e. implements `Ember.Array`) + + ```javascript + Ember.isArray(); // false + Ember.isArray([]); // true + Ember.isArray(Ember.ArrayProxy.create({ content: [] })); // true + ``` + + @method isArray @for Ember - @param {Object} obj The object to modify. - @param {String} path The property path to set - @param {Object} value The value to set + @param {Object} obj The object to test + @return {Boolean} true if the passed object is an array or Array-like */ - function trySet(root, path, value) { - return set(root, path, value, true); - } + // ES6TODO: Move up to runtime? This is only use in ember-metal by concatenatedProperties + function isArray(obj) { + var modulePath, type; - __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"]; + if (typeof EmberArray === "undefined") { + modulePath = 'ember-runtime/mixins/array'; + if (Ember.__loader.registry[modulePath]) { + EmberArray = Ember.__loader.require(modulePath)['default']; + } + } - function onBegin(current) { - run.currentRunLoop = current; - } + if (!obj || obj.setInterval) { return false; } + if (Array.isArray && Array.isArray(obj)) { return true; } + if (EmberArray && EmberArray.detect(obj)) { return true; } - function onEnd(current, next) { - run.currentRunLoop = next; + type = typeOf(obj); + if ('array' === type) { return true; } + if ((obj.length !== undefined) && 'object' === type) { return true; } + return false; } - // 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' - }); - var slice = [].slice; - - // .......................................................... - // run - this is ideally the only public API the dev sees - // - /** - Runs the passed target and method inside of a RunLoop, ensuring any - deferred actions including bindings and views updates are flushed at the - end. - - 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. + Forces the passed object to be part of an array. If the object is already + an array or array-like, it will return the object. Otherwise, it will add the object to + an array. If obj is `null` or `undefined`, it will return an empty array. ```javascript - run(function() { - // code to be execute within a RunLoop - }); + Ember.makeArray(); // [] + Ember.makeArray(null); // [] + Ember.makeArray(undefined); // [] + Ember.makeArray('lindsay'); // ['lindsay'] + Ember.makeArray([1, 2, 42]); // [1, 2, 42] + + var controller = Ember.ArrayProxy.create({ content: [] }); + + Ember.makeArray(controller) === controller; // true ``` - @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. + @method makeArray + @for Ember + @param {Object} obj the object + @return {Array} */ - __exports__["default"] = run; - function run() { - return backburner.run.apply(backburner, arguments); + function makeArray(obj) { + if (obj === null || obj === undefined) { return []; } + return isArray(obj) ? obj : [obj]; } - /** - 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. - - Please note: This is not for normal usage, and should be used sparingly. - - If invoked when not within a run loop: + __exports__.makeArray = makeArray;/** + Checks to see if the `methodName` exists on the `obj`. ```javascript - run.join(function() { - // creates a new run-loop - }); - ``` - - Alternatively, if called within an existing run loop: + var foo = { bar: function() { return 'bar'; }, baz: null }; - ```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. - }); - }); + Ember.canInvoke(foo, 'bar'); // true + Ember.canInvoke(foo, 'baz'); // false + Ember.canInvoke(foo, 'bat'); // false ``` - @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. + @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} */ - run.join = function() { - return backburner.join.apply(backburner, arguments); - }; + function canInvoke(obj, methodName) { + return !!(obj && typeof obj[methodName] === 'function'); + } /** - 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. - - `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. - - 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: + Checks to see if the `methodName` exists on the `obj`, + and if it does, invokes it with the arguments passed. ```javascript - App.RichTextEditorComponent = Ember.Component.extend({ - initializeTinyMCE: function(){ - tinymce.init({ - selector: '#' + this.$().prop('id'), - setup: Ember.run.bind(this, this.setupEditor) - }); - }.on('didInsertElement'), + var d = new Date('03/15/2013'); - setupEditor: function(editor) { - this.set('editor', editor); - editor.on('change', function(){ console.log('content changed!')} ); - } - }); + Ember.tryInvoke(d, 'getTime'); // 1363320000000 + Ember.tryInvoke(d, 'setFullYear', [2014]); // 1394856000000 + Ember.tryInvoke(d, 'noSuchMethod', [2014]); // undefined ``` - 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 - @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 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 */ - run.bind = function(target, method /* args */) { - var args = slice.call(arguments); - return function() { - return run.join.apply(run, args.concat(slice.call(arguments))); - }; - }; + function tryInvoke(obj, methodName, args) { + if (canInvoke(obj, methodName)) { + return args ? applyStr(obj, methodName, args) : applyStr(obj, methodName); + } + } - run.backburner = backburner; - run.currentRunLoop = null; - run.queues = backburner.queueNames; + __exports__.tryInvoke = tryInvoke;// 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; + })(); /** - 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()`. + Provides try/finally functionality, while working + around Safari's double finally bug. ```javascript - run.begin(); - // code to be execute within a RunLoop - run.end(); + var tryable = function() { + someResource.lock(); + runCallback(); // May throw error. + }; + + var finalizer = function() { + someResource.unlock(); + }; + + Ember.tryFinally(tryable, finalizer); ``` - @method begin - @return {void} + @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 */ - run.begin = function() { - backburner.begin(); - }; - /** - 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()`. + var tryFinally; + if (needsFinallyFix) { + tryFinally = function(tryable, finalizer, binding) { + var result, finalResult, finalError; - ```javascript - run.begin(); - // code to be execute within a RunLoop - run.end(); - ``` + binding = binding || this; - @method end - @return {void} - */ - run.end = function() { - backburner.end(); - }; + try { + result = tryable.call(binding); + } finally { + try { + finalResult = finalizer.call(binding); + } catch (e) { + finalError = e; + } + } - /** - 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 (finalError) { throw finalError; } - @property queues - @type Array - @default ['sync', 'actions', 'destroy'] - */ + return (finalResult === undefined) ? result : finalResult; + }; + } else { + tryFinally = function(tryable, finalizer, binding) { + var result, finalResult; - /** - 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. + binding = binding || this; - 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. + try { + result = tryable.call(binding); + } finally { + finalResult = finalizer.call(binding); + } + + return (finalResult === undefined) ? result : finalResult; + }; + } + + /** + Provides try/catch/finally functionality, while working + around Safari's double finally bug. ```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"); - }); + var tryable = function() { + for (i = 0, l = listeners.length; i < l; i++) { + listener = listeners[i]; + beforeValues[i] = listener.before(name, time(), payload); + } - run.schedule('actions', this, function() { - // this will be executed in the 'actions' queue, after bindings have synced. - console.log("scheduled on actions queue"); - }); + return callback.call(binding); + }; - // 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 catchable = function(e) { + payload = payload || {}; + payload.exception = e; + }; + + var finalizer = function() { + for (i = 0, l = listeners.length; i < l; i++) { + listener = listeners[i]; + listener.after(name, time(), payload, beforeValues[i]); + } + }; + + Ember.tryCatchFinally(tryable, catchable, finalizer); ``` - @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} + @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. */ - run.schedule = function(queue, target, method) { - checkAutoRun(); - backburner.schedule.apply(backburner, arguments); - }; + var tryCatchFinally; + if (needsFinallyFix) { + tryCatchFinally = function(tryable, catchable, finalizer, binding) { + var result, finalResult, finalError; - // Used by global test teardown - run.hasScheduledTimers = function() { - return backburner.hasTimers(); - }; + binding = binding || this; - // Used by global test teardown - run.cancelTimers = function () { - backburner.cancelTimers(); - }; + try { + result = tryable.call(binding); + } catch(error) { + result = catchable.call(binding, error); + } finally { + try { + finalResult = finalizer.call(binding); + } catch (e) { + finalError = e; + } + } - /** - 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 (finalError) { throw finalError; } - 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). + return (finalResult === undefined) ? result : finalResult; + }; + } else { + tryCatchFinally = function(tryable, catchable, finalizer, binding) { + var result, finalResult; - ```javascript - run.sync(); - ``` + binding = binding || this; - @method sync - @return {void} - */ - run.sync = function() { - if (backburner.currentInstance) { - backburner.currentInstance.queues.sync.flush(); - } - }; + try { + result = tryable.call(binding); + } catch(error) { + result = catchable.call(binding, error); + } finally { + finalResult = finalizer.call(binding); + } - /** - 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. + return (finalResult === undefined) ? result : finalResult; + }; + } - 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. + // ........................................ + // TYPING & ARRAY MESSAGING + // - ```javascript - run.later(myContext, function() { - // code here will execute within a RunLoop in about 500ms with this == myContext - }, 500); - ``` + var TYPE_MAP = {}; + var t = "Boolean Number String Function Array Date RegExp Object".split(" "); + forEach.call(t, function(name) { + TYPE_MAP[ "[object " + name + "]" ] = name.toLowerCase(); + }); - @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); - }; + var toString = Object.prototype.toString; + + var EmberObject; /** - Schedule a function to run one time during the current RunLoop. This is equivalent - to calling `scheduleOnce` with the "actions" queue. + Returns a consistent type for the passed item. - @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); - }; + Use this instead of the built-in `typeof` to get the type of an item. + It will return the same result across all browsers and includes a bit + more detail. Here is what will be returned: - /** - 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). + | 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 | - Note that although you can pass optional arguments these will not be - considered when looking for duplicates. New arguments will replace previous - calls. + Examples: ```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 - }); + 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' + + // 'normal' JavaScript object + Ember.typeOf({ a: 'b' }); // 'object' ``` - 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.: + @method typeOf + @for Ember + @param {Object} item the item to check + @return {String} the type + */ + function typeOf(item) { + var ret, modulePath; - ```javascript - function scheduleIt() { - run.scheduleOnce('actions', myContext, function() { console.log("Closure"); }); + // ES6TODO: Depends on Ember.Object which is defined in runtime. + if (typeof EmberObject === "undefined") { + modulePath = 'ember-runtime/system/object'; + if (Ember.__loader.registry[modulePath]) { + EmberObject = Ember.__loader.require(modulePath)['default']; + } } - 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` + ret = (item === null || item === undefined) ? String(item) : TYPE_MAP[toString.call(item)] || 'object'; - @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); - }; + 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; + } /** - 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. + Convenience method to inspect an object. This method will attempt to + convert the object into a useful string description. - ```javascript - run.next(myContext, function() { - // code to be executed in the next run loop, - // which will be scheduled after the current one - }); - ``` + It is a pretty simple implementation. If you want something more robust, + use something like JSDump: https://github.com/NV/jsDump - 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. + @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 + ''; + } - 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: + var v; + var 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() { ... }"; } - ```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. + if (v && typeof v.toString !== 'function') { + ret.push(key + ": " + toString.call(v)); + } else { + ret.push(key + ": " + v); + } } - }); - ``` - - 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. + } + return "{" + ret.join(", ") + "}"; + } - 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`. + __exports__.inspect = inspect;// The following functions are intentionally minified to keep the functions + // below Chrome's function body size inlining limit of 600 chars. - @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); - }; + 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); + } + } - /** - Cancels a scheduled item. Must be a value returned by `run.later()`, - `run.once()`, `run.next()`, `run.debounce()`, or - `run.throttle()`. + __exports__.apply = apply;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); + } + } - ```javascript - var runNext = run.next(myContext, function() { - // will not be executed - }); - run.cancel(runNext); + __exports__.applyStr = applyStr;__exports__.GUID_KEY = GUID_KEY; + __exports__.META_DESC = META_DESC; + __exports__.EMPTY_META = EMPTY_META; + __exports__.meta = meta; + __exports__.typeOf = typeOf; + __exports__.tryCatchFinally = tryCatchFinally; + __exports__.isArray = isArray; + __exports__.canInvoke = canInvoke; + __exports__.tryFinally = tryFinally; + }); +enifed("ember-metal/watch_key", + ["ember-metal/core","ember-metal/utils","ember-metal/platform","ember-metal/properties","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + var metaFor = __dependency2__.meta; + var typeOf = __dependency2__.typeOf; + var o_defineProperty = __dependency3__.defineProperty; + var hasPropertyAccessors = __dependency3__.hasPropertyAccessors; + var MANDATORY_SETTER_FUNCTION = __dependency4__.MANDATORY_SETTER_FUNCTION; + var DEFAULT_GETTER_FUNCTION = __dependency4__.DEFAULT_GETTER_FUNCTION; - var runLater = run.later(myContext, function() { - // will not be executed - }, 500); - run.cancel(runLater); + function watchKey(obj, keyName, meta) { + // can't watch length on Array - it is special... + if (keyName === 'length' && typeOf(obj) === 'array') { return; } - var runOnce = run.once(myContext, function() { - // will not be executed - }); - run.cancel(runOnce); + var m = meta || metaFor(obj), watching = m.watching; - var throttle = run.throttle(myContext, function() { - // will not be executed - }, 1, false); - run.cancel(throttle); + // activate watching first time + if (!watching[keyName]) { + watching[keyName] = 1; - var debounce = run.debounce(myContext, function() { - // will not be executed - }, 1); - run.cancel(debounce); + var desc = m.descs[keyName]; + if (desc && desc.willWatch) { desc.willWatch(obj, keyName); } - 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); - ``` + if ('function' === typeof obj.willWatchProperty) { + obj.willWatchProperty(keyName); + } - @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); - }; + + if (hasPropertyAccessors) { + handleMandatorySetter(m, obj, keyName); + } + + } else { + watching[keyName] = (watching[keyName] || 0) + 1; + } + } - /** - 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. + __exports__.watchKey = watchKey; + + var handleMandatorySetter = function handleMandatorySetter(m, obj, keyName) { + var descriptor = Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptor(obj, keyName); + var configurable = descriptor ? descriptor.configurable : true; + var isWritable = descriptor ? descriptor.writable : true; + var hasValue = descriptor ? 'value' in descriptor : true; - 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. + // this x in Y deopts, so keeping it in this function is better; + if (configurable && isWritable && hasValue && keyName in obj) { + m.values[keyName] = obj[keyName]; + o_defineProperty(obj, keyName, { + configurable: true, + enumerable: Object.prototype.propertyIsEnumerable.call(obj, keyName), + set: MANDATORY_SETTER_FUNCTION(keyName), + get: DEFAULT_GETTER_FUNCTION(keyName) + }); + } + }; + - ```javascript - var myFunc = function() { console.log(this.name + ' ran.'); }; - var myContext = {name: 'debounce'}; + function unwatchKey(obj, keyName, meta) { + var m = meta || metaFor(obj); + var watching = m.watching; - run.debounce(myContext, myFunc, 150); + if (watching[keyName] === 1) { + watching[keyName] = 0; - // less than 150ms passes + var desc = m.descs[keyName]; + if (desc && desc.didUnwatch) { desc.didUnwatch(obj, keyName); } - run.debounce(myContext, myFunc, 150); + if ('function' === typeof obj.didUnwatchProperty) { + obj.didUnwatchProperty(keyName); + } - // 150ms passes - // myFunc is invoked with context myContext - // console logs 'debounce ran.' one time. - ``` + + if (hasPropertyAccessors && keyName in obj) { + o_defineProperty(obj, keyName, { + configurable: true, + enumerable: Object.prototype.propertyIsEnumerable.call(obj, 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: DEFAULT_GETTER_FUNCTION(keyName) + }); + } + + } else if (watching[keyName] > 1) { + watching[keyName]--; + } + } - 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. + __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; - ```javascript - var myFunc = function() { console.log(this.name + ' ran.'); }; - var myContext = {name: 'debounce'}; + // 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; + } - run.debounce(myContext, myFunc, 150, true); + function watchPath(obj, keyPath, meta) { + // can't watch length on Array - it is special... + if (keyPath === 'length' && typeOf(obj) === 'array') { return; } - // console logs 'debounce ran.' one time immediately. - // 100ms passes + var m = meta || metaFor(obj); + var watching = m.watching; - run.debounce(myContext, myFunc, 150, true); + if (!watching[keyPath]) { // activate watching first time + watching[keyPath] = 1; + chainsFor(obj, m).add(keyPath); + } else { + watching[keyPath] = (watching[keyPath] || 0) + 1; + } + } - // 150ms passes and nothing else is logged to the console and - // the debouncee is no longer being watched + __exports__.watchPath = watchPath;function unwatchPath(obj, keyPath, meta) { + var m = meta || metaFor(obj); + var watching = m.watching; - run.debounce(myContext, myFunc, 150, true); + if (watching[keyPath] === 1) { + watching[keyPath] = 0; + chainsFor(obj, m).remove(keyPath); + } else if (watching[keyPath] > 1) { + watching[keyPath]--; + } + } - // 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 + __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; - @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); - }; + var isPath = __dependency5__.isPath; /** - Ensure that the target method is never called more frequently than - the specified spacing period. The target method is called immediately. + 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()` - ```javascript - var myFunc = function() { console.log(this.name + ' ran.'); }; - var myContext = {name: 'throttle'}; + @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; } - run.throttle(myContext, myFunc, 150); - // myFunc is invoked with context myContext - // console logs 'throttle ran.' + if (!isPath(_keyPath)) { + watchKey(obj, _keyPath, m); + } else { + watchPath(obj, _keyPath, m); + } + } - // 50ms passes - run.throttle(myContext, myFunc, 150); + __exports__.watch = watch; - // 50ms passes - run.throttle(myContext, myFunc, 150); + function isWatching(obj, key) { + var meta = obj['__ember_meta__']; + return (meta && meta.watching[key]) > 0; + } - // 150ms passes - run.throttle(myContext, myFunc, 150); - // myFunc is invoked with context myContext - // console logs 'throttle ran.' - ``` + __exports__.isWatching = isWatching;watch.flushPending = flushPendingChains; - @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); - }; + function unwatch(obj, _keyPath, m) { + // can't watch length on Array - it is special... + if (_keyPath === 'length' && typeOf(obj) === 'array') { return; } - // 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 (!isPath(_keyPath)) { + unwatchKey(obj, _keyPath, m); + } else { + unwatchPath(obj, _keyPath, m); } } - /** - Add a new named queue after the specified queue. + __exports__.unwatch = unwatch;var NODE_STACK = []; - The queue to add will only be added once. + /** + Tears down the meta on an object so that it can be garbage collected. + Multiple calls will have no effect. - @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 destroy + @for Ember + @param {Object} obj the object to destroy + @return {void} */ - run._addQueue = function(name, after) { - if (indexOf.call(run.queues, name) === -1) { - run.queues.splice(indexOf.call(run.queues, after)+1, 0, name); + 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-metal/set_properties", - ["ember-metal/property_events","ember-metal/property_set","ember-metal/keys","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { +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"; - 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. + Ember Routing HTMLBars Helpers - ```javascript - var anObject = Ember.Object.create(); + @module ember + @submodule ember-routing-htmlbars + @requires ember-routing + */ - anObject.setProperties({ - firstName: 'Stanley', - lastName: 'Stuart', - age: 21 - }); - ``` + var Ember = __dependency1__["default"]; - @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; + var registerHelper = __dependency2__.registerHelper; - for (var i = 0, l = props.length; i < l; i++) { - propertyName = props[i]; + var outletHelper = __dependency3__.outletHelper; + var renderHelper = __dependency4__.renderHelper; + var linkToHelper = __dependency5__.linkToHelper; + var deprecatedLinkToHelper = __dependency5__.deprecatedLinkToHelper; + var actionHelper = __dependency6__.actionHelper; + var queryParamsHelper = __dependency7__.queryParamsHelper; - set(obj, propertyName, properties[propertyName]); - } - }); - return obj; - } + 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-metal/streams/read", - ["exports"], - function(__exports__) { +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"; - function read(object) { - if (object && object.isStream) { - return object.value(); + /** + @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; + } - if (source && source.isStream) { - source.setValue(value); + for (var i=0, l=keys.length;i + click me + + ``` - var keyStream = this.children[firstKey]; + And application code - if (keyStream === undefined) { - keyStream = this._makeChildStream(firstKey, path); - this.children[firstKey] = keyStream; + ```javascript + App.ApplicationController = Ember.Controller.extend({ + actions: { + anActionName: function() { + } } + }); + ``` - if (tailPath === undefined) { - return keyStream; - } else { - return keyStream.get(tailPath); - } - }, + Will result in the following rendered HTML - value: function() { - if (this.cache !== NIL) { - return this.cache; - } else { - return this.cache = this.valueFn(); - } - }, + ```html +
    +
    + click me +
    +
    + ``` - setValue: function() { - throw new Error("Stream error: setValue not implemented"); - }, + Clicking "click me" will trigger the `anActionName` action of the + `App.ApplicationController`. In this case, no additional parameters will be passed. - notify: function() { - this.notifyExcept(); - }, + If you provide additional parameters to the helper: - notifyExcept: function(callbackToSkip, contextToSkip) { - if (this.cache !== NIL) { - this.cache = NIL; - this.notifySubscribers(callbackToSkip, contextToSkip); - } - }, + ```handlebars + + ``` - subscribe: function(callback, context) { - if (this.subscribers === undefined) { - this.subscribers = [callback, context]; - } else { - this.subscribers.push(callback, context); - } - }, + Those parameters will be passed along as arguments to the JavaScript + function implementing the action. - unsubscribe: function(callback, context) { - var subscribers = this.subscribers; + ### Event Propagation - 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; - } - } - } - }, + 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: - notifySubscribers: function(callbackToSkip, contextToSkip) { - var subscribers = this.subscribers; + ```handlebars +
    + + +
    + ``` - if (subscribers !== undefined) { - for (var i = 0, l = subscribers.length; i < l; i += 2) { - var callback = subscribers[i]; - var context = subscribers[i+1]; + To disable bubbling, pass `bubbles=false` to the helper: - if (callback === callbackToSkip && context === contextToSkip) { - continue; - } + ```handlebars + + ``` - if (context === undefined) { - callback(this); - } else { - callback.call(context, this); - } - } - } - }, + 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. - destroy: function() { - if (this.destroyed) return; - this.destroyed = true; + ### Specifying DOM event type - var children = this.children; - for (var key in children) { - children[key].destroy(); - } - }, + 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: - isGlobal: function() { - var stream = this; - while (stream !== undefined) { - if (stream._isRoot) { - return stream._isGlobal; - } - stream = stream.source; - } - } - }; + ```handlebars +
    + click me +
    + ``` - __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"]; + See `Ember.View` 'Responding to Browser Events' for a list of + acceptable DOM event names. - function StreamBinding(stream) { - Ember.assert("StreamBinding error: tried to bind to object that is not a stream", stream && stream.isStream); + ### Specifying whitelisted modifier keys - this.stream = stream; - this.senderCallback = undefined; - this.senderContext = undefined; - this.senderValue = undefined; - this.destroyed = false; + 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. - stream.subscribe(this._onNotify, this); - } + ```handlebars +
    + click me +
    + ``` - StreamBinding.prototype = create(Stream.prototype); + This way the `{{action}}` will fire when clicking with the alt key pressed down. - merge(StreamBinding.prototype, { - valueFn: function() { - return this.stream.value(); - }, + Alternatively, supply "any" to the `allowedKeys` option to accept any combination of modifier keys. - _onNotify: function() { - this._scheduleSync(undefined, undefined, this); - }, + ```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: - setValue: function(value, callback, context) { - this._scheduleSync(value, callback, context); - }, + ```handlebars + {{! the application template }} +
    + click me +
    + ``` - _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; + ```javascript + App.ApplicationView = Ember.View.extend({ + actions: { + anActionName: function(){} } - }, + }); - _sync: function() { - if (this.destroyed) { - return; - } + ``` - if (this.senderContext !== this) { - this.stream.setValue(this.senderValue); - } + ### Additional Parameters - var senderCallback = this.senderCallback; - var senderContext = this.senderContext; - this.senderCallback = undefined; - this.senderContext = undefined; - this.senderValue = undefined; + You may specify additional parameters to the `{{action}}` helper. These + parameters are passed along as the arguments to the JavaScript function + implementing the action. - // Force StreamBindings to always notify - this.cache = undefined; + ```handlebars + {{#each person in people}} +
    + click me +
    + {{/each}} + ``` - this.notifyExcept(senderCallback, senderContext); - }, + Clicking "click me" will trigger the `edit` method on the current controller + with the value of `person` as a parameter. - destroy: function() { - if (this.destroyed) { - return; - } + @method action + @for Ember.Handlebars.helpers + @param {String} actionName + @param {Object} [context]* + @param {Hash} options + */ + function actionHelper(params, hash, options, env) { - this.destroyed = true; - this.stream.unsubscribe(this._onNotify, this); + var target; + if (!hash.target) { + target = this.getStream('controller'); + } else if (isStream(hash.target)) { + target = hash.target; + } else { + target = this.getStream(hash.target); } - }); - __exports__["default"] = StreamBinding; - }); -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 + // 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 Ember = __dependency1__["default"]; - var o_defineProperty = __dependency2__.defineProperty; - var canDefineNonEnumerableProperties = __dependency2__.canDefineNonEnumerableProperties; - var hasPropertyAccessors = __dependency2__.hasPropertyAccessors; - var o_create = __dependency2__.create; + var actionOptions = { + eventName: hash.on || "click", + parameters: params.slice(1), + view: this, + bubbles: hash.bubbles, + preventDefault: hash.preventDefault, + target: target, + withKeyCode: hash.withKeyCode + }; - var forEach = __dependency3__.forEach; + 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-metal + @module ember + @submodule ember-routing-handlebars */ - /** - Previously we used `Ember.$.uuid`, however `$.uuid` has been removed from - jQuery master. We'll just bootstrap our own uuid now. + 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; - @private - @return {Number} the uuid - */ - var _uuid = 0; + // We need the HTMLBars view helper from ensure ember-htmlbars. + // This ensures it is loaded first: /** - 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. + 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: - @public - @return {Number} [description] - */ - function uuid() { - return ++_uuid; - } + ```handlebars + {{#link-to 'photoGallery'}} + Great Hamster Photos + {{/link-to}} + ``` - __exports__.uuid = uuid;/** - Prefix used for guids through out Ember. - @private - @property GUID_PREFIX - @for Ember - @type String - @final - */ - var GUID_PREFIX = 'ember'; + You can also use an inline form of `{{link-to}}` helper by + passing the link text as the first argument + to the helper: - // Used for guid generation... - var numberCache = []; - var stringCache = {}; + ```handlebars + {{link-to 'Great Hamster Photos' 'photoGallery'}} + ``` - /** - Strongly hint runtimes to intern the provided string. + Both will result in: - When do I need to use this function? + ```html + + Great Hamster Photos + + ``` - 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. + ### 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: - Why? + ```handlebars + {{#link-to 'photoGallery' tagName="li"}} + Great Hamster Photos + {{/link-to}} + ``` - 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. + ```html +
  • + Great Hamster Photos +
  • + ``` - 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 - comparision. + To override this option for your entire application, see + "Overriding Application-wide Defaults". - How do I know if my string is a rope or symbol? + ### Disabling the `link-to` helper + By default `{{link-to}}` is enabled. + any passed value to `disabled` helper property will disable the `link-to` helper. - 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 comparisions 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. + static use: the `disabled` option: - Some numbers: http://jsperf.com/eval-vs-keys/8 + ```handlebars + {{#link-to 'photoGallery' disabled=true}} + Great Hamster Photos + {{/link-to}} + ``` - Known Trickâ„¢ + dynamic use: the `disabledWhen` option: - @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; - } + ```handlebars + {{#link-to 'photoGallery' disabledWhen=controller.someProperty}} + Great Hamster Photos + {{/link-to}} + ``` - /** - 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. + 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`. - On browsers that support it, these properties are added with enumeration - disabled so they won't show up when you iterate over your properties. + ```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; + }) + }); + ``` - @private - @property GUID_KEY - @for Ember - @type String - @final - */ - var GUID_KEY = intern('__ember' + (+ new Date())); + see "Overriding Application-wide Defaults" for more. - var GUID_DESC = { - writable: false, - configurable: false, - enumerable: false, - value: null - }; + ### 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. - /** - 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. + ### 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}}`: - @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. + ```handlebars + {{#link-to 'photoGallery.recent'}} + Great Hamster Photos from the last week + {{/link-to}} + ``` - 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; - } + will result in - __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. + ```html +
    + Great Hamster Photos + + ``` - You can also use this method on DOM Element objects. + The CSS class name used for active classes can be customized + for a single use of `{{link-to}}` by passing an `activeClass` + option: - @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 + {{#link-to 'photoGallery.recent' activeClass="current-url"}} + Great Hamster Photos from the last week + {{/link-to}} + ``` - // special cases where we don't want to add a key to object - if (obj === undefined) return "(undefined)"; - if (obj === null) return "(null)"; + ```html + + Great Hamster Photos + + ``` - var ret; - var type = typeof obj; + To override this option for your entire application, see + "Overriding Application-wide Defaults". - // 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; + ### 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: - case 'string': - ret = stringCache[obj]; - if (!ret) ret = stringCache[obj] = 'st' + uuid(); - return ret; + ```javascript + App.Router.map(function() { + this.resource("photoGallery", {path: "hamster-photos/:photo_id"}); + }); + ``` - case 'boolean': - return obj ? '(true)' : '(false)'; + ```handlebars + {{#link-to 'photoGallery' aPhoto}} + {{aPhoto.title}} + {{/link-to}} + ``` - default: - if (obj[GUID_KEY]) return obj[GUID_KEY]; - if (obj === Object) return '(Object)'; - if (obj === Array) return '(Array)'; - ret = GUID_PREFIX + uuid(); + ```html + + Tomster + + ``` - if (obj[GUID_KEY] === null) { - obj[GUID_KEY] = ret; - } else { - GUID_DESC.value = ret; - o_defineProperty(obj, GUID_KEY, GUID_DESC); - } - return ret; - } - } + ### 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: - __exports__.guidFor = guidFor;// .......................................................... - // META - // + ```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: - var META_DESC = { - writable: true, - configurable: false, - enumerable: false, - value: null - }; + ```handlebars + {{#link-to 'photoGallery.comment' aPhoto comment}} + {{comment.body}} + {{/link-to}} + ``` - 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; - } + ```html + + A+++ would snuggle again. + + ``` - Meta.prototype = { - chainWatchers: null - }; + ### 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: - 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; + ```javascript + App.Router.map(function() { + this.resource("photoGallery", {path: "hamster-photos/:photo_id"}); + }); + ``` - // Without non-enumerable properties, meta objects will be output in JSON - // unless explicitly suppressed - Meta.prototype.toJSON = function () { }; - } + ```handlebars + {{#link-to 'photoGallery' aPhotoId}} + {{aPhoto.title}} + {{/link-to}} + ``` - // Placeholder for non-writable metas. - var EMPTY_META = new Meta(null); + ```html + + Tomster + + ``` - - if (hasPropertyAccessors) { - EMPTY_META.values = {}; - } - + When transitioning into the linked route, the `model` hook will + be triggered with parameters including this passed identifier. - /** - Retrieves the meta hash for an object. If `writable` is true ensures the - hash is writable for this object as well. + ### Allowing Default Action - 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. + 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). - @method meta - @for Ember - @private + 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: - @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; + ``` javascript + Ember.LinkView.reopen({ + eventName: 'customEventName' + }); + ``` - if (!ret) { - if (canDefineNonEnumerableProperties) o_defineProperty(obj, '__ember_meta__', META_DESC); + @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; - ret = new Meta(obj); + Ember.assert("You must provide one or more parameters to the link-to helper.", params.length); - - if (hasPropertyAccessors) { - ret.values = {}; - } - + var lastParam = params[params.length - 1]; - obj['__ember_meta__'] = ret; + if (lastParam && lastParam.isQueryParams) { + hash.queryParamsObject = queryParamsObject = params.pop(); + } - // make sure we don't accidentally try to create constructor like desc - ret.descs.constructor = null; + if (hash.disabledWhen) { + hash.disabled = hash.disabledWhen; + delete hash.disabledWhen; + } - } else if (ret.source !== obj) { - if (canDefineNonEnumerableProperties) o_defineProperty(obj, '__ember_meta__', META_DESC); + if (!options.template) { + var linkTitle = params.shift(); - ret = o_create(ret); - ret.descs = o_create(ret.descs); - ret.watching = o_create(ret.watching); - ret.cache = {}; - ret.cacheMeta = {}; - ret.source = obj; + if (isStream(linkTitle)) { + hash.linkTitle = { stream: linkTitle }; + } - - if (hasPropertyAccessors) { - ret.values = o_create(ret.values); + options.template = { + isHTMLBars: true, + render: function() { + var value = read(linkTitle); + if (value) { + return shouldEscape ? escapeExpression(value) : value; + } else { + return ""; + } } - - - obj['__ember_meta__'] = ret; + }; } - return ret; - } - function getMeta(obj, property) { - var _meta = meta(obj, false); - return _meta[property]; - } + for (var i = 0; i < params.length; i++) { + if (isStream(params[i])) { + var lazyValue = params[i]; - __exports__.getMeta = getMeta;function setMeta(obj, property, value) { - var _meta = meta(obj, true); - _meta[property] = value; - return value; - } + if (!lazyValue._isController) { + while (ControllerMixin.detect(lazyValue.value())) { + lazyValue = lazyValue.get('model'); + } + } - __exports__.setMeta = setMeta;/** - @deprecated - @private + params[i] = lazyValue; + } + } - 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. + hash.params = params; - 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. + options.helperName = options.helperName || 'link-to'; - 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. + return env.helpers.view.helperFunction.call(this, [LinkView], hash, options, env); + } - 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. + /** + See [link-to](/api/classes/Ember.Handlebars.helpers.html#method_link-to) - 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 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'"); - @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 + 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 */ - 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 + Hello, {{who}}. + ``` - Ember.tryFinally(tryable, finalizer); + ```handlebars + +

    My great app

    + {{render "navigation"}} ``` - @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 - */ + ```html +

    My great app

    +
    + Hello, world. +
    + ``` - var tryFinally; - if (needsFinallyFix) { - tryFinally = function(tryable, finalizer, binding) { - var result, finalResult, finalError; + Optionally you may provide a second argument: a property path + that will be bound to the `model` property of the controller. - binding = binding || this; + 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. - try { - result = tryable.call(binding); - } finally { - try { - finalResult = finalizer.call(binding); - } catch (e) { - finalError = e; - } - } + For example if you had this `author` template. - if (finalError) { throw finalError; } + ```handlebars +
    + Written by {{firstName}} {{lastName}}. + Total Posts: {{postCount}} +
    + ``` - return (finalResult === undefined) ? result : finalResult; - }; - } else { - tryFinally = function(tryable, finalizer, binding) { - var result, finalResult; + You could render it inside the `post` template using the `render` helper. - binding = binding || this; + ```handlebars +
    +

    {{title}}

    +
    {{body}}
    + {{render "author" author}} +
    + ``` - try { - result = tryable.call(binding); - } finally { - finalResult = finalizer.call(binding); - } + @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; - return (finalResult === undefined) ? result : finalResult; - }; - } + var name = params[0]; + var context = params[1]; - /** - Provides try/catch/finally functionality, while working - around Safari's double finally bug. + container = this._keywords.controller.value().container; + router = container.lookup('router:main'); - ```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( + "The first argument of {{render}} must be quoted, e.g. {{render \"sidebar\"}}.", + typeof name === 'string' + ); - return callback.call(binding); - }; + 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 - var catchable = function(e) { - payload = payload || {}; - payload.exception = e; - }; - var finalizer = function() { - for (i = 0, l = listeners.length; i < l; i++) { - listener = listeners[i]; - listener.after(name, time(), payload, beforeValues[i]); - } - }; + view = container.lookup('view:' + name) || container.lookup('view:default'); - Ember.tryCatchFinally(tryable, catchable, finalizer); - ``` + // provide controller override + var controllerName = hash.controller || name; + var controllerFullName = 'controller:' + controllerName; - @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. - */ - var tryCatchFinally; - if (needsFinallyFix) { - tryCatchFinally = function(tryable, catchable, finalizer, binding) { - var result, finalResult, finalError; + Ember.assert("The controller name you supplied '" + controllerName + + "' did not resolve to a controller.", !hash.controller || container.has(controllerFullName)); - binding = binding || this; + var parentController = this._keywords.controller.value(); - try { - result = tryable.call(binding); - } catch(error) { - result = catchable.call(binding, error); - } finally { - try { - finalResult = finalizer.call(binding); - } catch (e) { - finalError = e; - } - } + // choose name + if (params.length > 1) { + var factory = container.lookupFactory(controllerFullName) || + generateControllerFactory(container, controllerName, initialContext); - if (finalError) { throw finalError; } + controller = factory.create({ + modelBinding: context, // TODO: Use a StreamBinding + parentController: parentController, + target: parentController + }); - return (finalResult === undefined) ? result : finalResult; - }; - } else { - tryCatchFinally = function(tryable, catchable, finalizer, binding) { - var result, finalResult; + view.one('willDestroyElement', function() { + controller.destroy(); + }); + } else { + controller = container.lookup(controllerFullName) || + generateController(container, controllerName); - binding = binding || this; + controller.setProperties({ + target: parentController, + parentController: parentController + }); + } - try { - result = tryable.call(binding); - } catch(error) { - result = catchable.call(binding, error); - } finally { - finalResult = finalizer.call(binding); - } + hash.viewName = camelize(name); - return (finalResult === undefined) ? result : finalResult; - }; - } + 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); - // ........................................ - // TYPING & ARRAY MESSAGING - // + hash.controller = controller; - var TYPE_MAP = {}; - var t = "Boolean Number String Function Array Date RegExp Object".split(" "); - forEach.call(t, function(name) { - TYPE_MAP[ "[object " + name + "]" ] = name.toLowerCase(); - }); + if (router && !initialContext) { + router._connectActiveView(name, view); + } - var toString = Object.prototype.toString; + options.helperName = options.helperName || ('render "' + name + '"'); - var EmberObject; + 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"; /** - Returns a consistent type for the passed item. - - Use this instead of the built-in `typeof` to get the type of an item. - It will return the same result across all browsers and includes a bit - more detail. Here is what will be returned: + Ember Routing Views - | 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 | + @module ember + @submodule ember-routing-views + @requires ember-routing + */ - Examples: + var Ember = __dependency1__["default"]; - ```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' + var LinkView = __dependency2__.LinkView; + var OutletView = __dependency3__.OutletView; - // 'normal' JavaScript object - Ember.typeOf({ a: 'b' }); // 'object' - ``` + Ember.LinkView = LinkView; + Ember.OutletView = OutletView; - @method typeOf - @for Ember - @param {Object} item the item to check - @return {String} the type + __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 */ - function typeOf(item) { - var ret, modulePath; - // ES6TODO: Depends on Ember.Object which is defined in runtime. - if (typeof EmberObject === "undefined") { - modulePath = 'ember-runtime/system/object'; - if (Ember.__loader.registry[modulePath]) { - EmberObject = Ember.__loader.require(modulePath)['default']; - } - } + var Ember = __dependency1__["default"]; + // FEATURES, Logger, assert - ret = (item === null || item === undefined) ? String(item) : TYPE_MAP[toString.call(item)] || 'object'; + 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; - 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'; + 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 ret; - } + return req; + }; /** - Convenience method to inspect an object. This method will attempt to - convert the object into a useful string description. + `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. - It is a pretty simple implementation. If you want something more robust, - use something like JSDump: https://github.com/NV/jsDump + 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. - @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 + ''; - } + @class LinkView + @namespace Ember + @extends Ember.View + @see {Handlebars.helpers.link-to} + **/ + var LinkView = Ember.LinkView = EmberComponent.extend({ + tagName: 'a', - var v; - var 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() { ... }"; } + /** + @deprecated Use current-when instead. + @property currentWhen + */ + currentWhen: null, - if (v && typeof v.toString !== 'function') { - ret.push(key + ": " + toString.call(v)); - } else { - ret.push(key + ": " + v); - } - } - } - return "{" + ret.join(", ") + "}"; - } + /** + Used to determine when this LinkView is active. - __exports__.inspect = inspect;// The following functions are intentionally minified to keep the functions - // below Chrome's function body size inlining limit of 600 chars. + @property currentWhen + */ + 'current-when': null, - 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); - } - } + /** + Sets the `title` attribute of the `LinkView`'s HTML element. - __exports__.apply = apply;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); - } - } + @property title + @default null + **/ + title: null, - __exports__.applyStr = applyStr;__exports__.GUID_KEY = GUID_KEY; - __exports__.META_DESC = META_DESC; - __exports__.EMPTY_META = EMPTY_META; - __exports__.meta = meta; - __exports__.typeOf = typeOf; - __exports__.tryCatchFinally = tryCatchFinally; - __exports__.isArray = isArray; - __exports__.canInvoke = canInvoke; - __exports__.tryFinally = tryFinally; - }); -enifed("ember-metal/watch_key", - ["ember-metal/core","ember-metal/utils","ember-metal/platform","ember-metal/properties","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - var metaFor = __dependency2__.meta; - var typeOf = __dependency2__.typeOf; - var o_defineProperty = __dependency3__.defineProperty; - var hasPropertyAccessors = __dependency3__.hasPropertyAccessors; - var MANDATORY_SETTER_FUNCTION = __dependency4__.MANDATORY_SETTER_FUNCTION; - var DEFAULT_GETTER_FUNCTION = __dependency4__.DEFAULT_GETTER_FUNCTION; + /** + Sets the `rel` attribute of the `LinkView`'s HTML element. - function watchKey(obj, keyName, meta) { - // can't watch length on Array - it is special... - if (keyName === 'length' && typeOf(obj) === 'array') { return; } + @property rel + @default null + **/ + rel: null, - var m = meta || metaFor(obj), watching = m.watching; + /** + Sets the `tabindex` attribute of the `LinkView`'s HTML element. - // activate watching first time - if (!watching[keyName]) { - watching[keyName] = 1; + @property tabindex + @default null + **/ + tabindex: null, - var desc = m.descs[keyName]; - if (desc && desc.willWatch) { desc.willWatch(obj, keyName); } + /** + Sets the `target` attribute of the `LinkView`'s HTML element. - if ('function' === typeof obj.willWatchProperty) { - obj.willWatchProperty(keyName); - } + @since 1.8.0 + @property target + @default null + **/ + target: null, - - if (hasPropertyAccessors) { - handleMandatorySetter(m, obj, keyName); - } - - } else { - watching[keyName] = (watching[keyName] || 0) + 1; - } - } + /** + The CSS class to apply to `LinkView`'s element when its `active` + property is `true`. - __exports__.watchKey = watchKey; - - var handleMandatorySetter = function handleMandatorySetter(m, obj, keyName) { - var descriptor = Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptor(obj, keyName); - var configurable = descriptor ? descriptor.configurable : true; + @property activeClass + @type String + @default active + **/ + activeClass: 'active', - // this x in Y deopts, so keeping it in this function is better; - if (configurable && keyName in obj) { - m.values[keyName] = obj[keyName]; - o_defineProperty(obj, keyName, { - configurable: true, - enumerable: Object.prototype.propertyIsEnumerable.call(obj, keyName), - set: MANDATORY_SETTER_FUNCTION(keyName), - get: DEFAULT_GETTER_FUNCTION(keyName) - }); - } - }; - + /** + The CSS class to apply to `LinkView`'s element when its `loading` + property is `true`. - function unwatchKey(obj, keyName, meta) { - var m = meta || metaFor(obj); - var watching = m.watching; + @property loadingClass + @type String + @default loading + **/ + loadingClass: 'loading', - if (watching[keyName] === 1) { - watching[keyName] = 0; + /** + The CSS class to apply to a `LinkView`'s element when its `disabled` + property is `true`. - var desc = m.descs[keyName]; - if (desc && desc.didUnwatch) { desc.didUnwatch(obj, keyName); } + @property disabledClass + @type String + @default disabled + **/ + disabledClass: 'disabled', + _isDisabled: false, - if ('function' === typeof obj.didUnwatchProperty) { - obj.didUnwatchProperty(keyName); - } + /** + Determines whether the `LinkView` will trigger routing via + the `replaceWith` routing strategy. - - if (hasPropertyAccessors && keyName in obj) { - o_defineProperty(obj, keyName, { - configurable: true, - enumerable: Object.prototype.propertyIsEnumerable.call(obj, 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: DEFAULT_GETTER_FUNCTION(keyName) - }); - } - - } else if (watching[keyName] > 1) { - watching[keyName]--; - } - } + @property replace + @type Boolean + @default false + **/ + replace: false, - __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; + /** + 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. - // 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; - } + @property attributeBindings + @type Array | String + @default ['href', 'title', 'rel', 'tabindex', 'target'] + **/ + attributeBindings: ['href', 'title', 'rel', 'tabindex'], - function watchPath(obj, keyPath, meta) { - // can't watch length on Array - it is special... - if (keyPath === 'length' && typeOf(obj) === 'array') { return; } + /** + By default the `{{link-to}}` helper will bind to the `active`, `loading`, and + `disabled` classes. It is discouraged to override these directly. - var m = meta || metaFor(obj); - var watching = m.watching; + @property classNameBindings + @type Array + @default ['active', 'loading', 'disabled'] + **/ + classNameBindings: ['active', 'loading', 'disabled'], - if (!watching[keyPath]) { // activate watching first time - watching[keyPath] = 1; - chainsFor(obj, m).add(keyPath); - } else { - watching[keyPath] = (watching[keyPath] || 0) + 1; - } - } + /** + 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. - __exports__.watchPath = watchPath;function unwatchPath(obj, keyPath, meta) { - var m = meta || metaFor(obj); - var watching = m.watching; + This is particularly useful on mobile when one wants to avoid the 300ms + click delay using some sort of custom `tap` event. - if (watching[keyPath] === 1) { - watching[keyPath] = 0; - chainsFor(obj, m).remove(keyPath); - } else if (watching[keyPath] > 1) { - watching[keyPath]--; - } - } + @property eventName + @type String + @default click + */ + eventName: 'click', - __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 - */ + // 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. - 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; + @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')); + } + }); + ``` - var isPath = __dependency5__.isPath; + 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. - /** - 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()` + @method init + */ + init: function() { + this._super.apply(this, arguments); - @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; } + Ember.deprecate('Using currentWhen with {{link-to}} is deprecated in favor of `current-when`.', !this.currentWhen); - if (!isPath(_keyPath)) { - watchKey(obj, _keyPath, m); - } else { - watchPath(obj, _keyPath, m); - } - } + // Map desired event name to invoke function + var eventName = get(this, 'eventName'); + this.on(eventName, this, this._invoke); + }, - __exports__.watch = watch; + /** + This method is invoked by observers installed during `init` that fire + whenever the params change - function isWatching(obj, key) { - var meta = obj['__ember_meta__']; - return (meta && meta.watching[key]) > 0; - } + @private + @method _paramsChanged + @since 1.3.0 + */ + _paramsChanged: function() { + this.notifyPropertyChange('resolvedParams'); + }, - __exports__.isWatching = isWatching;watch.flushPending = flushPendingChains; + /** + This is called to setup observers that will trigger a rerender. - function unwatch(obj, _keyPath, m) { - // can't watch length on Array - it is special... - if (_keyPath === 'length' && typeOf(obj) === 'array') { return; } + @private + @method _setupPathObservers + @since 1.3.0 + **/ + _setupPathObservers: function(){ + var params = this.params; - if (!isPath(_keyPath)) { - unwatchKey(obj, _keyPath, m); - } else { - unwatchPath(obj, _keyPath, m); - } - } + var scheduledRerender = this._wrapAsScheduled(this.rerender); + var scheduledParamsChanged = this._wrapAsScheduled(this._paramsChanged); - __exports__.unwatch = unwatch;var NODE_STACK = []; + if (this.linkTitle) { + var linkTitle = this.linkTitle.stream || this.linkTitle; + subscribe(linkTitle, scheduledRerender, this); + } - /** - Tears down the meta on an object so that it can be garbage collected. - Multiple calls will have no effect. + for (var i = 0; i < params.length; i++) { + subscribe(params[i], scheduledParamsChanged, this); + } - @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); - } + 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); } } - } - } - - __exports__.destroy = destroy; - }); -enifed("ember-routing-handlebars", - ["ember-metal/core","ember-handlebars","ember-routing-handlebars/helpers/link_to","ember-routing-handlebars/helpers/outlet","ember-routing-handlebars/helpers/render","ember-routing-handlebars/helpers/action","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { - "use strict"; - /** - Ember Routing Handlebars + }, - @module ember - @submodule ember-routing-handlebars - @requires ember-views - */ + afterRender: function(){ + this._super.apply(this, arguments); + this._setupPathObservers(); + }, - var Ember = __dependency1__["default"]; - var EmberHandlebars = __dependency2__["default"]; + /** - var deprecatedLinkToHelper = __dependency3__.deprecatedLinkToHelper; - var linkToHelper = __dependency3__.linkToHelper; - var LinkView = __dependency3__.LinkView; - var queryParamsHelper = __dependency3__.queryParamsHelper; + Accessed as a classname binding to apply the `LinkView`'s `disabledClass` + CSS `class` to the element when the link is disabled. - var outletHelper = __dependency4__.outletHelper; - var OutletView = __dependency4__.OutletView; + 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); } - var renderHelper = __dependency5__["default"]; + return value ? get(this, 'disabledClass') : false; + }), - var ActionHelper = __dependency6__.ActionHelper; - var actionHelper = __dependency6__.actionHelper; + /** + Accessed as a classname binding to apply the `LinkView`'s `activeClass` + CSS `class` to the element when the link is active. - Ember.LinkView = LinkView; - EmberHandlebars.ActionHelper = ActionHelper; - EmberHandlebars.OutletView = OutletView; + 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. - EmberHandlebars.registerHelper('render', renderHelper); - EmberHandlebars.registerHelper('action', actionHelper); - EmberHandlebars.registerHelper('outlet', outletHelper); - EmberHandlebars.registerHelper('link-to', linkToHelper); - EmberHandlebars.registerHelper('linkTo', deprecatedLinkToHelper); - EmberHandlebars.registerHelper('query-params', queryParamsHelper); + The `currentWhen` property can match against multiple routes by separating + route names using the ` ` (space) character. - __exports__["default"] = Ember; - }); -enifed("ember-routing-handlebars/helpers/action", - ["ember-metal/core","ember-metal/array","ember-metal/utils","ember-metal/run_loop","ember-views/streams/read","ember-views/system/utils","ember-views/system/action_manager","ember-handlebars","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // Handlebars, uuid, FEATURES, assert, deprecate - var forEach = __dependency2__.forEach; - var uuid = __dependency3__.uuid; - var run = __dependency4__["default"]; + @property active + **/ + active: computed('loadedParams', function computeLinkViewActive() { + if (get(this, 'loading')) { return false; } - var readUnwrappedModel = __dependency5__.readUnwrappedModel; - var isSimpleClick = __dependency6__.isSimpleClick; - var ActionManager = __dependency7__["default"]; - var EmberHandlebars = __dependency8__["default"]; + 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; - /** - @module ember - @submodule ember-routing - */ + function isActiveForRoute(routeName) { + var handlers = router.router.recognizer.handlersFor(routeName); + var leafName = handlers[handlers.length-1].handler; + var maximumContexts = numberOfContextsAcceptedByHandler(routeName, handlers); - function actionArgs(parameters, actionName) { - var ret, i; + // 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 (actionName === undefined) { - ret = new Array(parameters.length); - for (i = 0; i < parameters.length; i++) { - ret[i] = readUnwrappedModel(parameters[i]); - } - } else { - ret = new Array(parameters.length + 1); - ret[0] = actionName; - for (i = 0; i < parameters.length; i++) { - ret[i + 1] = readUnwrappedModel(parameters[i]); - } - } + // 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; + } - return ret; - } + var args = routeArgs(routeName, contexts, null); + var isActive = router.isActive.apply(router, args); + if (!isActive) { return false; } - var ActionHelper = {}; + var emptyQueryParams = Ember.isEmpty(Ember.keys(loadedParams.queryParams)); - // registeredActions is re-exported for compatibility with older plugins - // that were using this undocumented API. - ActionHelper.registeredActions = ActionManager.registeredActions; + if (!isCurrentWhenSpecified && !emptyQueryParams && isActive) { + var visibleQueryParams = {}; + merge(visibleQueryParams, loadedParams.queryParams); + router._prepareQueryParams(loadedParams.targetRouteName, loadedParams.models, visibleQueryParams); + isActive = shallowEqual(visibleQueryParams, router.router.state.queryParams); + } - __exports__.ActionHelper = ActionHelper; + return isActive; + } - var keys = ["alt", "shift", "meta", "ctrl"]; + + currentWhen = currentWhen.split(' '); + for (var i = 0, len = currentWhen.length; i < len; i++) { + if (isActiveForRoute(currentWhen[i])) { + return get(this, 'activeClass'); + } + } + }), - var POINTER_EVENT_TYPE_REGEX = /^click|mouse|touch/; + /** + Accessed as a classname binding to apply the `LinkView`'s `loadingClass` + CSS `class` to the element when the link is loading. - var isAllowedEvent = function(event, allowedKeys) { - if (typeof allowedKeys === "undefined") { - if (POINTER_EVENT_TYPE_REGEX.test(event.type)) { - return isSimpleClick(event); - } else { - allowedKeys = ''; - } - } + 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. - if (allowedKeys.indexOf("any") >= 0) { - return true; - } + @property loading + **/ + loading: computed('loadedParams', function computeLinkViewLoading() { + if (!get(this, 'loadedParams')) { return get(this, 'loadingClass'); } + }), - var allowed = true; + /** + Returns the application's main router from the container. - forEach.call(keys, function(key) { - if (event[key + "Key"] && allowedKeys.indexOf(key) === -1) { - allowed = false; + @private + @property router + **/ + router: computed(function() { + var controller = get(this, 'controller'); + if (controller && controller.container) { + return controller.container.lookup('router:main'); } - }); + }), - return allowed; - }; + /** + Event handler that invokes the link, activating the associated route. - function isKeyEvent(eventName) { - return ['keyUp', 'keyPress', 'keyDown'].indexOf(eventName) !== -1; - } + @private + @method _invoke + @param {Event} event + */ + _invoke: function(event) { + if (!isSimpleClick(event)) { return true; } - function ignoreKeyEvent(eventName, event, keyCode) { - var any = 'any'; - keyCode = keyCode || any; - return isKeyEvent(eventName) && keyCode !== any && keyCode !== event.which.toString(); - } + if (this.preventDefault !== false) { + + var targetAttribute = get(this, 'target'); + if (!targetAttribute || targetAttribute === '_self') { + event.preventDefault(); + } + } - ActionHelper.registerAction = function(actionNameOrStream, options, allowedKeys) { - var actionId = uuid(); - var eventName = options.eventName; - var parameters = options.parameters; + if (this.bubbles === false) { event.stopPropagation(); } - ActionManager.registeredActions[actionId] = { - eventName: eventName, - handler: function handleRegisteredAction(event) { - if (!isAllowedEvent(event, allowedKeys)) { return true; } + if (get(this, '_isDisabled')) { return false; } - if (options.preventDefault !== false) { - event.preventDefault(); - } + 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; + } - if (options.bubbles === false) { - event.stopPropagation(); + + var targetAttribute2 = get(this, 'target'); + if (targetAttribute2 && targetAttribute2 !== '_self') { + return false; } + - var target = options.target.value(); - - - var actionName; + var router = get(this, 'router'); + var loadedParams = get(this, 'loadedParams'); - if (actionNameOrStream.isStream) { - actionName = actionNameOrStream.value(); + var transition = router._doTransition(loadedParams.targetRouteName, loadedParams.models, loadedParams.queryParams); + if (get(this, 'replace')) { + transition.method('replace'); + } - if (typeof actionName === 'undefined' || typeof actionName === 'function') { - actionName = actionNameOrStream._originalPath; - Ember.deprecate("You specified a quoteless path to the {{action}} helper '" + - actionName + "' which did not resolve to an actionName." + - " Perhaps you meant to use a quoted actionName? (e.g. {{action '" + actionName + "'}})."); - } - } + // 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); - if (!actionName) { - actionName = actionNameOrStream; - } + run.scheduleOnce('routerTransitions', this, this._eagerUpdateUrl, transition, url); + }, - run(function runRegisteredAction() { - if (target.send) { - target.send.apply(target, actionArgs(parameters, actionName)); - } else { - Ember.assert("The action '" + actionName + "' did not exist on " + target, typeof target[actionName] === 'function'); - target[actionName].apply(target, actionArgs(parameters)); - } - }); + /** + @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; } - }; - options.view.on('willClearRender', function() { - delete ActionManager.registeredActions[actionId]; - }); + if (href.indexOf('#') === 0) { + href = href.slice(1); + } - return actionId; - }; + // 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); + } - /** - The `{{action}}` helper provides a useful shortcut for registering an HTML - element within a template for a single DOM event and forwarding that - interaction to the template's controller or specified `target` option. + // Prevent later update url refire. + transition.method(null); + }, - If the controller does not implement the specified action, the event is sent - to the current route, and it bubbles up the route hierarchy from there. + /** + Computed property that returns an array of the + resolved parameters passed to the `link-to` helper, + e.g.: - For more advanced event handling see [Ember.Component](/api/classes/Ember.Component.html) + ```hbs + {{link-to a b '123' c}} + ``` + will generate a `resolvedParams` of: - ### Use - Given the following application Handlebars template on the page + ```js + [aObject, bObject, '123', cObject] + ``` - ```handlebars -
    - click me -
    - ``` + @private + @property + @return {Array} + */ + resolvedParams: computed('router.url', function() { + var params = this.params; + var targetRouteName; + var models = []; + var onlyQueryParamsSupplied = (params.length === 0); - And application code + if (onlyQueryParamsSupplied) { + var appController = this.container.lookup('controller:application'); + targetRouteName = get(appController, 'currentRouteName'); + } else { + targetRouteName = read(params[0]); - ```javascript - App.ApplicationController = Ember.Controller.extend({ - actions: { - anActionName: function() { + for (var i = 1; i < params.length; i++) { + models.push(read(params[i])); } } - }); - ``` - Will result in the following rendered HTML + var suppliedQueryParams = getResolvedQueryParams(this, targetRouteName); - ```html -
    -
    - click me -
    -
    - ``` + return { + targetRouteName: targetRouteName, + models: models, + queryParams: suppliedQueryParams + }; + }), - Clicking "click me" will trigger the `anActionName` action of the - `App.ApplicationController`. In this case, no additional parameters will be passed. + /** + 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. - If you provide additional parameters to the helper: + @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; } - ```handlebars - - ``` + var resolvedParams = get(this, 'resolvedParams'); + var namedRoute = resolvedParams.targetRouteName; - Those parameters will be passed along as arguments to the JavaScript - function implementing the action. + if (!namedRoute) { return; } - ### Event Propagation + 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)); - 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: + if (!paramsAreLoaded(resolvedParams.models)) { return; } - ```handlebars -
    - - -
    - ``` + return resolvedParams; + }), - To disable bubbling, pass `bubbles=false` to the helper: + queryParamsObject: null, - ```handlebars - - ``` + /** + Sets the element's `href` attribute to the url for + the `LinkView`'s targeted route. - 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. + If the `LinkView`'s `tagName` is changed to a value other + than `a`, this property will be ignored. - ### Specifying DOM event type + @property href + **/ + href: computed('loadedParams', function computeLinkViewHref() { + if (get(this, 'tagName') !== 'a') { return; } - 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: + var router = get(this, 'router'); + var loadedParams = get(this, 'loadedParams'); - ```handlebars -
    - click me -
    - ``` + if (!loadedParams) { + return get(this, 'loadingHref'); + } - See `Ember.View` 'Responding to Browser Events' for a list of - acceptable DOM event names. + var visibleQueryParams = {}; + merge(visibleQueryParams, loadedParams.queryParams); + router._prepareQueryParams(loadedParams.targetRouteName, loadedParams.models, visibleQueryParams); - ### Specifying whitelisted modifier keys + var args = routeArgs(loadedParams.targetRouteName, loadedParams.models, visibleQueryParams); + var result = router.generate.apply(router, args); + return result; + }), - 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. + /** + The default href value to use while a link-to is loading. + Only applies when tagName is 'a' - ```handlebars -
    - click me -
    - ``` + @property loadingHref + @type String + @default # + */ + loadingHref: '#' + }); - This way the `{{action}}` will fire when clicking with the alt key pressed down. + LinkView.toString = function() { return "LinkView"; }; - Alternatively, supply "any" to the `allowedKeys` option to accept any combination of modifier keys. + + LinkView.reopen({ + attributeBindings: ['target'], - ```handlebars -
    - click me with any key pressed -
    - ``` + /** + Sets the `target` attribute of the `LinkView`'s anchor element. - ### Specifying a Target + @property target + @default null + **/ + target: null + }); + - There are several possible target objects for `{{action}}` helpers: + function getResolvedQueryParams(linkView, targetRouteName) { + var queryParamsObject = linkView.queryParamsObject; + var resolvedQueryParams = {}; - 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. + if (!queryParamsObject) { return resolvedQueryParams; } - 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: + var values = queryParamsObject.values; + for (var key in values) { + if (!values.hasOwnProperty(key)) { continue; } + resolvedQueryParams[key] = read(values[key]); + } - ```handlebars - {{! the application template }} -
    - click me -
    - ``` + return resolvedQueryParams; + } - ```javascript - App.ApplicationView = Ember.View.extend({ - actions: { - anActionName: function(){} + 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; + } - ### Additional Parameters + 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; + } - You may specify additional parameters to the `{{action}}` helper. These - parameters are passed along as the arguments to the JavaScript function - implementing the action. + __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 + */ - ```handlebars - {{#each person in people}} -
    - click me -
    - {{/each}} - ``` + var ContainerView = __dependency1__["default"]; + var _Metamorph = __dependency2__._Metamorph; - Clicking "click me" will trigger the `edit` method on the current controller - with the value of `person` as a parameter. + 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 - @method action - @for Ember.Handlebars.helpers - @param {String} actionName - @param {Object} [context]* - @param {Hash} options + @module ember + @submodule ember-routing + @requires ember-views */ - function actionHelper(actionName) { - var length = arguments.length; - var options = arguments[length - 1]; - var view = options.data.view; - var hash = options.hash; - var types = options.types; - // create a hash to pass along to registerAction - var parameters = []; + var Ember = __dependency1__["default"]; - var actionOptions = { - eventName: hash.on || "click", - parameters: parameters, - view: options.data.view, - bubbles: hash.bubbles, - preventDefault: hash.preventDefault, - target: view.getStream(hash.target || 'controller'), - withKeyCode: hash.withKeyCode - }; + // ES6TODO: Cleanup modules with side-effects below - var actionNameStream; + var EmberLocation = __dependency5__["default"]; + var NoneLocation = __dependency6__["default"]; + var HashLocation = __dependency7__["default"]; + var HistoryLocation = __dependency8__["default"]; + var AutoLocation = __dependency9__["default"]; - if (types[0] === "ID") { - actionNameStream = view.getStream(actionName); - actionNameStream._originalPath = actionName; - } else { - actionNameStream = actionName; - } + var generateControllerFactory = __dependency10__.generateControllerFactory; + var generateController = __dependency10__["default"]; + var controllerFor = __dependency11__["default"]; + var RouterDSL = __dependency12__["default"]; + var Router = __dependency13__["default"]; + var Route = __dependency14__["default"]; - for (var i = 1; i < length - 1; i++) { - if (types[i] === "ID") { - parameters.push(view.getStream(arguments[i])); - } else { - parameters.push(arguments[i]); - } - } + Ember.Location = EmberLocation; + Ember.AutoLocation = AutoLocation; + Ember.HashLocation = HashLocation; + Ember.HistoryLocation = HistoryLocation; + Ember.NoneLocation = NoneLocation; - var actionId = ActionHelper.registerAction(actionNameStream, actionOptions, hash.allowedKeys); - return new EmberHandlebars.SafeString('data-ember-action="' + actionId + '"'); - } + Ember.controllerFor = controllerFor; + Ember.generateControllerFactory = generateControllerFactory; + Ember.generateController = generateController; + Ember.RouterDSL = RouterDSL; + Ember.Router = Router; + Ember.Route = Route; - __exports__.actionHelper = actionHelper; + __exports__["default"] = Ember; }); -enifed("ember-routing-handlebars/helpers/link_to", - ["ember-metal/core","ember-metal/property_get","ember-metal/merge","ember-metal/run_loop","ember-metal/computed","ember-runtime/system/string","ember-runtime/system/object","ember-runtime/mixins/controller","ember-metal/keys","ember-views/system/utils","ember-views/views/component","ember-handlebars/helpers/view","ember-routing/utils","ember-handlebars/ext","ember-metal/streams/read","ember-handlebars","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __exports__) { +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, Logger, Handlebars, warn, assert + // FEATURES, deprecate var get = __dependency2__.get; - var merge = __dependency3__["default"]; - var run = __dependency4__["default"]; - var computed = __dependency5__.computed; - - var fmt = __dependency6__.fmt; - var EmberObject = __dependency7__["default"]; - var ControllerMixin = __dependency8__["default"]; - var keys = __dependency9__["default"]; - var isSimpleClick = __dependency10__.isSimpleClick; - var EmberComponent = __dependency11__["default"]; - var viewHelper = __dependency12__.viewHelper; - var routeArgs = __dependency13__.routeArgs; - var stringifyValue = __dependency14__.stringifyValue; - var read = __dependency15__.read; + 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 */ - var slice = [].slice; + ControllerMixin.reopen({ + concatenatedProperties: ['queryParams', '_pCacheMeta'], - 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; - } + init: function() { + this._super.apply(this, arguments); + listenForQueryParamChanges(this); + }, - return req; - }; + /** + 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` - var QueryParams = EmberObject.extend({ - values: null - }); + @property queryParams + @public + */ + queryParams: null, - /** - `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. + /** + @property _qpDelegate + @private + */ + _qpDelegate: null, - 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. + /** + @property _normalizedQueryParams + @private + */ + _normalizedQueryParams: computed(function() { + var m = meta(this); + if (m.proto !== this) { + return get(m.proto, '_normalizedQueryParams'); + } - @class LinkView - @namespace Ember - @extends Ember.View - @see {Handlebars.helpers.link-to} - **/ - var LinkView = Ember.LinkView = EmberComponent.extend({ - tagName: 'a', + 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; + }), /** - @deprecated Use current-when instead. - @property currentWhen + @property _cacheMeta + @private */ - currentWhen: null, + _cacheMeta: computed(function() { + var m = meta(this); + if (m.proto !== this) { + return get(m.proto, '_cacheMeta'); + } - /** - Used to determine when this LinkView is active. + var cacheMeta = {}; + var qpMap = get(this, '_normalizedQueryParams'); + for (var prop in qpMap) { + if (!qpMap.hasOwnProperty(prop)) { continue; } - @property currentWhen - */ - 'current-when': null, + var qp = qpMap[prop]; + var scope = qp.scope; + var parts; - /** - Sets the `title` attribute of the `LinkView`'s HTML element. + if (scope === 'controller') { + parts = []; + } - @property title - @default null - **/ - title: null, + cacheMeta[prop] = { + parts: parts, // provided by route if 'model' scope + values: null, // provided by route + scope: scope, + prefix: "", + def: get(this, prop) + }; + } + + return cacheMeta; + }), /** - Sets the `rel` attribute of the `LinkView`'s HTML element. + @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; - @property rel - @default null - **/ - rel: null, + 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); + } + } + }, /** - The CSS class to apply to `LinkView`'s element when its `active` - property is `true`. + @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); - @property activeClass - @type String - @default active - **/ - activeClass: 'active', + // 1. Update model-dep cache + var cache = this._bucketCache; + if (cache) { + controller._bucketCache.stash(cacheKey, prop, value); + } - /** - The CSS class to apply to `LinkView`'s element when its `loading` - property is `true`. + // 2. Notify a delegate (e.g. to fire a qp transition) + var delegate = controller._qpDelegate; + if (delegate) { + delegate(controller, prop); + } + }, - @property loadingClass - @type String - @default loading - **/ - loadingClass: 'loading', + /** + @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, '-'); + }, /** - The CSS class to apply to a `LinkView`'s element when its `disabled` - property is `true`. + Transition the application into another route. The route may + be either a single route or route path: - @property disabledClass - @type String - @default disabled - **/ - disabledClass: 'disabled', - _isDisabled: false, + ```javascript + aController.transitionToRoute('blogPosts'); + aController.transitionToRoute('blogPosts.recentEntries'); + ``` - /** - Determines whether the `LinkView` will trigger routing via - the `replaceWith` routing strategy. + Optionally supply a model for the route in question. The model + will be serialized into the URL using the `serialize` hook of + the route: - @property replace - @type Boolean - @default false - **/ - replace: false, + ```javascript + aController.transitionToRoute('blogPost', aPost); + ``` - /** - By default the `{{link-to}}` helper will bind to the `href` and - `title` attributes. It's discourage that you override these defaults, - however you can push onto the array if needed. + 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: - @property attributeBindings - @type Array | String - @default ['href', 'title', 'rel'] - **/ - attributeBindings: ['href', 'title', 'rel', 'tabindex'], + ```javascript + aController.transitionToRoute('blogPost', 1); + ``` - /** - By default the `{{link-to}}` helper will bind to the `active`, `loading`, and - `disabled` classes. It is discouraged to override these directly. + Multiple models will be applied last to first recursively up the + resource tree. - @property classNameBindings - @type Array - @default ['active', 'loading', 'disabled'] - **/ - classNameBindings: ['active', 'loading', 'disabled'], + ```javascript + App.Router.map(function() { + this.resource('blogPost', {path:':blogPostId'}, function(){ + this.resource('blogComment', {path: ':blogCommentId'}); + }); + }); - /** - 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. + aController.transitionToRoute('blogComment', aPost, aComment); + aController.transitionToRoute('blogComment', 1, 13); + ``` - This is particularly useful on mobile when one wants to avoid the 300ms - click delay using some sort of custom `tap` event. + 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. - @property eventName - @type String - @default click + ```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 */ - eventName: 'click', + 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); + }, - // 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 - **/ + @deprecated + @for Ember.ControllerMixin + @method transitionTo + */ + transitionTo: function() { + Ember.deprecate("transitionTo is deprecated. Please use transitionToRoute."); + return this.transitionToRoute.apply(this, arguments); + }, /** - An overridable method called when LinkView objects are instantiated. + 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. - Example: + ```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 - App.MyLinkView = Ember.LinkView.extend({ - init: function() { - this._super(); - Ember.Logger.log('Event is ' + this.get('eventName')); - } - }); + 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); ``` - 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. + Multiple models will be applied last to first recursively up the + resource tree. - @method init - */ - init: function() { - this._super.apply(this, arguments); + ```javascript + App.Router.map(function() { + this.resource('blogPost', {path:':blogPostId'}, function(){ + this.resource('blogComment', {path: ':blogCommentId'}); + }); + }); - Ember.deprecate('Using currentWhen with {{link-to}} is deprecated in favor of `current-when`.', !this.currentWhen); + aController.replaceRoute('blogComment', aPost, aComment); + aController.replaceRoute('blogComment', 1, 13); + ``` - // Map desired event name to invoke function - var eventName = get(this, 'eventName'); - this.on(eventName, this, this._invoke); - }, + 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. - /** - This method is invoked by observers installed during `init` that fire - whenever the params change + ```javascript + aController.replaceRoute('/'); + aController.replaceRoute('/blog/post/1/comment/13'); + ``` - @private - @method _paramsChanged - @since 1.3.0 - */ - _paramsChanged: function() { - this.notifyPropertyChange('resolvedParams'); + @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); }, /** - This is called to setup observers that will trigger a rerender. + @deprecated + @for Ember.ControllerMixin + @method replaceWith + */ + replaceWith: function() { + Ember.deprecate("replaceWith is deprecated. Please use replaceRoute."); + return this.replaceRoute.apply(this, arguments); + } + }); - @private - @method _setupPathObservers - @since 1.3.0 - **/ - _setupPathObservers: function(){ - var params = this.params; + var ALL_PERIODS_REGEX = /\./g; - var scheduledRerender = this._wrapAsScheduled(this.rerender); - var scheduledParamsChanged = this._wrapAsScheduled(this._paramsChanged); + function accumulateQueryParamDescriptors(_desc, accum) { + var desc = _desc; + var tmp; + if (typeOf(desc) === 'string') { + tmp = {}; + tmp[desc] = { as: null }; + desc = tmp; + } - if (this.linkTitle) { - this.linkTitle.subscribe(scheduledRerender, this); - } + for (var key in desc) { + if (!desc.hasOwnProperty(key)) { return; } - for (var i = 0; i < params.length; i++) { - var param = params[i]; - if (param && param.isStream) { - param.subscribe(scheduledParamsChanged, this); - } + var singleDesc = desc[key]; + if (typeOf(singleDesc) === 'string') { + singleDesc = { as: singleDesc }; } - var queryParamsObject = this.queryParamsObject; - if (queryParamsObject) { - var values = queryParamsObject.values; - for (var k in values) { - if (!values.hasOwnProperty(k)) { - continue; - } + tmp = accum[key] || { as: null, scope: 'model' }; + merge(tmp, singleDesc); - var value = values[k]; - if (value && value.isStream) { - value.subscribe(scheduledParamsChanged, this); - } - } - } - }, + accum[key] = tmp; + } + } - afterRender: function(){ - this._super.apply(this, arguments); - this._setupPathObservers(); - }, + function listenForQueryParamChanges(controller) { + var qpMap = get(controller, '_normalizedQueryParams'); + for (var prop in qpMap) { + if (!qpMap.hasOwnProperty(prop)) { continue; } + controller.addObserver(prop + '.[]', controller, controller._qpChanged); + } + } - /** - Accessed as a classname binding to apply the `LinkView`'s `disabledClass` - CSS `class` to the element when the link is disabled. + __exports__["default"] = ControllerMixin; + }); +enifed("ember-routing/ext/run_loop", + ["ember-metal/run_loop"], + function(__dependency1__) { + "use strict"; + var run = __dependency1__["default"]; - 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); } + /** + @module ember + @submodule ember-views + */ - return value ? get(this, 'disabledClass') : false; - }), + // 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"]; - /** - Accessed as a classname binding to apply the `LinkView`'s `activeClass` - CSS `class` to the element when the link is active. + /** + @module ember + @submodule ember-routing + */ - 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. + EmberView.reopen({ - The `currentWhen` property can match against multiple routes by separating - route names using the ` ` (space) character. + /** + Sets the private `_outlets` object on the view. - @property active - **/ - active: computed('loadedParams', function computeLinkViewActive() { - if (get(this, 'loading')) { return false; } + @method init + */ + init: function() { + this._outlets = {}; + this._super(); + }, - 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; + /** + Manually fill any of a view's `{{outlet}}` areas with the + supplied view. - function isActiveForRoute(routeName) { - var handlers = router.router.recognizer.handlersFor(routeName); - var leafName = handlers[handlers.length-1].handler; - var maximumContexts = numberOfContextsAcceptedByHandler(routeName, handlers); + Example - // 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`. + ```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:
    - // 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 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]; + } - var args = routeArgs(routeName, contexts, null); - var isActive = router.isActive.apply(router, args); - if (!isActive) { return false; } + if (this._hasEquivalentView(outletName, view)) { + view.destroy(); + return; + } - var emptyQueryParams = Ember.isEmpty(Ember.keys(loadedParams.queryParams)); + var outlets = get(this, '_outlets'); + var container = get(this, 'container'); + var router = container && container.lookup('router:main'); + var renderedName = get(view, 'renderedName'); - if (!isCurrentWhenSpecified && !emptyQueryParams && isActive) { - var visibleQueryParams = {}; - merge(visibleQueryParams, loadedParams.queryParams); - router._prepareQueryParams(loadedParams.targetRouteName, loadedParams.models, visibleQueryParams); - isActive = shallowEqual(visibleQueryParams, router.router.state.queryParams); - } + set(outlets, outletName, view); - return isActive; + if (router && renderedName) { + router._connectActiveView(renderedName, view); } + }, - - currentWhen = currentWhen.split(' '); - for (var i = 0, len = currentWhen.length; i < len; i++) { - if (isActiveForRoute(currentWhen[i])) { - return get(this, 'activeClass'); - } - } - }), + /** + 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'); + }, /** - Accessed as a classname binding to apply the `LinkView`'s `loadingClass` - CSS `class` to the element when the link is loading. + Removes an outlet from the view. - 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. + Example - @property loading - **/ - loading: computed('loadedParams', function computeLinkViewLoading() { - if (!get(this, 'loadedParams')) { return get(this, 'loadingClass'); } - }), + ```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:
    - /** - Returns the application's main router from the container. + var FooView = Ember.View.extend({ + template: Ember.Handlebars.compile('

    Foo

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

    Foo

    + //
    - @private - @property router - **/ - router: computed(function() { - var controller = get(this, 'controller'); - if (controller && controller.container) { - return controller.container.lookup('router:main'); + 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'); + }, /** - Event handler that invokes the link, activating the associated route. + Gets an outlet that is pending disconnection and then + nullifys the object on the `_outlet` object. @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; } + @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; - 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; + for (var outletName in pendingDisconnections) { + set(outlets, outletName, null); } + } + }); - - var targetAttribute2 = get(this, 'target'); - if (targetAttribute2 && targetAttribute2 !== '_self') { - return false; - } - + __exports__["default"] = EmberView; + }); +enifed("ember-routing/location/api", + ["ember-metal/core","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // deprecate, assert - var router = get(this, 'router'); - var loadedParams = get(this, 'loadedParams'); + /** + @module ember + @submodule ember-routing + */ - var transition = router._doTransition(loadedParams.targetRouteName, loadedParams.models, loadedParams.queryParams); - if (get(this, 'replace')) { - transition.method('replace'); - } + /** + Ember.Location returns an instance of the correct implementation of + the `location` API. - // 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); + ## Implementations - run.scheduleOnce('routerTransitions', this, this._eagerUpdateUrl, transition, url); - }, + You can pass an implementation name (`hash`, `history`, `none`) to force a + particular implementation to be used in your application. - /** - @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; - } + ### HashLocation - if (href.indexOf('#') === 0) { - href = href.slice(1); - } + 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. - // 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); - } + Example: - // Prevent later update url refire. - transition.method(null); - }, + ```javascript + App.Router.map(function() { + this.resource('posts', function() { + this.route('new'); + }); + }); - /** - Computed property that returns an array of the - resolved parameters passed to the `link-to` helper, - e.g.: + App.Router.reopen({ + location: 'hash' + }); + ``` - ```hbs - {{link-to a b '123' c}} - ``` + This will result in a posts.new url of `/#/posts/new`. - will generate a `resolvedParams` of: + ### HistoryLocation - ```js - [aObject, bObject, '123', cObject] - ``` + Using `HistoryLocation` results in URLs that are indistinguishable from a + standard URL. This relies upon the browser's `history` API. - @private - @property - @return {Array} - */ - resolvedParams: computed('router.url', function() { - var params = this.params; - var targetRouteName; - var models = []; - var onlyQueryParamsSupplied = (params.length === 0); + Example: - if (onlyQueryParamsSupplied) { - var appController = this.container.lookup('controller:application'); - targetRouteName = get(appController, 'currentRouteName'); - } else { - targetRouteName = read(params[0]); + ```javascript + App.Router.map(function() { + this.resource('posts', function() { + this.route('new'); + }); + }); - for (var i = 1; i < params.length; i++) { - models.push(read(params[i])); - } - } + App.Router.reopen({ + location: 'history' + }); + ``` - var suppliedQueryParams = getResolvedQueryParams(this, targetRouteName); + This will result in a posts.new url of `/posts/new`. - return { - targetRouteName: targetRouteName, - models: models, - queryParams: suppliedQueryParams - }; - }), + Keep in mind that your server must serve the Ember app at all the routes you + define. - /** - 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. + ### AutoLocation - @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; } + Using `AutoLocation`, the router will use the best Location class supported by + the browser it is running in. - var resolvedParams = get(this, 'resolvedParams'); - var namedRoute = resolvedParams.targetRouteName; + 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`. - if (!namedRoute) { return; } + Example: - 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)); + ```javascript + App.Router.map(function() { + this.resource('posts', function() { + this.route('new'); + }); + }); - if (!paramsAreLoaded(resolvedParams.models)) { return; } + App.Router.reopen({ + location: 'auto' + }); + ``` - return resolvedParams; - }), + 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. - queryParamsObject: null, + 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. - /** - Sets the element's `href` attribute to the url for - the `LinkView`'s targeted route. + 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. - If the `LinkView`'s `tagName` is changed to a value other - than `a`, this property will be ignored. + ### NoneLocation - @property href - **/ - href: computed('loadedParams', function computeLinkViewHref() { - if (get(this, 'tagName') !== 'a') { return; } + 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()`. - var router = get(this, 'router'); - var loadedParams = get(this, 'loadedParams'); + ## Location API - if (!loadedParams) { - return get(this, 'loadingHref'); - } + Each location implementation must provide the following methods: - var visibleQueryParams = {}; - merge(visibleQueryParams, loadedParams.queryParams); - router._prepareQueryParams(loadedParams.targetRouteName, loadedParams.models, visibleQueryParams); + * 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. - var args = routeArgs(loadedParams.targetRouteName, loadedParams.models, visibleQueryParams); - var result = router.generate.apply(router, args); - return result; - }), + Calling setURL or replaceURL will not trigger onUpdateURL callbacks. + @class Location + @namespace Ember + @static + */ + __exports__["default"] = { /** - The default href value to use while a link-to is loading. - Only applies when tagName is 'a' - - @property loadingHref - @type String - @default # - */ - loadingHref: '#' - }); + This is deprecated in favor of using the container to lookup the location + implementation as desired. - LinkView.toString = function() { return "LinkView"; }; + For example: - - LinkView.reopen({ - attributeBindings: ['target'], + ```javascript + // Given a location registered as follows: + container.register('location:history-test', HistoryTestLocation); - /** - Sets the `target` attribute of the `LinkView`'s anchor element. + // You could create a new instance via: + container.lookup('location:history-test'); + ``` - @property target - @default null - **/ - target: null - }); - + @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); - /** - 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: + var implementationClass = this.implementations[implementation]; + Ember.assert("Ember.Location.create: " + implementation + " is not a valid implementation", !!implementationClass); - ```handlebars - {{#link-to 'photoGallery'}} - Great Hamster Photos - {{/link-to}} - ``` + return implementationClass.create.apply(implementationClass, arguments); + }, - ```html - - Great Hamster Photos - - ``` + /** + This is deprecated in favor of using the container to register the + location implementation as desired. - ### 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: + Example: - ```handlebars - {{#link-to 'photoGallery' tagName="li"}} - Great Hamster Photos - {{/link-to}} - ``` + ```javascript + Application.initializer({ + name: "history-test-location", - ```html -
  • - Great Hamster Photos -
  • - ``` + initialize: function(container, application) { + application.register('location:history-test', HistoryTestLocation); + } + }); + ``` - To override this option for your entire application, see - "Overriding Application-wide Defaults". + @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); - ### Disabling the `link-to` helper - By default `{{link-to}}` is enabled. - any passed value to `disabled` helper property will disable the `link-to` helper. + this.implementations[name] = implementation; + }, - static use: the `disabled` option: + implementations: {}, + _location: window.location, - ```handlebars - {{#link-to 'photoGallery' disabled=true}} - Great Hamster Photos - {{/link-to}} - ``` + /** + Returns the current `location.hash` by parsing location.href since browsers + inconsistently URL-decode `location.hash`. - dynamic use: the `disabledWhen` option: + https://bugzilla.mozilla.org/show_bug.cgi?id=483304 - ```handlebars - {{#link-to 'photoGallery' disabledWhen=controller.someProperty}} - Great Hamster Photos - {{/link-to}} - ``` + @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('#'); - 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`. + 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; - ```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; - }) - }); - ``` + var EmberLocation = __dependency3__["default"]; + var HistoryLocation = __dependency4__["default"]; + var HashLocation = __dependency5__["default"]; + var NoneLocation = __dependency6__["default"]; - see "Overriding Application-wide Defaults" for more. + /** + @module ember + @submodule ember-routing + */ - ### 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. + /** + Ember.AutoLocation will select the best location option based off browser + support with the priority order: history, hash, none. - ### 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}}`: + Clean pushState paths accessed by hashchange-only browsers will be redirected + to the hash-equivalent and vice versa so future transitions are consistent. - ```handlebars - {{#link-to 'photoGallery.recent'}} - Great Hamster Photos from the last week - {{/link-to}} - ``` + 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. - will result in + @class AutoLocation + @namespace Ember + @static + */ + __exports__["default"] = { - ```html -
    - Great Hamster Photos - - ``` + /** + @private - The CSS class name used for active classes can be customized - for a single use of `{{link-to}}` by passing an `activeClass` - option: + This property is used by router:main to know whether to cancel the routing + setup process, which is needed while we redirect the browser. - ```handlebars - {{#link-to 'photoGallery.recent' activeClass="current-url"}} - Great Hamster Photos from the last week - {{/link-to}} - ``` + @since 1.5.1 + @property cancelRouterSetup + @default false + */ + cancelRouterSetup: false, - ```html - - Great Hamster Photos - - ``` + /** + @private - To override this option for your entire application, see - "Overriding Application-wide Defaults". + Will be pre-pended to path upon state change. - ### 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: + @since 1.5.1 + @property rootURL + @default '/' + */ + rootURL: '/', - ```javascript - App.Router.map(function() { - this.resource("photoGallery", {path: "hamster-photos/:photo_id"}); - }); - ``` + /** + @private - ```handlebars - {{#link-to 'photoGallery' aPhoto}} - {{aPhoto.title}} - {{/link-to}} - ``` + Attached for mocking in tests - ```html - - Tomster - - ``` + @since 1.5.1 + @property _window + @default window + */ + _window: window, - ### 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: + /** + @private - ```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: + Attached for mocking in tests - ```handlebars - {{#link-to 'photoGallery.comment' aPhoto comment}} - {{comment.body}} - {{/link-to}} - ``` + @property location + @default window.location + */ + _location: window.location, - ```html - - A+++ would snuggle again. - - ``` + /** + @private - ### 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: + Attached for mocking in tests - ```javascript - App.Router.map(function() { - this.resource("photoGallery", {path: "hamster-photos/:photo_id"}); - }); - ``` + @since 1.5.1 + @property _history + @default window.history + */ + _history: window.history, - ```handlebars - {{#link-to 'photoGallery' aPhotoId}} - {{aPhoto.title}} - {{/link-to}} - ``` + /** + @private - ```html - - Tomster - - ``` + Attached for mocking in tests - When transitioning into the linked route, the `model` hook will - be triggered with parameters including this passed identifier. + @since 1.5.1 + @property _HistoryLocation + @default Ember.HistoryLocation + */ + _HistoryLocation: HistoryLocation, - ### Allowing Default Action + /** + @private - 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). + Attached for mocking in tests - If you need to override this behavior specify `preventDefault=false` in - your template: + @since 1.5.1 + @property _HashLocation + @default Ember.HashLocation + */ + _HashLocation: HashLocation, - ```handlebars - {{#link-to 'photoGallery' aPhotoId preventDefault=false}} - {{aPhotoId.title}} - {{/link-to}} - ``` + /** + @private - ### 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: + Attached for mocking in tests - ```handlebars - {{#link-to aPhoto tagName='li' title='Following this link will change your life' classNames='pic sweet'}} - Uh-mazing! - {{/link-to}} - ``` + @since 1.5.1 + @property _NoneLocation + @default Ember.NoneLocation + */ + _NoneLocation: NoneLocation, - 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`. + /** + @private - ### 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: + Returns location.origin or builds it if device doesn't support it. - ``` javascript - Ember.LinkView.reopen({ - activeClass: "is-active", - tagName: 'li' - }) - ``` + @method _getOrigin + */ + _getOrigin: function () { + var location = this._location; + var origin = location.origin; - It is also possible to override the default event in - this manner: + // Older browsers, especially IE, don't have origin + if (!origin) { + origin = location.protocol + '//' + location.hostname; - ``` javascript - Ember.LinkView.reopen({ - eventName: 'customEventName' - }); - ``` + if (location.port) { + origin += ':' + location.port; + } + } - @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(name) { - var options = slice.call(arguments, -1)[0]; - var params = slice.call(arguments, 0, -1); - var view = options.data.view; - var hash = options.hash; - var hashTypes = options.hashTypes; - var types = options.types; - var shouldEscape = !hash.unescaped; - var queryParamsObject; + return origin; + }, - Ember.assert("You must provide one or more parameters to the link-to helper.", params.length); + /** + @private - if (params[params.length - 1] instanceof QueryParams) { - hash.queryParamsObject = queryParamsObject = params.pop(); - } + We assume that if the history object has a pushState method, the host should + support HistoryLocation. - if (hash.disabledWhen) { - hash.disabledBinding = hash.disabledWhen; - hashTypes.disabledBinding = hashTypes.disabledWhen; - delete hash.disabledWhen; - delete hashTypes.disabledWhen; - } + @method _getSupportsHistory + */ + _getSupportsHistory: function () { + // Boosted from Modernizr: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/history.js + // The stock browser on Android 2.2 & 2.3 returns positive on history support + // Unfortunately support is really buggy and there is no clean way to detect + // these bugs, so we fall back to a user agent sniff :( + var userAgent = this._window.navigator.userAgent; - if (!options.fn) { - var linkTitle = params.shift(); - var linkTitleType = types.shift(); - if (linkTitleType === 'ID') { - hash.linkTitle = linkTitle = view.getStream(linkTitle); - options.fn = function() { - return stringifyValue(linkTitle.value(), shouldEscape); - }; - } else { - options.fn = function() { - return linkTitle; - }; + // 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; } - } - // Setup route & param streams - for (var i = 0; i < params.length; i++) { - var paramPath = params[i]; - if (types[i] === 'ID') { - var lazyValue = view.getStream(paramPath); + return !!(this._history && 'pushState' in this._history); + }, - // TODO: Consider a better approach to unwrapping controllers. - if (paramPath !== 'controller') { - while (ControllerMixin.detect(lazyValue.value())) { - paramPath = (paramPath === '') ? 'model' : paramPath + '.model'; - lazyValue = view.getStream(paramPath); - } - } - params[i] = lazyValue; - } - } + /** + @private - hash.params = params; + IE8 running in IE7 compatibility mode gives false positive, so we must also + check documentMode. - options.helperName = options.helperName || 'link-to'; + @method _getSupportsHashChange + */ + _getSupportsHashChange: function () { + var _window = this._window; + var documentMode = _window.document.documentMode; - return viewHelper.call(this, LinkView, options); - } + return ('onhashchange' in _window && (documentMode === undefined || documentMode > 7 )); + }, - /** - 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. + /** + @private - Example + Redirects the browser using location.replace, prepending the locatin.origin + to prevent phishing attempts - {#link-to 'posts' (query-params direction="asc")}}Sort{{/link-to}} + @method _replacePath + */ + _replacePath: function (path) { + this._location.replace(this._getOrigin() + path); + }, - @method query-params - @for Ember.Handlebars.helpers - @param {Object} hash takes a hash of query parameters - @return {String} HTML string - */ - function queryParamsHelper(options) { - Ember.assert(fmt("The `query-params` helper only accepts hash parameters, e.g. (query-params queryParamPropertyName='%@') as opposed to just (query-params '%@')", [options, options]), arguments.length === 1); + /** + @since 1.5.1 + @private + @method _getRootURL + */ + _getRootURL: function () { + return this.rootURL; + }, + + /** + @private - var view = options.data.view; - var hash = options.hash; - var hashTypes = options.hashTypes; + Returns the current `location.pathname`, normalized for IE inconsistencies. - for (var k in hash) { - if (hashTypes[k] === 'ID') { - hash[k] = view.getStream(hash[k]); + @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 QueryParams.create({ - values: options.hash - }); - } + return pathname; + }, - __exports__.queryParamsHelper = queryParamsHelper;/** - See [link-to](/api/classes/Ember.Handlebars.helpers.html#method_link-to) + /** + @private - @method linkTo - @for Ember.Handlebars.helpers - @deprecated - @param {String} routeName - @param {Object} [context]* - @return {String} HTML string - */ - function deprecatedLinkToHelper() { - Ember.deprecate("The 'linkTo' view helper is deprecated in favor of 'link-to'"); + Returns normalized location.hash as an alias to Ember.Location._getHash - return linkToHelper.apply(this, arguments); - } + @since 1.5.1 + @method _getHash + */ + _getHash: EmberLocation._getHash, - function getResolvedQueryParams(linkView, targetRouteName) { - var queryParamsObject = linkView.queryParamsObject; - var resolvedQueryParams = {}; + /** + @private - if (!queryParamsObject) { return resolvedQueryParams; } + Returns location.search - var values = queryParamsObject.values; - for (var key in values) { - if (!values.hasOwnProperty(key)) { continue; } - resolvedQueryParams[key] = read(values[key]); - } + @since 1.5.1 + @method _getQuery + */ + _getQuery: function () { + return this._location.search; + }, - return resolvedQueryParams; - } + /** + @private - 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; - } + Returns the full pathname including query and hash - 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; - } + @method _getFullPath + */ + _getFullPath: function () { + return this._getPath() + this._getQuery() + this._getHash(); + }, - __exports__.LinkView = LinkView; - __exports__.deprecatedLinkToHelper = deprecatedLinkToHelper; - __exports__.linkToHelper = linkToHelper; - }); -enifed("ember-routing-handlebars/helpers/outlet", - ["ember-metal/core","ember-metal/property_set","ember-views/views/container_view","ember-handlebars/views/metamorph_view","ember-handlebars/helpers/view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // assert - var set = __dependency2__.set; - var ContainerView = __dependency3__["default"]; - var _Metamorph = __dependency4__._Metamorph; - var viewHelper = __dependency5__.viewHelper; + /** + @private - /** - @module ember - @submodule ember-routing - */ + 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) - /** - @module ember - @submodule ember-routing + @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; - var OutletView = ContainerView.extend(_Metamorph); - __exports__.OutletView = 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. + Ember.assert('Path ' + path + ' does not start with the provided rootURL ' + rootURL, rootURLIndex === 0); - ``` handlebars - {{outlet}} - ``` + // 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(); - 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). + // 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); + } - 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`. + // This is the "expected" final order + path += routeHash; + path += query; - ``` javascript - App.PostsRoute = Ember.Route.extend({ - renderTemplate: function() { - this.render('favoritePost'); + if (hashParts.length) { + path += '#' + hashParts.join('#'); + } + } else { + path += query; + path += hash; } - }); - ``` - - 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. + return path; + }, - ``` javascript - App.PostsRoute = Ember.Route.extend({ - renderTemplate: function() { - this.render('favoritePost', { outlet: 'favoritePost' }); - this.render('posts', { outlet: 'posts' }); - } - }); - ``` + /** + @private - You can specify the view that the outlet uses to contain and manage the - templates rendered into it. + Returns the current path as it should appear for HashLocation supported + browsers. This may very well differ from the real current path. - ``` handlebars - {{outlet view='sectionContainer'}} - ``` + @method _getHashPath + */ + _getHashPath: function () { + var rootURL = this._getRootURL(); + var path = rootURL; + var historyPath = this._getHistoryPath(); + var routePath = historyPath.substr(rootURL.length); - ``` javascript - App.SectionContainer = Ember.ContainerView.extend({ - tagName: 'section', - classNames: ['special'] - }); - ``` + if (routePath !== '') { + if (routePath.charAt(0) !== '/') { + routePath = '/' + routePath; + } - @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(property, options) { - var outletSource; - var viewName; - var viewClass; - var viewFullName; + path += '#' + routePath; + } - if (property && property.data && property.data.isRenderData) { - options = property; - property = 'main'; - } + return path; + }, - Ember.deprecate( - "Using {{outlet}} with an unquoted name is not supported. " + - "Please update to quoted usage '{{outlet \"" + property + "\"}}'.", - arguments.length === 1 || options.types[0] === 'STRING' - ); + /** + Selects the best location option based off browser support and returns an + instance of that Location class. - var view = options.data.view; - var container = view.container; + @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; + } - outletSource = view; - while (!outletSource.get('template.isTop')) { - outletSource = outletSource.get('_parentView'); - } - set(view, 'outletSource', outletSource); + var historyPath, hashPath; + var cancelRouterSetup = false; + var implementationClass = this._NoneLocation; + var currentPath = this._getFullPath(); - // provide controller override - viewName = options.hash.view; + if (this._getSupportsHistory()) { + historyPath = this._getHistoryPath(); - 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 + "\"}}.", - options.hashTypes.view !== 'ID' - ); - Ember.assert( - "The view name you supplied '" + viewName + "' did not resolve to a view.", - container.has(viewFullName) - ); - } + // 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); + } + } - viewClass = viewName ? container.lookupFactory(viewFullName) : options.hash.viewClass || OutletView; - options.types = [ 'ID' ]; + } else if (this._getSupportsHashChange()) { + hashPath = this._getHashPath(); - options.hash.currentViewBinding = '_view.outletSource._outlets.' + property; - options.hashTypes.currentViewBinding = 'STRING'; + // 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); + } + } - options.helperName = options.helperName || 'outlet'; + var implementation = implementationClass.create.apply(implementationClass, arguments); - return viewHelper.call(this, viewClass, options); - } + if (cancelRouterSetup) { + set(implementation, 'cancelRouterSetup', true); + } - __exports__.outletHelper = outletHelper; + return implementation; + } + }; }); -enifed("ember-routing-handlebars/helpers/render", - ["ember-metal/core","ember-metal/error","ember-runtime/system/string","ember-routing/system/generate_controller","ember-handlebars/helpers/view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { +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"]; - // assert, deprecate - var EmberError = __dependency2__["default"]; - var camelize = __dependency3__.camelize; - var generateControllerFactory = __dependency4__.generateControllerFactory; - var generateController = __dependency4__["default"]; - var ViewHelper = __dependency5__.ViewHelper; + 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 @@ -21497,727 +21850,648 @@ enifed("ember-routing-handlebars/helpers/render", */ /** - 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}} -
    - ``` + `Ember.HashLocation` implements the location API using the browser's + hash. At present, it relies on a `hashchange` event existing in the + browser. - @method render - @for Ember.Handlebars.helpers - @param {String} name - @param {Object?} contextString - @param {Hash} options - @return {String} HTML string + @class HashLocation + @namespace Ember + @extends Ember.Object */ - __exports__["default"] = function renderHelper(name, contextString, options) { - var length = arguments.length; - var container, router, controller, view, initialContext; - - container = (options || contextString).data.view._keywords.controller.value().container; - router = container.lookup('router:main'); - - if (length === 2) { - // use the singleton controller - options = contextString; - contextString = undefined; - 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 (length === 3) { - // create a new controller - initialContext = options.data.view.getStream(contextString).value(); - } else { - throw new EmberError("You must pass a templateName to render"); - } + __exports__["default"] = EmberObject.extend({ + implementation: 'hash', - Ember.deprecate("Using a quoteless parameter with {{render}} is deprecated. Please update to" + - " quoted usage '{{render \"" + name + "\"}}.", options.types[0] !== 'ID'); + init: function() { + set(this, 'location', get(this, '_location') || window.location); + }, - // # legacy namespace - name = name.replace(/\//g, '.'); - // \ legacy slash as namespace support + /** + @private + Returns normalized location.hash - view = container.lookup('view:' + name) || container.lookup('view:default'); + @since 1.5.1 + @method getHash + */ + getHash: EmberLocation._getHash, - // provide controller override - var controllerName = options.hash.controller || name; - var controllerFullName = 'controller:' + controllerName; + /** + Returns the normalized URL, constructed from `location.hash`. - if (options.hash.controller) { - Ember.assert("The controller name you supplied '" + controllerName + - "' did not resolve to a controller.", container.has(controllerFullName)); - } + e.g. `#/foo` => `/foo` as well as `#/foo#bar` => `/foo#bar`. - var parentController = options.data.view._keywords.controller.value(); + By convention, hashed paths must begin with a forward slash, otherwise they + are not treated as a path so we can distinguish intent. - // choose name - if (length > 2) { - var factory = container.lookupFactory(controllerFullName) || - generateControllerFactory(container, controllerName, initialContext); + @private + @method getURL + */ + getURL: function() { + var originalPath = this.getHash().substr(1); + var outPath = originalPath; + + if (outPath.charAt(0) !== '/') { + outPath = '/'; - controller = factory.create({ - modelBinding: options.data.view._getBindingForStream(contextString), - parentController: parentController, - target: parentController - }); + // 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; + } + } - view.one('willDestroyElement', function() { - controller.destroy(); - }); - } else { - controller = container.lookup(controllerFullName) || - generateController(container, controllerName); + return outPath; + }, - controller.setProperties({ - target: parentController, - parentController: parentController - }); - } + /** + Set the `location.hash` and remembers what was set. This prevents + `onUpdateURL` callbacks from triggering when the hash was set by + `HashLocation`. - options.hash.viewName = camelize(name); + @private + @method setURL + @param path {String} + */ + setURL: function(path) { + get(this, 'location').hash = path; + set(this, 'lastSetURL', path); + }, - 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.fn); - options.hash.template = container.lookup(templateName); + /** + Uses location.replace to update the url without a page reload + or history modification. - options.hash.controller = controller; + @private + @method replaceURL + @param path {String} + */ + replaceURL: function(path) { + get(this, 'location').replace('#' + path); + set(this, 'lastSetURL', path); + }, - if (router && !initialContext) { - router._connectActiveView(name, view); - } + /** + 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. - options.helperName = options.helperName || ('render "' + name + '"'); + @private + @method onUpdateURL + @param callback {Function} + */ + onUpdateURL: function(callback) { + var self = this; + var guid = guidFor(this); - ViewHelper.instanceHelper(this, view, options); - } - }); -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 + Ember.$(window).on('hashchange.ember-location-'+guid, function() { + run(function() { + var path = self.getURL(); + if (get(self, 'lastSetURL') === path) { return; } - @module ember - @submodule ember-routing - @requires ember-views - */ + set(self, 'lastSetURL', null); - var Ember = __dependency1__["default"]; + callback(path); + }); + }); + }, - // ES6TODO: Cleanup modules with side-effects below + /** + Given a URL, formats it to be placed into the page as part + of an element's `href` attribute. - var EmberLocation = __dependency5__["default"]; - var NoneLocation = __dependency6__["default"]; - var HashLocation = __dependency7__["default"]; - var HistoryLocation = __dependency8__["default"]; - var AutoLocation = __dependency9__["default"]; + This is used, for example, when using the {{action}} helper + to generate a URL based on an event. - var generateControllerFactory = __dependency10__.generateControllerFactory; - var generateController = __dependency10__["default"]; - var controllerFor = __dependency11__["default"]; - var RouterDSL = __dependency12__["default"]; - var Router = __dependency13__["default"]; - var Route = __dependency14__["default"]; + @private + @method formatURL + @param url {String} + */ + formatURL: function(url) { + return '#' + url; + }, - Ember.Location = EmberLocation; - Ember.AutoLocation = AutoLocation; - Ember.HashLocation = HashLocation; - Ember.HistoryLocation = HistoryLocation; - Ember.NoneLocation = NoneLocation; + /** + Cleans up the HashLocation event listener. - Ember.controllerFor = controllerFor; - Ember.generateControllerFactory = generateControllerFactory; - Ember.generateController = generateController; - Ember.RouterDSL = RouterDSL; - Ember.Router = Router; - Ember.Route = Route; + @private + @method willDestroy + */ + willDestroy: function() { + var guid = guidFor(this); - __exports__["default"] = Ember; + Ember.$(window).off('hashchange.ember-location-'+guid); + } + }); }); -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__) { +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 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 get = __dependency1__.get; + var set = __dependency2__.set; + var guidFor = __dependency3__.guidFor; - var ControllerMixin = __dependency7__["default"]; + var EmberObject = __dependency4__["default"]; + var EmberLocation = __dependency5__["default"]; + var jQuery = __dependency6__["default"]; /** @module ember @submodule ember-routing */ - ControllerMixin.reopen({ - concatenatedProperties: ['queryParams', '_pCacheMeta'], + 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() { - this._super.apply(this, arguments); - listenForQueryParamChanges(this); + set(this, 'location', get(this, 'location') || window.location); + set(this, 'baseURL', jQuery('base').attr('href') || ''); }, /** - 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, + Used to set state on first call to setURL - /** - @property _qpDelegate @private + @method initState */ - _qpDelegate: null, + initState: function() { + set(this, 'history', get(this, 'history') || window.history); + this.replaceState(this.formatURL(this.getURL())); + }, /** - @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); - } + Will be pre-pended to path upon state change - return qpMap; - }), + @property rootURL + @default '/' + */ + rootURL: '/', /** - @property _cacheMeta + Returns the current `location.pathname` without `rootURL` or `baseURL` + @private + @method getURL + @return url {String} */ - _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; } + getURL: function() { + var rootURL = get(this, 'rootURL'); + var location = get(this, 'location'); + var path = location.pathname; + var baseURL = get(this, 'baseURL'); - var qp = qpMap[prop]; - var scope = qp.scope; - var parts; + rootURL = rootURL.replace(/\/$/, ''); + baseURL = baseURL.replace(/\/$/, ''); - if (scope === 'controller') { - parts = []; - } + var url = path.replace(baseURL, '').replace(rootURL, ''); + var search = location.search || ''; - cacheMeta[prop] = { - parts: parts, // provided by route if 'model' scope - values: null, // provided by route - scope: scope, - prefix: "", - def: get(this, prop) - }; - } + url += search; + url += this.getHash(); - return cacheMeta; - }), + return url; + }, /** - @method _updateCacheParams + Uses `history.pushState` to update the url without a page reload. + @private + @method setURL + @param path {String} */ - _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; + setURL: function(path) { + var state = this.getState(); + path = this.formatURL(path); - if (cache) { - var value = cache.lookup(cacheKey, prop, propMeta.def); - set(this, prop, value); - } + if (!state || state.path !== path) { + this.pushState(path); } }, /** - @method _qpChanged + Uses `history.replaceState` to update the url without a page reload + or history modification. + @private + @method replaceURL + @param path {String} */ - _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); - } + replaceURL: function(path) { + var state = this.getState(); + path = this.formatURL(path); - // 2. Notify a delegate (e.g. to fire a qp transition) - var delegate = controller._qpDelegate; - if (delegate) { - delegate(controller, prop); + if (!state || state.path !== path) { + this.replaceState(path); } }, /** - @method _calculateCacheKey - @private + 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} */ - _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, '-'); + getState: function() { + return supportsHistoryState ? get(this, 'history').state : this._historyState; }, /** - 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: + Pushes a new state. - ```javascript - aController.transitionToRoute('blogPost', aPost); - ``` + @private + @method pushState + @param path {String} + */ + pushState: function(path) { + var state = { path: path }; - 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: + get(this, 'history').pushState(state, null, path); - ```javascript - aController.transitionToRoute('blogPost', 1); - ``` + // store state if browser doesn't support `history.state` + if (!supportsHistoryState) { + this._historyState = state; + } - Multiple models will be applied last to first recursively up the - resource tree. + // used for webkit workaround + this._previousURL = this.getURL(); + }, - ```javascript - App.Router.map(function() { - this.resource('blogPost', {path:':blogPostId'}, function(){ - this.resource('blogComment', {path: ':blogCommentId'}); - }); - }); + /** + Replaces the current state. - aController.transitionToRoute('blogComment', aPost, aComment); - aController.transitionToRoute('blogComment', 1, 13); - ``` + @private + @method replaceState + @param path {String} + */ + replaceState: function(path) { + var state = { path: path }; + get(this, 'history').replaceState(state, null, path); - 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. + // store state if browser doesn't support `history.state` + if (!supportsHistoryState) { + this._historyState = state; + } - ```javascript - aController.transitionToRoute('/'); - aController.transitionToRoute('/blog/post/1/comment/13'); - ``` + // used for webkit workaround + this._previousURL = this.getURL(); + }, - See also [replaceRoute](/api/classes/Ember.ControllerMixin.html#method_replaceRoute). + /** + Register a callback to be invoked whenever the browser + history changes, including using forward and back buttons. - @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 transitionToRoute + @private + @method onUpdateURL + @param callback {Function} */ - 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); + 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()); + }); }, /** - @deprecated - @for Ember.ControllerMixin - @method transitionTo + Used when using `{{action}}` helper. The url is always appended to the rootURL. + + @private + @method formatURL + @param url {String} + @return formatted url {String} */ - transitionTo: function() { - Ember.deprecate("transitionTo is deprecated. Please use transitionToRoute."); - return this.transitionToRoute.apply(this, arguments); + 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; }, /** - 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'); - ``` + Cleans up the HistoryLocation event listener. - Optionally supply a model for the route in question. The model - will be serialized into the URL using the `serialize` hook of - the route: + @private + @method willDestroy + */ + willDestroy: function() { + var guid = guidFor(this); - ```javascript - aController.replaceRoute('blogPost', aPost); - ``` + jQuery(window).off('popstate.ember-location-'+guid); + }, - 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: + /** + @private - ```javascript - aController.replaceRoute('blogPost', 1); - ``` + Returns normalized location.hash - Multiple models will be applied last to first recursively up the - resource tree. + @method getHash + */ + 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"]; - ```javascript - App.Router.map(function() { - this.resource('blogPost', {path:':blogPostId'}, function(){ - this.resource('blogComment', {path: ':blogCommentId'}); - }); - }); + /** + @module ember + @submodule ember-routing + */ - aController.replaceRoute('blogComment', aPost, aComment); - aController.replaceRoute('blogComment', 1, 13); - ``` + /** + 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). - 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. + @class NoneLocation + @namespace Ember + @extends Ember.Object + */ + __exports__["default"] = EmberObject.extend({ + implementation: 'none', + path: '', - ```javascript - aController.replaceRoute('/'); - aController.replaceRoute('/blog/post/1/comment/13'); - ``` + /** + Returns the current path. - @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 + @private + @method getURL + @return {String} path */ - 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); + getURL: function() { + return get(this, 'path'); }, - /** - @deprecated - @for Ember.ControllerMixin - @method replaceWith + /** + Set the path and remembers what was set. Using this method + to change the path will not invoke the `updateURL` callback. + + @private + @method setURL + @param path {String} */ - replaceWith: function() { - Ember.deprecate("replaceWith is deprecated. Please use replaceRoute."); - return this.replaceRoute.apply(this, arguments); - } - }); + setURL: function(path) { + set(this, 'path', path); + }, - var ALL_PERIODS_REGEX = /\./g; + /** + 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. - function accumulateQueryParamDescriptors(_desc, accum) { - var desc = _desc, tmp; - if (typeOf(desc) === 'string') { - tmp = {}; - tmp[desc] = { as: null }; - desc = tmp; - } + @private + @method onUpdateURL + @param callback {Function} + */ + onUpdateURL: function(callback) { + this.updateCallback = callback; + }, - for (var key in desc) { - if (!desc.hasOwnProperty(key)) { return; } + /** + Sets the path and calls the `updateURL` callback. - var singleDesc = desc[key]; - if (typeOf(singleDesc) === 'string') { - singleDesc = { as: singleDesc }; - } + @private + @method handleURL + @param callback {Function} + */ + handleURL: function(url) { + set(this, 'path', url); + this.updateCallback(url); + }, - tmp = accum[key] || { as: null, scope: 'model' }; - merge(tmp, singleDesc); + /** + Given a URL, formats it to be placed into the page as part + of an element's `href` attribute. - accum[key] = tmp; - } - } + This is used, for example, when using the {{action}} helper + to generate a URL based on an event. - function listenForQueryParamChanges(controller) { - var qpMap = get(controller, '_normalizedQueryParams'); - for (var prop in qpMap) { - if (!qpMap.hasOwnProperty(prop)) { continue; } - controller.addObserver(prop + '.[]', controller, controller._qpChanged); + @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"] = ControllerMixin; + }); }); -enifed("ember-routing/ext/run_loop", - ["ember-metal/run_loop"], - function(__dependency1__) { +enifed("ember-routing/system/cache", + ["ember-runtime/system/object","exports"], + function(__dependency1__, __exports__) { "use strict"; - var run = __dependency1__["default"]; + var EmberObject = __dependency1__["default"]; + __exports__["default"] = EmberObject.extend({ + init: function() { + this.cache = {}; + }, + has: function(bucketKey) { + return bucketKey in this.cache; + }, + stash: function(bucketKey, key, value) { + var bucket = this.cache[bucketKey]; + if (!bucket) { + bucket = this.cache[bucketKey] = {}; + } + bucket[key] = value; + }, + lookup: function(bucketKey, prop, defaultValue) { + var cache = this.cache; + if (!(bucketKey in cache)) { + return defaultValue; + } + var bucket = cache[bucketKey]; + if (prop in bucket) { + return bucket[prop]; + } else { + return defaultValue; + } + }, + cache: null + }); + }); +enifed("ember-routing/system/controller_for", + ["exports"], + function(__exports__) { + "use strict"; /** @module ember - @submodule ember-views + @submodule ember-routing */ - // 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'); + /** + + Finds a controller instance. + + @for Ember + @method controllerFor + @private + */ + __exports__["default"] = function controllerFor(container, controllerName, lookupOptions) { + return container.lookup('controller:' + controllerName, lookupOptions); + } }); -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__) { +enifed("ember-routing/system/dsl", + ["ember-metal/core","exports"], + function(__dependency1__, __exports__) { "use strict"; - var get = __dependency1__.get; - var set = __dependency2__.set; - var run = __dependency3__["default"]; - var EmberView = __dependency4__["default"]; + var Ember = __dependency1__["default"]; + // FEATURES, assert /** @module ember @submodule ember-routing */ - EmberView.reopen({ - - /** - Sets the private `_outlets` object on the view. + function DSL(name) { + this.parent = name; + this.matches = []; + } + __exports__["default"] = DSL; - @method init - */ - init: function() { - this._outlets = {}; - this._super(); - }, + DSL.prototype = { + route: function(name, options, callback) { + if (arguments.length === 2 && typeof options === 'function') { + callback = options; + options = {}; + } - /** - Manually fill any of a view's `{{outlet}}` areas with the - supplied view. + if (arguments.length === 1) { + options = {}; + } - Example + var type = options.resetNamespace === true ? 'resource' : 'route'; + Ember.assert("'basic' cannot be used as a " + type + " name.", name !== 'basic'); - ```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:
    + + if (callback) { + var fullName = getFullName(this, name, options.resetNamespace); + var dsl = new DSL(fullName); + createRoute(dsl, 'loading'); + createRoute(dsl, 'error', { path: "/_unused_dummy_error_path_route_" + name + "/:error" }); - 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]; - } + callback.call(dsl); - if (this._hasEquivalentView(outletName, view)) { - view.destroy(); - return; + createRoute(this, name, options, dsl.generate()); + } else { + createRoute(this, name, options); } + }, - var outlets = get(this, '_outlets'); - var container = get(this, 'container'); - var router = container && container.lookup('router:main'); - var renderedName = get(view, 'renderedName'); + push: function(url, name, callback) { + var parts = name.split('.'); + if (url === "" || url === "/" || parts[parts.length-1] === "index") { this.explicitIndex = true; } - set(outlets, outletName, view); + this.matches.push([url, name, callback]); + }, - if (router && renderedName) { - router._connectActiveView(renderedName, view); + resource: function(name, options, callback) { + if (arguments.length === 2 && typeof options === 'function') { + callback = options; + options = {}; } - }, - /** - 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. + if (arguments.length === 1) { + options = {}; + } - @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'); + options.resetNamespace = true; + this.route(name, options, callback); }, - /** - Removes an outlet from the view. - - Example + generate: function() { + var dslMatches = this.matches; - ```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:
    + if (!this.explicitIndex) { + this.route("index", { path: "/" }); + } - var FooView = Ember.View.extend({ - template: Ember.Handlebars.compile('

    Foo

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

    Foo

    - //
    + return function(match) { + for (var i=0, l=dslMatches.length; iChild view: - ``` + function canNest(dsl) { + return dsl.parent && dsl.parent !== 'application'; + } - @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'); - }, + function getFullName(dsl, name, resetNamespace) { + if (canNest(dsl) && resetNamespace !== true) { + return dsl.parent + "." + name; + } else { + return name; + } + } - /** - Gets an outlet that is pending disconnection and then - nullifys the object on the `_outlet` object. + function createRoute(dsl, name, options, callback) { + options = options || {}; - @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; + var fullName = getFullName(dsl, name, options.resetNamespace); - for (var outletName in pendingDisconnections) { - set(outlets, outletName, null); - } + if (typeof options.path !== 'string') { + options.path = "/" + name; } - }); - __exports__["default"] = EmberView; + dsl.push(options.path, fullName, callback); + } + + DSL.map = function(callback) { + var dsl = new DSL(); + callback.call(dsl); + return dsl; + }; }); -enifed("ember-routing/location/api", - ["ember-metal/core","exports"], - function(__dependency1__, __exports__) { +enifed("ember-routing/system/generate_controller", + ["ember-metal/core","ember-metal/property_get","ember-metal/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; var Ember = __dependency1__["default"]; - // deprecate, assert + // Logger + var get = __dependency2__.get; + var isArray = __dependency3__.isArray; /** @module ember @@ -22225,5661 +22499,6315 @@ enifed("ember-routing/location/api", */ /** - Ember.Location returns an instance of the correct implementation of - the `location` API. + Generates a controller factory - ## Implementations + 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. - You can pass an implementation name (`hash`, `history`, `none`) to force a - particular implementation to be used in your application. + You can customize your generated controllers by defining + `App.ObjectController` or `App.ArrayController`. - ### HashLocation + @for Ember + @method generateControllerFactory + @private + */ - 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. + function generateControllerFactory(container, controllerName, context) { + var Factory, fullName, factoryName, controllerType; - Example: + if (context && isArray(context)) { + controllerType = 'array'; + } else if (context) { + controllerType = 'object'; + } else { + controllerType = 'basic'; + } - ```javascript - App.Router.map(function() { - this.resource('posts', function() { - this.route('new'); - }); - }); + factoryName = 'controller:' + controllerType; - App.Router.reopen({ - location: 'hash' + Factory = container.lookupFactory(factoryName).extend({ + isGenerated: true, + toString: function() { + return "(generated " + controllerName + " controller)"; + } }); - ``` - This will result in a posts.new url of `/#/posts/new`. + fullName = 'controller:' + controllerName; - ### HistoryLocation + container.register(fullName, Factory); - Using `HistoryLocation` results in URLs that are indistinguishable from a - standard URL. This relies upon the browser's `history` API. + return Factory; + } - Example: + __exports__.generateControllerFactory = generateControllerFactory;/** + Generates and instantiates a controller. - ```javascript - App.Router.map(function() { - this.resource('posts', function() { - this.route('new'); - }); - }); + 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. - App.Router.reopen({ - location: 'history' - }); - ``` + @for Ember + @method generateController + @private + @since 1.3.0 + */ + __exports__["default"] = function generateController(container, controllerName, context) { + generateControllerFactory(container, controllerName, context); + var fullName = 'controller:' + controllerName; + var instance = container.lookup(fullName); - This will result in a posts.new url of `/posts/new`. + if (get(instance, 'namespace.LOG_ACTIVE_GENERATION')) { + Ember.Logger.info("generated -> " + fullName, { fullName: fullName }); + } - Keep in mind that your server must serve the Ember app at all the routes you - define. + return instance; + } + }); +enifed("ember-routing/system/query_params", + ["ember-runtime/system/object","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var EmberObject = __dependency1__["default"]; - ### AutoLocation + __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, { + /** + 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`): + + ```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, - Using `AutoLocation`, the router will use the best Location class supported by - the browser it is running in. + // 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 + } + } + ``` - 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`. + @property queryParams + @for Ember.Route + @type Hash + */ + queryParams: {}, - Example: + /** + @private - ```javascript - App.Router.map(function() { - this.resource('posts', function() { - this.route('new'); - }); - }); + @property _qp + */ + _qp: computed(function() { + var controllerName = this.controllerName || this.routeName; + var controllerClass = this.container.lookupFactory('controller:' + controllerName); - App.Router.reopen({ - location: 'auto' - }); - ``` + if (!controllerClass) { + return defaultQPMeta; + } - 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. + var controllerProto = controllerClass.proto(); + var qpProps = get(controllerProto, '_normalizedQueryParams'); + var cacheMeta = get(controllerProto, '_cacheMeta'); - 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. + var qps = [], map = {}, self = this; + for (var propName in qpProps) { + if (!qpProps.hasOwnProperty(propName)) { continue; } - 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. + var desc = qpProps[propName]; + var urlKey = desc.as || this.serializeQueryParamKey(propName); + var defaultValue = get(controllerProto, propName); - ### NoneLocation + if (isArray(defaultValue)) { + defaultValue = Ember.A(defaultValue.slice()); + } - 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()`. + 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] + }; - ## Location API + map[propName] = map[urlKey] = map[fprop] = qp; + qps.push(qp); + } - Each location implementation must provide the following methods: + 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]); + } + } + }; + }), - * 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. + /** + @private - Calling setURL or replaceURL will not trigger onUpdateURL callbacks. + @property _names + */ + _names: null, - @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: + @private - ```javascript - // Given a location registered as follows: - container.register('location:history-test', HistoryTestLocation); + @method _stashNames + */ + _stashNames: function(_handlerInfo, dynamicParent) { + var handlerInfo = _handlerInfo; + if (this._names) { return; } + var names = this._names = handlerInfo._names; - // You could create a new instance via: - container.lookup('location:history-test'); - ``` + if (!names.length) { + handlerInfo = dynamicParent; + names = handlerInfo && handlerInfo._names || []; + } - @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 qps = get(this, '_qp.qps'); + var len = qps.length; - var implementationClass = this.implementations[implementation]; - Ember.assert("Ember.Location.create: " + implementation + " is not a valid implementation", !!implementationClass); + var namePaths = new Array(names.length); + for (var a = 0, nlen = names.length; a < nlen; ++a) { + namePaths[a] = handlerInfo.name + '.' + names[a]; + } - return implementationClass.create.apply(implementationClass, arguments); + 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; + } }, /** - This is deprecated in favor of using the container to register the - location implementation as desired. - - Example: + @private - ```javascript - Application.initializer({ - name: "history-test-location", + @property _updateSerializedQPValue + */ + _updateSerializedQPValue: function(controller, qp) { + var value = get(controller, qp.prop); + qp.svalue = this.serializeQueryParam(value, qp.urlKey, qp.type); + }, - initialize: function(container, application) { - application.register('location:history-test', HistoryTestLocation); - } - }); - ``` + /** + @private - @method registerImplementation - @param {String} name - @param {Object} implementation of the `location` API - @deprecated Register your custom location implementation with the - container directly. + @property _activeQPChanged */ - 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); + _activeQPChanged: function(controller, qp) { + var value = get(controller, qp.prop); + this.router._queuedQPChanges[qp.fprop] = value; + run.once(this, this._fireQueryParamTransition); + }, - this.implementations[name] = implementation; + /** + @private + @method _updatingQPChanged + */ + _updatingQPChanged: function(controller, qp) { + var router = this.router; + if (!router._qpUpdates) { + router._qpUpdates = {}; + } + router._qpUpdates[qp.urlKey] = true; }, - implementations: {}, - _location: window.location, + mergedProperties: ['events', 'queryParams'], /** - Returns the current `location.hash` by parsing location.href since browsers - inconsistently URL-decode `location.hash`. + Retrieves parameters, for current route using the state.params + variable and getQueryParamsFor, using the supplied routeName. - https://bugzilla.mozilla.org/show_bug.cgi?id=483304 + @method paramsFor + @param {String} routename - @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('#'); + paramsFor: function(name) { + var route = this.container.lookup('route:' + name); - if (hashIndex === -1) { - return ''; - } else { - return href.substr(hashIndex); + if (!route) { + return {}; } - } - }; - }); -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. + var transition = this.router.router.activeTransition; + var state = transition ? transition.state : this.router.router.state; - 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. + var params = {}; + merge(params, state.params[name]); + merge(params, getQueryParamsFor(route, state)); - @class AutoLocation - @namespace Ember - @static - */ - __exports__["default"] = { + return params; + }, /** - @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. + Serializes the query parameter key - @since 1.5.1 - @property cancelRouterSetup - @default false + @method serializeQueryParamKey + @param {String} controllerPropertyName */ - cancelRouterSetup: false, + serializeQueryParamKey: function(controllerPropertyName) { + return controllerPropertyName; + }, /** - @private - - Will be pre-pended to path upon state change. + Serializes value of the query parameter based on defaultValueType - @since 1.5.1 - @property rootURL - @default '/' + @method serializeQueryParam + @param {Object} value + @param {String} urlKey + @param {String} defaultValueType */ - rootURL: '/', + 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; + }, /** - @private - - Attached for mocking in tests + Deserializes value of the query parameter based on defaultValueType - @since 1.5.1 - @property _window - @default window + @method deserializeQueryParam + @param {Object} value + @param {String} urlKey + @param {String} defaultValueType */ - _window: window, - - /** - @private + 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. - Attached for mocking in tests + // 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; + }, - @property location - @default window.location - */ - _location: window.location, /** @private - - Attached for mocking in tests - - @since 1.5.1 - @property _history - @default window.history + @property _fireQueryParamTransition */ - _history: window.history, + _fireQueryParamTransition: function() { + this.transitionTo({ queryParams: this.router._queuedQPChanges }); + this.router._queuedQPChanges = {}; + }, /** @private - Attached for mocking in tests - - @since 1.5.1 - @property _HistoryLocation - @default Ember.HistoryLocation + @property _optionsForQueryParam */ - _HistoryLocation: HistoryLocation, + _optionsForQueryParam: function(qp) { + return get(this, 'queryParams.' + qp.urlKey) || get(this, 'queryParams.' + qp.prop) || {}; + }, /** - @private + A hook you can use to reset controller values either when the model + changes or the route is exiting. - Attached for mocking in tests + ```javascript + App.ArticlesRoute = Ember.Route.extend({ + // ... - @since 1.5.1 - @property _HashLocation - @default Ember.HashLocation + resetController: function (controller, isExiting, transition) { + if (isExiting) { + controller.set('page', 1); + } + } + }); + ``` + + @method resetController + @param {Controller} controller instance + @param {Boolean} isExiting + @param {Object} transition + @since 1.7.0 */ - _HashLocation: HashLocation, + resetController: K, /** @private - Attached for mocking in tests - - @since 1.5.1 - @property _NoneLocation - @default Ember.NoneLocation + @method exit */ - _NoneLocation: NoneLocation, + exit: function() { + this.deactivate(); + + this.trigger('deactivate'); + + this.teardownViews(); + }, /** @private - Returns location.origin or builds it if device doesn't support it. - - @method _getOrigin + @method _reset + @since 1.7.0 */ - _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; + _reset: function(isExiting, transition) { + var controller = this.controller; - if (location.port) { - origin += ':' + location.port; - } - } + controller._qpDelegate = get(this, '_qp.states.inactive'); - return origin; + this.resetController(controller, isExiting, transition); }, /** @private - We assume that if the history object has a pushState method, the host should - support HistoryLocation. - - @method _getSupportsHistory + @method enter */ - _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); + enter: function() { + this.activate(); + + this.trigger('activate'); + }, /** - @private + The name of the view to use by default when rendering this routes template. - IE8 running in IE7 compatibility mode gives false positive, so we must also - check documentMode. + 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. - @method _getSupportsHashChange - */ - _getSupportsHashChange: function () { - var _window = this._window; - var documentMode = _window.document.documentMode; + 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: - return ('onhashchange' in _window && (documentMode === undefined || documentMode > 7 )); - }, + ```javascript + var PostsList = Ember.Route.extend({ + viewName: 'postsList' + }); + + App.PostsIndexRoute = PostsList.extend(); + App.PostsArchivedRoute = PostsList.extend(); + ``` + + @property viewName + @type String + @default null + @since 1.4.0 + */ + viewName: null, /** - @private + The name of the template to use by default when rendering this routes + template. - Redirects the browser using location.replace, prepending the locatin.origin - to prevent phishing attempts + This is similar with `viewName`, but is useful when you just want a custom + template without a view. - @method _replacePath - */ - _replacePath: function (path) { - this._location.replace(this._getOrigin() + path); - }, + ```javascript + var PostsList = Ember.Route.extend({ + templateName: 'posts/list' + }); + + App.PostsIndexRoute = PostsList.extend(); + App.PostsArchivedRoute = PostsList.extend(); + ``` - /** - @since 1.5.1 - @private - @method _getRootURL + @property templateName + @type String + @default null + @since 1.4.0 */ - _getRootURL: function () { - return this.rootURL; - }, + templateName: null, /** - @private + The name of the controller to associate with this route. - Returns the current `location.pathname`, normalized for IE inconsistencies. + 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 _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; - } + This is useful in many ways, as the controller specified will be: - return pathname; - }, + * 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. + + @property controllerName + @type String + @default null + @since 1.4.0 + */ + controllerName: null, /** - @private + 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. - Returns normalized location.hash as an alias to Ember.Location._getHash + A good example is preventing navigation when a form is + half-filled out: - @since 1.5.1 - @method _getHash + ```javascript + App.ContactFormRoute = Ember.Route.extend({ + actions: { + willTransition: function(transition) { + if (this.controller.get('userHasEnteredData')) { + this.controller.displayNavigationConfirm(); + transition.abort(); + } + } + } + }); + ``` + + 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()`. + + @event willTransition + @param {Transition} transition */ - _getHash: EmberLocation._getHash, /** - @private + 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. - Returns location.search + ```javascript + App.LoginRoute = Ember.Route.extend({ + actions: { + didTransition: function() { + this.controller.get('errors.base').clear(); + return true; // Bubble the didTransition event + } + } + }); + ``` - @since 1.5.1 - @method _getQuery + @event didTransition + @since 1.2.0 */ - _getQuery: function () { - return this._location.search; - }, /** - @private + 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. - Returns the full pathname including query and hash + ```javascript + App.ApplicationRoute = Ember.Route.extend({ + actions: { + loading: function(transition, route) { + var view = Ember.View.create({ + classNames: ['app-loading'] + }) + .append(); - @method _getFullPath + this.router.one('didTransition', function() { + view.destroy(); + }); + + return true; // Bubble the loading event + } + } + }); + ``` + + @event loading + @param {Transition} transition + @param {Ember.Route} route The route that triggered the loading event + @since 1.2.0 */ - _getFullPath: function () { - return this._getPath() + this._getQuery() + this._getHash(); - }, /** - @private + 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. - 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) + 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: - @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; + ```javascript + App.AdminRoute = Ember.Route.extend({ + beforeModel: function() { + return Ember.RSVP.reject('bad things!'); + }, - Ember.assert('Path ' + path + ' does not start with the provided rootURL ' + rootURL, rootURLIndex === 0); + 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`. - // 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(); + // The `error` hook is also provided the failed + // `transition`, which can be stored and later + // `.retry()`d if desired. - // 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.transitionTo('login'); + } } + }); + ``` - // This is the "expected" final order - path += routeHash; - path += query; + `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`: - if (hashParts.length) { - path += '#' + hashParts.join('#'); + ```javascript + App.ApplicationRoute = Ember.Route.extend({ + actions: { + error: function(error, transition) { + this.controllerFor('banner').displayError(error.message); + } } - } else { - path += query; - path += hash; - } - - return path; - }, + }); + ``` + @event error + @param {Error} error + @param {Transition} transition + */ /** - @private + The controller associated with this route. - Returns the current path as it should appear for HashLocation supported - browsers. This may very well differ from the real current path. + Example - @method _getHashPath + ```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; + } + } + } + }); + ``` + + @property controller + @type Ember.Controller + @since 1.6.0 */ - _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; + _actions: { + + queryParamsDidChange: function(changed, totalPresent, removed) { + var qpMap = get(this, '_qp').map; + + var totalChanged = keys(changed).concat(keys(removed)); + for (var i = 0, len = totalChanged.length; i < len; ++i) { + var qp = qpMap[totalChanged[i]]; + if (qp && get(this._optionsForQueryParam(qp), 'refreshModel')) { + this.refresh(); + } } - path += '#' + routePath; - } + return true; + }, - return path; - }, + finalizeQueryParamChange: function(params, finalParams, transition) { + if (this.routeName !== 'application') { return true; } - /** - Selects the best location option based off browser support and returns an - instance of that Location class. + // Transition object is absent for intermediate transitions. + if (!transition) { return; } - @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 handlerInfos = transition.state.handlerInfos; + var router = this.router; + var qpMeta = router._queryParamsFor(handlerInfos[handlerInfos.length-1].name); + var changes = router._qpUpdates; + var replaceUrl; - var historyPath, hashPath; - var cancelRouterSetup = false; - var implementationClass = this._NoneLocation; - var currentPath = this._getFullPath(); + stashParamNames(router, handlerInfos); - if (this._getSupportsHistory()) { - historyPath = this._getHistoryPath(); + for (var i = 0, len = qpMeta.qps.length; i < len; ++i) { + var qp = qpMeta.qps[i]; + var route = qp.route; + var controller = route.controller; + var presentKey = qp.urlKey in params && qp.urlKey; - // 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; + // Do a reverse lookup to see if the changed query + // param URL key corresponds to a QP property on + // this controller. + var value, svalue; + if (changes && qp.urlKey in changes) { + // Value updated in/before setupController + value = get(controller, qp.prop); + svalue = route.serializeQueryParam(value, qp.urlKey, qp.type); + } else { + if (presentKey) { + svalue = params[presentKey]; + value = route.deserializeQueryParam(svalue, qp.urlKey, qp.type); } else { - cancelRouterSetup = true; - this._replacePath(historyPath); + // No QP provided; use default value. + svalue = qp.sdef; + value = copyDefaultValue(qp.def); } - } - - } 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); + controller._qpDelegate = get(this, '_qp.states.inactive'); - if (cancelRouterSetup) { - set(implementation, 'cancelRouterSetup', true); - } + var thisQueryParamChanged = (svalue !== qp.svalue); + if (thisQueryParamChanged) { + if (transition.queryParamsOnly && replaceUrl !== false) { + var options = route._optionsForQueryParam(qp); + var replaceConfigValue = get(options, 'replace'); + if (replaceConfigValue) { + replaceUrl = true; + } else if (replaceConfigValue === false) { + // Explicit pushState wins over any other replaceStates. + replaceUrl = false; + } + } - 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; + set(controller, qp.prop, value); + } - var EmberObject = __dependency6__["default"]; - var EmberLocation = __dependency7__["default"]; + // Stash current serialized value of controller. + qp.svalue = svalue; - /** - @module ember - @submodule ember-routing - */ + var thisQueryParamHasDefaultValue = (qp.sdef === svalue); + if (!thisQueryParamHasDefaultValue) { + finalParams.push({ + value: svalue, + visible: true, + key: presentKey || qp.urlKey + }); + } + } - /** - `Ember.HashLocation` implements the location API using the browser's - hash. At present, it relies on a `hashchange` event existing in the - browser. + if (replaceUrl) { + transition.method('replace'); + } - @class HashLocation - @namespace Ember - @extends Ember.Object - */ - __exports__["default"] = EmberObject.extend({ - implementation: 'hash', + forEach(qpMeta.qps, function(qp) { + var routeQpMeta = get(qp.route, '_qp'); + var finalizedController = qp.route.controller; + finalizedController._qpDelegate = get(routeQpMeta, 'states.active'); + }); - init: function() { - set(this, 'location', get(this, '_location') || window.location); + router._qpUpdates = null; + } }, /** - @private - - Returns normalized location.hash + @deprecated - @since 1.5.1 - @method getHash + Please use `actions` instead. + @method events */ - getHash: EmberLocation._getHash, + events: null, /** - 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. + This hook is executed when the router completely exits this route. It is + not executed when the model for the route changes. - @private - @method getURL + @method deactivate */ - 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; - }, + deactivate: K, /** - Set the `location.hash` and remembers what was set. This prevents - `onUpdateURL` callbacks from triggering when the hash was set by - `HashLocation`. + This hook is executed when the router enters the route. It is not executed + when the model for the route changes. - @private - @method setURL - @param path {String} + @method activate */ - setURL: function(path) { - get(this, 'location').hash = path; - set(this, 'lastSetURL', path); - }, + activate: K, /** - Uses location.replace to update the url without a page reload - or history modification. + Transition the application into another route. The route may + be either a single route or route path: - @private - @method replaceURL - @param path {String} - */ - replaceURL: function(path) { - get(this, 'location').replace('#' + path); - set(this, 'lastSetURL', path); - }, + ```javascript + this.transitionTo('blogPosts'); + this.transitionTo('blogPosts.recentEntries'); + ``` - /** - 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. + Optionally supply a model for the route in question. The model + will be serialized into the URL using the `serialize` hook of + the route: - @private - @method onUpdateURL - @param callback {Function} - */ - onUpdateURL: function(callback) { - var self = this; - var guid = guidFor(this); + ```javascript + this.transitionTo('blogPost', aPost); + ``` - Ember.$(window).on('hashchange.ember-location-'+guid, function() { - run(function() { - var path = self.getURL(); - if (get(self, 'lastSetURL') === path) { return; } + 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: - set(self, 'lastSetURL', null); + ```javascript + this.transitionTo('blogPost', 1); + ``` - callback(path); + 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' }); }); }); - }, - /** - Given a URL, formats it to be placed into the page as part - of an element's `href` attribute. + this.transitionTo('blogComment', aPost, aComment); + this.transitionTo('blogComment', 1, 13); + ``` - This is used, for example, when using the {{action}} helper - to generate a URL based on an event. + 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. - @private - @method formatURL - @param url {String} - */ - formatURL: function(url) { - return '#' + url; - }, + ```javascript + this.transitionTo('/'); + this.transitionTo('/blog/post/1/comment/13'); + this.transitionTo('/blog/posts?sort=title'); + ``` - /** - Cleans up the HashLocation event listener. + An options hash with a `queryParams` property may be provided as + the final argument to add query parameters to the destination URL. - @private - @method willDestroy - */ - willDestroy: function() { - var guid = guidFor(this); + ```javascript + this.transitionTo('blogPost', 1, { + queryParams: {showComments: 'true'} + }); - 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; + // if you just want to transition the query parameters without changing the route + this.transitionTo({queryParams: {sort: 'date'}}); + ``` - var EmberObject = __dependency4__["default"]; - var EmberLocation = __dependency5__["default"]; - var jQuery = __dependency6__["default"]; + See also 'replaceWith'. - /** - @module ember - @submodule ember-routing - */ + Simple Transition Example - var popstateFired = false; - var supportsHistoryState = window.history && 'state' in window.history; + ```javascript + App.Router.map(function() { + this.route('index'); + this.route('secret'); + this.route('fourOhFour', { path: '*:' }); + }); - /** - Ember.HistoryLocation implements the location API using the browser's - history.pushState API. + App.IndexRoute = Ember.Route.extend({ + actions: { + moveToSecret: function(context) { + if (authorized()) { + this.transitionTo('secret', context); + } else { + this.transitionTo('fourOhFour'); + } + } + } + }); + ``` - @class HistoryLocation - @namespace Ember - @extends Ember.Object - */ - __exports__["default"] = EmberObject.extend({ - implementation: 'history', + Transition to a nested route - init: function() { - set(this, 'location', get(this, 'location') || window.location); - set(this, 'baseURL', jQuery('base').attr('href') || ''); - }, + ```javascript + App.Router.map(function() { + this.resource('articles', { path: '/articles' }, function() { + this.route('new'); + }); + }); - /** - Used to set state on first call to setURL + App.IndexRoute = Ember.Route.extend({ + actions: { + transitionToNewArticle: function() { + this.transitionTo('articles.new'); + } + } + }); + ``` - @private - @method initState - */ - initState: function() { - set(this, 'history', get(this, 'history') || window.history); - this.replaceState(this.formatURL(this.getURL())); - }, + Multiple Models Example - /** - Will be pre-pended to path upon state change + ```javascript + App.Router.map(function() { + this.route('index'); - @property rootURL - @default '/' - */ - rootURL: '/', + this.resource('breakfast', { path: ':breakfastId' }, function() { + this.resource('cereal', { path: ':cerealId' }); + }); + }); - /** - Returns the current `location.pathname` without `rootURL` or `baseURL` + App.IndexRoute = Ember.Route.extend({ + actions: { + moveToChocolateCereal: function() { + var cereal = { cerealId: 'ChocolateYumminess' }; + var breakfast = { breakfastId: 'CerealAndMilk' }; - @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'); + this.transitionTo('cereal', breakfast, cereal); + } + } + }); + ``` - rootURL = rootURL.replace(/\/$/, ''); - baseURL = baseURL.replace(/\/$/, ''); + Nested Route with Query String Example - var url = path.replace(baseURL, '').replace(rootURL, ''); - var search = location.search || ''; + ```javascript + App.Router.map(function() { + this.resource('fruits', function() { + this.route('apples'); + }); + }); - url += search; - url += this.getHash(); + App.IndexRoute = Ember.Route.extend({ + actions: { + transitionToApples: function() { + this.transitionTo('fruits.apples', {queryParams: {color: 'red'}}); + } + } + }); + ``` - return url; + @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 + */ + transitionTo: function(name, context) { + var router = this.router; + return router.transitionTo.apply(router, arguments); }, /** - Uses `history.pushState` to update the url without a page reload. + 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). - @private - @method setURL - @param path {String} - */ - setURL: function(path) { - var state = this.getState(); - path = this.formatURL(path); + 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. - if (!state || state.path !== path) { - this.pushState(path); - } + @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); }, /** - 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); + 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. - if (!state || state.path !== path) { - this.replaceState(path); - } - }, + 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. - /** - 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. + Note that this will cause `model` hooks to fire even on routes + that were provided a model object when the route was initially + entered. - @private - @method getState - @return state {Object} - */ - getState: function() { - return supportsHistoryState ? get(this, 'history').state : this._historyState; + @method refresh + @return {Transition} the transition object associated with this + attempted transition + @since 1.4.0 + */ + refresh: function() { + return this.router.router.refresh(this); }, /** - Pushes a new state. + 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. - @private - @method pushState - @param path {String} - */ - pushState: function(path) { - var state = { path: path }; + Example - get(this, 'history').pushState(state, null, path); + ```javascript + App.Router.map(function() { + this.route('index'); + this.route('secret'); + }); - // store state if browser doesn't support `history.state` - if (!supportsHistoryState) { - this._historyState = state; - } + App.SecretRoute = Ember.Route.extend({ + afterModel: function() { + if (!authorized()){ + this.replaceWith('index'); + } + } + }); + ``` - // used for webkit workaround - this._previousURL = this.getURL(); + @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); }, /** - Replaces the current state. + Sends an action to the router, which will delegate it to the currently + active route hierarchy per the bubbling rules explained under `actions`. - @private - @method replaceState - @param path {String} - */ - replaceState: function(path) { - var state = { path: path }; - get(this, 'history').replaceState(state, null, path); + Example - // store state if browser doesn't support `history.state` - if (!supportsHistoryState) { - this._historyState = state; - } + ```javascript + App.Router.map(function() { + this.route('index'); + }); - // used for webkit workaround - this._previousURL = this.getURL(); - }, + App.ApplicationRoute = Ember.Route.extend({ + actions: { + track: function(arg) { + console.log(arg, 'was clicked'); + } + } + }); - /** - Register a callback to be invoked whenever the browser - history changes, including using forward and back buttons. + App.IndexRoute = Ember.Route.extend({ + actions: { + trackIfDebug: function(arg) { + if (debug) { + this.send('track', arg); + } + } + } + }); + ``` - @private - @method onUpdateURL - @param callback {Function} + @method send + @param {String} name the name of the action to trigger + @param {...*} args */ - 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; } + 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); } - callback(self.getURL()); - }); + } }, /** - Used when using `{{action}}` helper. The url is always appended to the rootURL. + This hook is the entry point for router.js @private - @method formatURL - @param url {String} - @return formatted url {String} + @method setup */ - formatURL: function(url) { - var rootURL = get(this, 'rootURL'); - var baseURL = get(this, 'baseURL'); + setup: function(context, transition) { + var controllerName = this.controllerName || this.routeName; + var controller = this.controllerFor(controllerName, true); - if (url !== '') { - rootURL = rootURL.replace(/\/$/, ''); - baseURL = baseURL.replace(/\/$/, ''); - } else if(baseURL.match(/^\//) && rootURL.match(/^\//)) { - baseURL = baseURL.replace(/\/$/, ''); + if (!controller) { + controller = this.generateController(controllerName, context); } - return baseURL + rootURL + url; - }, + // Assign the route's controller so that it can more easily be + // referenced in action handlers + this.controller = controller; - /** - Cleans up the HistoryLocation event listener. + 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; - @private - @method willDestroy - */ - willDestroy: function() { - var guid = guidFor(this); + if (transition) { + var qpValues = getQueryParamsFor(this, transition.state); + controller.setProperties(qpValues); + } - jQuery(window).off('popstate.ember-location-'+guid); + this.setupController(controller, context, transition); + } + + 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); + } }, /** - @private + 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: - Returns normalized location.hash + 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 getHash + 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`. + + 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. + + ```javascript + App.PostRoute = Ember.Route.extend({ + beforeModel: function(transition) { + if (!App.Post) { + return Ember.$.getScript('/models/post.js'); + } + } + }); + ``` + + 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): + + ```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'); + + // 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); + }); + } + } + }); + ``` + + @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. */ - 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"]; + beforeModel: K, - /** - @module ember - @submodule ember-routing - */ + /** + 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. + + ```javascript + App.PostsRoute = Ember.Route.extend({ + afterModel: function(posts, transition) { + if (posts.get('length') === 1) { + this.transitionTo('post.show', posts.get('firstObject')); + } + } + }); + ``` - /** - 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). + Refer to documentation for `beforeModel` for a description + of transition-pausing semantics when a promise is returned + from this hook. - @class NoneLocation - @namespace Ember - @extends Ember.Object - */ - __exports__["default"] = EmberObject.extend({ - implementation: 'none', - path: '', + @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, /** - Returns the current path. + A hook you can implement to optionally redirect to another route. - @private - @method getURL - @return {String} path - */ - getURL: function() { - return get(this, 'path'); - }, + If you call `this.transitionTo` from inside of this hook, this route + will not be entered in favor of the other hook. - /** - Set the path and remembers what was set. Using this method - to change the path will not invoke the `updateURL` callback. + `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. - @private - @method setURL - @param path {String} + @method redirect + @param {Object} model the model for this route + @param {Transition} transition the transition object associated with the current transition */ - setURL: function(path) { - set(this, 'path', path); - }, + redirect: K, /** - 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. + Called when the context is changed by router.js. @private - @method onUpdateURL - @param callback {Function} + @method contextDidChange */ - onUpdateURL: function(callback) { - this.updateCallback = callback; + contextDidChange: function() { + this.currentModel = this.context; }, /** - Sets the path and calls the `updateURL` callback. + A hook you can implement to convert the URL into the model for + this route. - @private - @method handleURL - @param callback {Function} - */ - handleURL: function(url) { - set(this, 'path', url); - this.updateCallback(url); - }, + ```javascript + App.Router.map(function() { + this.resource('post', { path: '/posts/:post_id' }); + }); + ``` - /** - Given a URL, formats it to be placed into the page as part - of an element's `href` attribute. + The model for the `post` route is `store.find('post', params.post_id)`. - This is used, for example, when using the {{action}} helper - to generate a URL based on an event. + By default, if your route has a dynamic segment ending in `_id`: - @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; - } - }); - }); -enifed("ember-routing/system/cache", - ["ember-runtime/system/object","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var EmberObject = __dependency1__["default"]; + * 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. - __exports__["default"] = EmberObject.extend({ - init: function() { - this.cache = {}; - }, - has: function(bucketKey) { - return bucketKey in this.cache; - }, - stash: function(bucketKey, key, value) { - var bucket = this.cache[bucketKey]; - if (!bucket) { - bucket = this.cache[bucketKey] = {}; - } - bucket[key] = value; - }, - lookup: function(bucketKey, prop, defaultValue) { - var cache = this.cache; - if (!(bucketKey in cache)) { - return defaultValue; - } - var bucket = cache[bucketKey]; - if (prop in bucket) { - return bucket[prop]; - } else { - return defaultValue; - } - }, - cache: null - }); - }); -enifed("ember-routing/system/controller_for", - ["exports"], - function(__exports__) { - "use strict"; - /** - @module ember - @submodule ember-routing - */ + 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. - /** + A model context does not include a primitive string or number, + which does cause the model hook to be called. - Finds a controller instance. + Routes without dynamic segments will always execute the model hook. - @for Ember - @method controllerFor - @private - */ - __exports__["default"] = function controllerFor(container, controllerName, lookupOptions) { - return container.lookup('controller:' + controllerName, lookupOptions); - } - }); -enifed("ember-routing/system/dsl", - ["ember-metal/core","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // FEATURES, assert + ```javascript + // no dynamic segment, model hook always called + this.transitionTo('posts'); - /** - @module ember - @submodule ember-routing - */ + // model passed in, so model hook not called + thePost = store.find('post', 1); + this.transitionTo('post', thePost); - function DSL(name) { - this.parent = name; - this.matches = []; - } - __exports__["default"] = DSL; + // integer passed in, model hook is called + this.transitionTo('post', 1); + ``` - DSL.prototype = { - route: function(name, options, callback) { - if (arguments.length === 2 && typeof options === 'function') { - callback = options; - options = {}; - } - if (arguments.length === 1) { - options = {}; - } + 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`. - var type = options.resetNamespace === true ? 'resource' : 'route'; - Ember.assert("'basic' cannot be used as a " + type + " name.", name !== 'basic'); + Example - - if (callback) { - var fullName = getFullName(this, name, options.resetNamespace); - var dsl = new DSL(fullName); - createRoute(dsl, 'loading'); - createRoute(dsl, 'error', { path: "/_unused_dummy_error_path_route_" + name + "/:error" }); + ```javascript + App.PostRoute = Ember.Route.extend({ + model: function(params) { + return this.store.find('post', params.post_id); + } + }); + ``` - callback.call(dsl); + @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; - createRoute(this, name, options, dsl.generate()); - } else { - createRoute(this, name, options); + var queryParams = get(this, '_qp.map'); + + 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; } - }, - push: function(url, name, callback) { - var parts = name.split('.'); - if (url === "" || url === "/" || parts[parts.length-1] === "index") { this.explicitIndex = true; } + if (!name && sawParams) { return copy(params); } + else if (!name) { + if (transition.resolveIndex < 1) { return; } - this.matches.push([url, name, callback]); - }, + var parentModel = transition.state.handlerInfos[transition.resolveIndex-1].context; - resource: function(name, options, callback) { - if (arguments.length === 2 && typeof options === 'function') { - callback = options; - options = {}; + return parentModel; } - if (arguments.length === 1) { - options = {}; - } + return this.findModel(name, value); + }, - options.resetNamespace = true; - this.route(name, options, callback); + /** + @private + @method deserialize + @param {Object} params the parameters extracted from the URL + @param {Transition} transition + @return {Object|Promise} the model for this route. + + Router.js hook. + */ + deserialize: function(params, transition) { + return this.model(this.paramsFor(this.routeName), transition); }, - generate: function() { - var dslMatches = this.matches; + /** - if (!this.explicitIndex) { - this.route("index", { path: "/" }); - } + @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); + }, - return function(match) { - for (var i=0, l=dslMatches.length; i " + fullName, { fullName: fullName }); - } + 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`: - return instance; - } - }); -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, K, 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; + ```javascript + 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); + } + }); + ``` - var slice = Array.prototype.slice; + 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. - /** - @module ember - @submodule ember-routing - */ + The provided controller will be one resolved based on the name + of this route. - /** - The `Ember.Route` class is used to define individual routes. Refer to - the [routing guide](http://emberjs.com/guides/routing/) for documentation. + If no explicit controller is defined, Ember will automatically create + an appropriate controller for the model. - @class Route - @namespace Ember - @extends Ember.Object - @uses Ember.ActionHandler - */ - var Route = EmberObject.extend(ActionHandler, { - /** - 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`): + * 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`. + + As an example, consider the router: ```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, + App.Router.map(function() { + this.resource('post', { path: '/posts/:post_id' }); + }); + ``` - // 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 + 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. + + Example + + ```javascript + App.PostRoute = Ember.Route.extend({ + setupController: function(controller, model) { + controller.set('model', model); } - } + }); ``` - @property queryParams - @for Ember.Route - @type Hash + @method setupController + @param {Controller} controller instance + @param {Object} model */ - queryParams: {}, + setupController: function(controller, context, transition) { + if (controller && (context !== undefined)) { + set(controller, 'model', context); + } + }, /** - @private + Returns the controller for a particular route or name. - @property _qp + 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} */ - _qp: computed(function() { - var controllerName = this.controllerName || this.routeName; - var controllerClass = this.container.lookupFactory('controller:' + controllerName); + controllerFor: function(name, _skipAssert) { + var container = this.container; + var route = container.lookup('route:'+name); + var controller; - if (!controllerClass) { - return defaultQPMeta; + if (route && route.controllerName) { + name = route.controllerName; } - var controllerProto = controllerClass.proto(); - var qpProps = get(controllerProto, '_normalizedQueryParams'); - var cacheMeta = get(controllerProto, '_cacheMeta'); + controller = container.lookup('controller:' + name); - var qps = [], map = {}, self = this; - for (var propName in qpProps) { - if (!qpProps.hasOwnProperty(propName)) { continue; } + // 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 desc = qpProps[propName]; - var urlKey = desc.as || this.serializeQueryParamKey(propName); - var defaultValue = get(controllerProto, propName); + return controller; + }, - if (isArray(defaultValue)) { - defaultValue = Ember.A(defaultValue.slice()); - } + /** + Generates a controller for a route. - 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] - }; + If the optional model is passed then the controller type is determined automatically, + e.g., an ArrayController for arrays. - map[propName] = map[urlKey] = map[fprop] = qp; - qps.push(qp); - } + Example - 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]); - } + ```javascript + App.PostRoute = Ember.Route.extend({ + setupController: function(controller, post) { + this._super(controller, post); + this.generateController('posts', post); } - }; - }), - - /** - @private + }); + ``` - @property _names + @method generateController + @param {String} name the name of the controller + @param {Object} model the model to infer the type of the controller (optional) */ - _names: null, + generateController: function(name, model) { + var container = this.container; + + model = model || this.modelFor(name); + + return generateController(container, name, model); + }, /** - @private + 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. - @method _stashNames - */ - _stashNames: function(_handlerInfo, dynamicParent) { - var handlerInfo = _handlerInfo; - if (this._names) { return; } - var names = this._names = handlerInfo._names; + Example - if (!names.length) { - handlerInfo = dynamicParent; - names = handlerInfo && handlerInfo._names || []; - } + ```javascript + App.Router.map(function() { + this.resource('post', { path: '/post/:post_id' }, function() { + this.resource('comments'); + }); + }); - var qps = get(this, '_qp.qps'); - var len = qps.length; + App.CommentsRoute = Ember.Route.extend({ + afterModel: function() { + this.set('post', this.modelFor('post')); + } + }); + ``` - var namePaths = new Array(names.length); - for (var a = 0, nlen = names.length; a < nlen; ++a) { - namePaths[a] = handlerInfo.name + '.' + names[a]; - } + @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); + var transition = this.router ? this.router.router.activeTransition : null; - for (var i = 0; i < len; ++i) { - var qp = qps[i]; - var cacheMeta = qp.cacheMeta; - if (cacheMeta.scope === 'model') { - cacheMeta.parts = namePaths; + // 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]; } - cacheMeta.prefix = qp.ctrl; } + + return route && route.currentModel; }, /** - @private + A hook you can use to render the template for the current route. - @property _updateSerializedQPValue - */ - _updateSerializedQPValue: function(controller, qp) { - var value = get(controller, qp.prop); - qp.svalue = this.serializeQueryParam(value, qp.urlKey, qp.type); - }, + 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. - /** - @private + This method can be overridden to set up and render additional or + alternative templates. - @property _activeQPChanged + ```javascript + 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 renderTemplate + @param {Object} controller the route's controller + @param {Object} model the route's model */ - _activeQPChanged: function(controller, qp) { - var value = get(controller, qp.prop); - this.router._queuedQPChanges[qp.fprop] = value; - run.once(this, this._fireQueryParamTransition); + renderTemplate: function(controller, model) { + this.render(); }, /** - @private - @method _updatingQPChanged - */ - _updatingQPChanged: function(controller, qp) { - var router = this.router; - if (!router._qpUpdates) { - router._qpUpdates = {}; - } - router._qpUpdates[qp.urlKey] = true; - }, + `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. - mergedProperties: ['events', 'queryParams'], + For example, given the following minimal router and templates: - /** - Retrieves parameters, for current route using the state.params - variable and getQueryParamsFor, using the supplied routeName. + ```javascript + Router.map(function() { + this.resource('photos'); + }); + ``` - @method paramsFor - @param {String} routename + ```handlebars + +
    + {{outlet "anOutletName"}} +
    + ``` - */ - paramsFor: function(name) { - var route = this.container.lookup('route:' + name); + ```handlebars + +

    Photos

    + ``` - if (!route) { - return {}; - } + You can render `photos.hbs` into the `"anOutletName"` outlet of + `application.hbs` by calling `render`: - var transition = this.router.router.activeTransition; - var state = transition ? transition.state : this.router.router.state; + ```javascript + // posts route + Ember.Route.extend({ + renderTemplate: function(){ + this.render('photos', { + into: 'application', + outlet: 'anOutletName' + }) + } + }); + ``` - var params = {}; - merge(params, state.params[name]); - merge(params, getQueryParamsFor(route, state)); + `render` additionally allows you to supply which `view`, `controller`, and + `model` objects should be loaded and associated with the rendered template. - return params; - }, - /** - Serializes the query parameter key + ```javascript + // 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 serializeQueryParamKey - @param {String} controllerPropertyName - */ - serializeQueryParamKey: function(controllerPropertyName) { - return controllerPropertyName; - }, + 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. - /** - Serializes value of the query parameter based on defaultValueType + 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. - @method serializeQueryParam - @param {Object} value - @param {String} urlKey - @param {String} defaultValueType - */ - 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; - }, + For example: - /** - Deserializes value of the query parameter based on defaultValueType + ```javascript + // router + Router.map(function() { + this.route('index'); + this.resource('post', { path: '/posts/:post_id' }); + }); + ``` - @method deserializeQueryParam - @param {Object} value - @param {String} urlKey - @param {String} defaultValueType + ```javascript + // post route + PostRoute = App.Route.extend({ + renderTemplate: function() { + this.render(); // all defaults apply + } + }); + ``` + + The name of the `PostRoute`, defined by the router, is `post`. + + The following equivalent default options will be applied when + the Route calls `render`: + + ```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 + }) + ``` + + 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. + + @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 */ - 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. + render: function(_name, options) { + Ember.assert("The name in the given arguments is undefined", arguments.length > 0 ? !isNone(arguments[0]) : true); - // 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)); + var namePassed = typeof _name === 'string' && !!_name; + var name; + + if (typeof _name === 'object' && !options) { + name = this.routeName; + options = _name; + } else { + name = _name; } - return value; - }, + var templateName; - /** - @private - @property _fireQueryParamTransition - */ - _fireQueryParamTransition: function() { - this.transitionTo({ queryParams: this.router._queuedQPChanges }); - this.router._queuedQPChanges = {}; + if (name) { + name = name.replace(/\//g, '.'); + templateName = name; + } else { + name = this.routeName; + templateName = this.templateName || name; + } + + var renderOptions = buildRenderOptions(this, namePassed, name, options); + + var LOG_VIEW_LOOKUPS = get(this.router, 'namespace.LOG_VIEW_LOOKUPS'); + var viewName = options && options.view || namePassed && name || this.viewName || name; + var view, template; + + 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 }); + } + } + + if (renderOptions.outlet === 'main') { this.lastRenderedTemplate = name; } + appendView(this, view, renderOptions); }, /** - @private + Disconnects a view that has been rendered into an outlet. - @property _optionsForQueryParam - */ - _optionsForQueryParam: function(qp) { - return get(this, 'queryParams.' + qp.urlKey) || get(this, 'queryParams.' + qp.prop) || {}; - }, + You may pass any or all of the following options to `disconnectOutlet`: + + * `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) - /** - A hook you can use to reset controller values either when the model - changes or the route is exiting. + Example: ```javascript - App.ArticlesRoute = Ember.Route.extend({ - // ... - - resetController: function (controller, isExiting, transition) { - if (isExiting) { - controller.set('page', 1); + 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 resetController - @param {Controller} controller instance - @param {Boolean} isExiting - @param {Object} transition - @since 1.7.0 - */ - resetController: Ember.K, + Alternatively, you can pass the `outlet` name directly as a string. - /** - @private + Example: - @method exit + ```javascript + hideModal: function(evt) { + this.disconnectOutlet('modal'); + } + ``` + + @method disconnectOutlet + @param {Object|String} options the options hash or outlet name */ - exit: function() { - this.deactivate(); - - this.trigger('deactivate'); - + 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'; + + var parentView = this.router._lookupActiveView(options.parentView); + if (parentView) { parentView.disconnectOutlet(options.outlet); } + }, + + willDestroy: function() { this.teardownViews(); }, /** @private - @method _reset - @since 1.7.0 + @method teardownViews */ - _reset: function(isExiting, transition) { - var controller = this.controller; + teardownViews: function() { + // Tear down the top level view + if (this.teardownTopLevelView) { this.teardownTopLevelView(); } - controller._qpDelegate = get(this, '_qp.states.inactive'); + // Tear down any outlets rendered with 'into' + var teardownOutletViews = this.teardownOutletViews || []; + forEach(teardownOutletViews, function(teardownOutletView) { + teardownOutletView(); + }); - this.resetController(controller, isExiting, transition); - }, + delete this.teardownTopLevelView; + delete this.teardownOutletViews; + delete this.lastRenderedTemplate; + } + }); - /** - @private + + // TODO add mixin directly to `Route` class definition above, once this + // feature is merged: + Route.reopen(Evented); + - @method enter - */ - enter: function() { - this.activate(); - - this.trigger('activate'); - - }, + var defaultQPMeta = { + qps: [], + map: {}, + states: {} + }; - /** - The name of the view to use by default when rendering this routes template. + function parentRoute(route) { + var handlerInfo = handlerInfoFor(route, route.router.router.state.handlerInfos, -1); + return handlerInfo && handlerInfo.handler; + } - 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. + function handlerInfoFor(route, handlerInfos, _offset) { + if (!handlerInfos) { return; } - 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: + var offset = _offset || 0; + var current; + for (var i=0, l=handlerInfos.length; i " + routeName, { fullName: routeName }); } } - }); - ``` - Multiple Models Example + handler.routeName = name; + return handler; + }; + }, - ```javascript - App.Router.map(function() { - this.route('index'); + _setupRouter: function(router, location) { + var lastURL, emberRouter = this; - this.resource('breakfast', { path: ':breakfastId' }, function() { - this.resource('cereal', { path: ':cerealId' }); + router.getHandler = this._getHandlerFunction(); + + var doUpdateURL = function() { + location.setURL(lastURL); + }; + + router.updateURL = function(path) { + lastURL = path; + run.once(doUpdateURL); + }; + + if (location.replaceURL) { + var doReplaceURL = function() { + location.replaceURL(lastURL); + }; + + router.replaceURL = function(path) { + lastURL = path; + run.once(doReplaceURL); + }; + } + + router.didTransition = function(infos) { + emberRouter.didTransition(infos); + }; + }, + + _serializeQueryParams: function(targetRouteName, queryParams) { + var groupedByUrlKey = {}; + + 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]; }); - App.IndexRoute = Ember.Route.extend({ - actions: { - moveToChocolateCereal: function() { - var cereal = { cerealId: 'ChocolateYumminess' }; - var breakfast = { breakfastId: 'CerealAndMilk' }; + 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); + } + }, - this.transitionTo('cereal', breakfast, cereal); - } - } + _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); }); - ``` + }, - @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); + _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]; + } + } }, - /** - 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). + _doTransition: function(_targetRouteName, models, _queryParams) { + var targetRouteName = _targetRouteName || getActiveTargetName(this.router); + Ember.assert("The route " + targetRouteName + " was not found", targetRouteName && this.router.hasRoute(targetRouteName)); - 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. + var queryParams = {}; + merge(queryParams, _queryParams); + this._prepareQueryParams(targetRouteName, models, queryParams); - @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); + var transitionArgs = routeArgs(targetRouteName, models, queryParams); + var transitionPromise = this.router.transitionTo.apply(this.router, transitionArgs); + + listenForTransitionErrors(transitionPromise); + + return transitionPromise; + }, + + _prepareQueryParams: function(targetRouteName, models, queryParams) { + this._hydrateUnsuppliedQueryParams(targetRouteName, models, queryParams); + this._serializeQueryParams(targetRouteName, queryParams); + this._pruneDefaultQueryParamValues(targetRouteName, queryParams); }, /** - 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. + 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]; + } - 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. + var map = {}, qps = []; + this._qpCache[leafRouteName] = { + map: map, + qps: qps + }; - 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 routerjs = this.router; + var recogHandlerInfos = routerjs.recognizer.handlersFor(leafRouteName); - @method refresh - @return {Transition} the transition object associated with this - attempted transition - @since 1.4.0 - */ - refresh: function() { - return this.router.router.refresh(this); - }, + 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'); - /** - 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. + if (!qpMeta) { continue; } - Example + merge(map, qpMeta.map); + qps.push.apply(qps, qpMeta.qps); + } - ```javascript - App.Router.map(function() { - this.route('index'); - this.route('secret'); - }); + return { + qps: qps, + map: map + }; + }, - App.SecretRoute = Ember.Route.extend({ - afterModel: function() { - if (!authorized()){ - this.replaceWith('index'); - } + /* + becomeResolved: function(payload, resolvedContext) { + var params = this.serialize(resolvedContext); + + if (payload) { + this.stashResolvedModel(payload, resolvedContext); + payload.params = payload.params || {}; + payload.params[this.name] = params; } - }); - ``` - @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 + return this.factory('resolved', { + context: resolvedContext, + name: this.name, + handler: this.handler, + params: params + }); + }, */ - 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`. + _hydrateUnsuppliedQueryParams: function(leafRouteName, contexts, queryParams) { + var state = calculatePostTransitionState(this, leafRouteName, contexts); + var handlerInfos = state.handlerInfos; + var appCache = this._bucketCache; - Example + stashParamNames(this, handlerInfos); - ```javascript - App.Router.map(function() { - this.route("index"); - }); + for (var i = 0, len = handlerInfos.length; i < len; ++i) { + var route = handlerInfos[i].handler; + var qpMeta = get(route, '_qp'); - App.ApplicationRoute = Ember.Route.extend({ - actions: { - track: function(arg) { - console.log(arg, 'was clicked'); - } - } - }); + 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; - App.IndexRoute = Ember.Route.extend({ - actions: { - trackIfDebug: function(arg) { - if (debug) { - this.send('track', arg); + if (presentProp) { + if (presentProp !== qp.fprop) { + queryParams[qp.fprop] = queryParams[presentProp]; + delete queryParams[presentProp]; } - } - } - }); - ``` + } else { + var controllerProto = qp.cProto; + var cacheMeta = get(controllerProto, '_cacheMeta'); - @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); + var cacheKey = controllerProto._calculateCacheKey(qp.ctrl, cacheMeta[qp.prop].parts, state.params); + queryParams[qp.fprop] = appCache.lookup(cacheKey, qp.prop, qp.def); + } } } }, - /** - This hook is the entry point for router.js + _scheduleLoadingEvent: function(transition, originRoute) { + this._cancelLoadingEvent(); + this._loadingStateTimer = run.scheduleOnce('routerTransitions', this, '_fireLoadingEvent', transition, originRoute); + }, - @private - @method setup - */ - setup: function(context, transition) { - var controllerName = this.controllerName || this.routeName; - var controller = this.controllerFor(controllerName, true); + _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; + } - if (!controller) { - controller = this.generateController(controllerName, context); + transition.trigger(true, 'loading', transition, originRoute); + }, + + _cancelLoadingEvent: function () { + if (this._loadingStateTimer) { + run.cancel(this._loadingStateTimer); } + this._loadingStateTimer = null; + } + }); - // Assign the route's controller so that it can more easily be - // referenced in action handlers - this.controller = controller; + /* + Helper function for iterating root-ward, starting + from (but not including) the provided `originRoute`. - 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; + Returns true if the last callback fired requested + to bubble upward. - if (transition) { - var qpValues = getQueryParamsFor(this, transition.state); - controller.setProperties(qpValues); - } + @private + */ + function forEachRouteAbove(originRoute, transition, callback) { + var handlerInfos = transition.state.handlerInfos; + var originRouteFound = false; + var handlerInfo, route; - this.setupController(controller, context, transition); - } + for (var i = handlerInfos.length - 1; i >= 0; --i) { + handlerInfo = handlerInfos[i]; + route = handlerInfo.handler; - 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); + if (!originRouteFound) { + if (originRoute === route) { + originRouteFound = true; + } + continue; } - }, - /** - 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: + if (callback(route, handlerInfos[i + 1].handler) !== true) { + return false; + } + } + return true; + } - 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. + // These get invoked when an action bubbles above ApplicationRoute + // and are not meant to be overridable. + var defaultActionHandlers = { - 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`. + willResolveModel: function(transition, originRoute) { + originRoute.router._scheduleLoadingEvent(transition, originRoute); + }, - 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. + error: function(error, transition, originRoute) { + // Attempt to find an appropriate error substate to enter. + var router = originRoute.router; - ```javascript - App.PostRoute = Ember.Route.extend({ - beforeModel: function(transition) { - if (!App.Post) { - return Ember.$.getScript('/models/post.js'); - } + var tryTopLevel = forEachRouteAbove(originRoute, transition, function(route, childRoute) { + var childErrorRouteName = findChildRouteName(route, childRoute, 'error'); + if (childErrorRouteName) { + router.intermediateTransitionTo(childErrorRouteName, error); + return; } + return true; }); - ``` - 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 (tryTopLevel) { + // Check for top-level error state to enter. + if (routeHasBeenDefined(originRoute.router, 'application_error')) { + router.intermediateTransitionTo('application_error', error); + return; + } + } - ```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'); + logError(error, 'Error while processing route: ' + transition.targetName); + }, - // 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); - }); - } - } - }); - ``` + loading: function(transition, originRoute) { + // Attempt to find an appropriate loading substate to enter. + var router = originRoute.router; - @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, + var tryTopLevel = forEachRouteAbove(originRoute, transition, function(route, childRoute) { + var childLoadingRouteName = findChildRouteName(route, childRoute, 'loading'); - /** - 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. + if (childLoadingRouteName) { + router.intermediateTransitionTo(childLoadingRouteName); + return; + } - ```javascript - App.PostsRoute = Ember.Route.extend({ - afterModel: function(posts, transition) { - if (posts.get('length') === 1) { - this.transitionTo('post.show', posts.get('firstObject')); - } + // Don't bubble above pivot route. + if (transition.pivotHandler !== route) { + return true; } }); - ``` - Refer to documentation for `beforeModel` for a description - of transition-pausing semantics when a promise is returned - from this hook. + if (tryTopLevel) { + // Check for top-level loading state to enter. + if (routeHasBeenDefined(originRoute.router, 'application_loading')) { + router.intermediateTransitionTo('application_loading'); + return; + } + } + } + }; - @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, + function logError(error, initialMessage) { + var errorArgs = []; - /** - A hook you can implement to optionally redirect to another route. + if (initialMessage) { errorArgs.push(initialMessage); } - If you call `this.transitionTo` from inside of this hook, this route - will not be entered in favor of the other hook. + if (error) { + if (error.message) { errorArgs.push(error.message); } + if (error.stack) { errorArgs.push(error.stack); } - `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 (typeof error === "string") { errorArgs.push(error); } + } - @method redirect - @param {Object} model the model for this route - @param {Transition} transition the transition object associated with the current transition - */ - redirect: Ember.K, + Ember.Logger.error.apply(this, errorArgs); + } - /** - Called when the context is changed by router.js. + function findChildRouteName(parentRoute, originatingChildRoute, name) { + var router = parentRoute.router; + var childName; + var targetChildRouteName = originatingChildRoute.routeName.split('.').pop(); + var namespace = parentRoute.routeName === 'application' ? '' : parentRoute.routeName + '.'; - @private - @method contextDidChange - */ - contextDidChange: function() { - this.currentModel = this.context; - }, + + // Second, try general loading state, e.g. 'loading' + childName = namespace + name; + if (routeHasBeenDefined(router, childName)) { + return childName; + } + } - /** - A hook you can implement to convert the URL into the model for - this route. + function routeHasBeenDefined(router, name) { + var container = router.container; + return router.hasRoute(name) && + (container.has('template:' + name) || container.has('route:' + name)); + } - ```javascript - App.Router.map(function() { - this.resource('post', { path: '/posts/:post_id' }); - }); - ``` + function triggerEvent(handlerInfos, ignoreFailure, args) { + var name = args.shift(); - The model for the `post` route is `store.find('post', params.post_id)`. + 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."); + } - By default, if your route has a dynamic segment ending in `_id`: + var eventWasHandled = false; + var handlerInfo, handler; - * 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. + for (var i = handlerInfos.length - 1; i >= 0; i--) { + handlerInfo = handlerInfos[i]; + handler = handlerInfo.handler; - 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. + if (handler._actions && handler._actions[name]) { + if (handler._actions[name].apply(handler, args) === true) { + eventWasHandled = true; + } else { + return; + } + } + } - A model context does not include a primitive string or number, - which does cause the model hook to be called. + if (defaultActionHandlers[name]) { + defaultActionHandlers[name].apply(null, args); + return; + } - Routes without dynamic segments will always execute the model hook. + 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."); + } + } - ```javascript - // no dynamic segment, model hook always called - this.transitionTo('posts'); + function calculatePostTransitionState(emberRouter, leafRouteName, contexts) { + var routerjs = emberRouter.router; + var state = routerjs.applyIntent(leafRouteName, contexts); + var handlerInfos = state.handlerInfos; + var params = state.params; - // model passed in, so model hook not called - thePost = store.find('post', 1); - this.transitionTo('post', thePost); + 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; + } - // integer passed in, model hook is called - this.transitionTo('post', 1); - ``` + function updatePaths(router) { + var appController = router.container.lookup('controller:application'); + 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; + } - 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`. + var infos = router.router.currentHandlerInfos; + var path = EmberRouter._routePath(infos); - Example + if (!('currentPath' in appController)) { + defineProperty(appController, 'currentPath'); + } + + set(appController, 'currentPath', path); + + if (!('currentRouteName' in appController)) { + defineProperty(appController, 'currentRouteName'); + } + + set(appController, 'currentRouteName', infos[infos.length - 1].name); + } + + EmberRouter.reopenClass({ + router: null, + + /** + 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`. ```javascript - App.PostRoute = Ember.Route.extend({ - model: function(params) { - return this.store.find('post', params.post_id); - } - }); + App.Router.map(function({ + this.route('about'); + this.resource('article'); + })); ``` - @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; + For more detailed examples please see + [the guides](http://emberjs.com/guides/routing/defining-your-routes/). - var queryParams = get(this, '_qp.map'); + @method map + @param callback + */ + map: function(callback) { + var router = this.router; + if (!router) { + router = new Router(); - for (var prop in params) { - if (prop === 'queryParams' || (queryParams && prop in queryParams)) { - continue; - } + + router._triggerWillChangeContext = K; + router._triggerWillLeave = K; + - if (match = prop.match(/^(.*)_id$/)) { - name = match[1]; - value = params[prop]; - } - sawParams = true; + router.callbacks = []; + router.triggerEvent = triggerEvent; + this.reopenClass({ router: router }); } - if (!name && sawParams) { return copy(params); } - else if (!name) { - if (transition.resolveIndex < 1) { return; } + var dsl = EmberRouterDSL.map(function() { + this.resource('application', { path: "/" }, function() { + for (var i=0; i < router.callbacks.length; i++) { + router.callbacks[i].call(this); + } + callback.call(this); + }); + }); + + router.callbacks.push(callback); + router.map(dsl.generate()); + return router; + }, + + _routePath: function(handlerInfos) { + var path = []; - var parentModel = transition.state.handlerInfos[transition.resolveIndex-1].context; + // 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 parentModel; + function intersectionMatches(a1, a2) { + for (var i = 0, len = a1.length; i < len; ++i) { + if (a1[i] !== a2[i]) { + return false; + } + } + return true; } - return this.findModel(name, value); - }, + var name, nameParts, oldNameParts; + for (var i=1, l=handlerInfos.length; i 0) - (diff < 0); + } - return generateController(container, name, model); - }, + /** + This will compare two javascript values of possibly different types. + It will tell you which one is greater than the other by returning: - /** - 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. + - -1 if the first is smaller than the second, + - 0 if both are equal, + - 1 if the first is greater than the second. - Example + 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 - App.Router.map(function() { - this.resource('post', { path: '/post/:post_id' }, function() { - this.resource('comments'); - }); - }); + ```javascript + Ember.compare('hello', 'hello'); // 0 + Ember.compare('abc', 'dfg'); // -1 + Ember.compare(2, 1); // 1 + ``` - App.CommentsRoute = Ember.Route.extend({ - afterModel: function() { - this.set('post', this.modelFor('post')); - } - }); - ``` + @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; + } - @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); - var transition = this.router ? this.router.router.activeTransition : null; + var type1 = typeOf(v); + var type2 = typeOf(w); - // 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]; - } + if (Comparable) { + if (type1 === 'instance' && Comparable.detect(v) && v.constructor.compare) { + return v.constructor.compare(v, w); } - return route && route.currentModel; - }, + if (type2 === 'instance' && Comparable.detect(w) && w.constructor.compare) { + return w.constructor.compare(w, v) * -1; + } + } - /** - A hook you can use to render the template for the current route. + var res = spaceship(TYPE_ORDER[type1], TYPE_ORDER[type2]); - 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. + if (res !== 0) { + return res; + } - This method can be overridden to set up and render additional or - alternative templates. + // types are equal - so we have to check values now + switch (type1) { + case 'boolean': + case 'number': + return spaceship(v,w); - ```javascript - App.PostsRoute = Ember.Route.extend({ - renderTemplate: function(controller, model) { - var favController = this.controllerFor('favoritePost'); + case 'string': + return spaceship(v.localeCompare(w), 0); - // Render the `favoritePost` template into - // the outlet `posts`, and display the `favoritePost` - // controller. - this.render('favoritePost', { - outlet: 'posts', - controller: favController - }); + 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; + } } - }); - ``` - @method renderTemplate - @param {Object} controller the route's controller - @param {Object} model the route's model - */ - renderTemplate: function(controller, model) { - this.render(); - }, + // all elements are equal now + // shorter array should be ordered first + return spaceship(vLen, wLen); - /** - `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. + case 'instance': + if (Comparable && Comparable.detect(v)) { + return v.compare(v, w); + } + return 0; - For example, given the following minimal router and templates: + case 'date': + return spaceship(v.getTime(), w.getTime()); - ```javascript - Router.map(function() { - this.resource('photos'); - }); - ``` + 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"]; - ```handlebars - -
    - {{outlet "anOutletName"}} -
    - ``` + var a_slice = [].slice; - ```handlebars - -

    Photos

    - ``` + function ArrayComputedProperty() { + var cp = this; - You can render `photos.hbs` into the `"anOutletName"` outlet of - `application.hbs` by calling `render`: + ReduceComputedProperty.apply(this, arguments); - ```javascript - // posts route - Ember.Route.extend({ - renderTemplate: function(){ - this.render('photos', { - into: 'application', - outlet: 'anOutletName' - }) + 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); } - }); - ``` - `render` additionally allows you to supply which `view`, `controller`, and - `model` objects should be loaded and associated with the rendered template. + return reduceFunc.apply(this, arguments); + }; + })(this.func); + return this; + } - ```javascript - // 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`. - }) - } - }); - ``` + ArrayComputedProperty.prototype = o_create(ReduceComputedProperty.prototype); - 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. + ArrayComputedProperty.prototype.initialValue = function () { + return Ember.A(); + }; - 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. + ArrayComputedProperty.prototype.resetValue = function (array) { + array.clear(); + return array; + }; - For example: + // This is a stopgap to keep the reference counts correct with lazy CPs. + ArrayComputedProperty.prototype.didChange = function (obj, keyName) { + return; + }; - ```javascript - // router - Router.map(function() { - this.route('index'); - this.resource('post', { path: '/posts/:post_id' }); - }); - ``` + /** + 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`. - ```javascript - // post route - PostRoute = App.Route.extend({ - renderTemplate: function() { - this.render(); // all defaults apply - } - }); - ``` + 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. - The name of the `PostRoute`, defined by the router, is `post`. + `initialize` - An optional initialize function. Typically this will be used + to set up state on the instanceMeta object. - The following equivalent default options will be applied when - the Route calls `render`: + `removedItem` - A function that is called each time an element is + removed from the array. - ```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 - }) - ``` + `addedItem` - A function that is called each time an element is + added to the array. - 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. - @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); + The `initialize` function has the following signature: - var namePassed = typeof name === 'string' && !!name; + ```javascript + function(array, changeMeta, instanceMeta) + ``` - if (typeof name === 'object' && !options) { - options = name; - name = this.routeName; - } + `array` - The initial value of the arrayComputed, an empty array. - options = options || {}; - options.namePassed = namePassed; + `changeMeta` - An object which contains meta information about the + computed. It contains the following properties: - var templateName; + - `property` the computed property + - `propertyName` the name of the property on the object - if (name) { - name = name.replace(/\//g, '.'); - templateName = name; - } else { - name = this.routeName; - templateName = this.templateName || name; - } + `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 viewName = options.view || namePassed && name || this.viewName || name; - var container = this.container; - var view = container.lookup('view:' + viewName); - var template = view ? view.get('template') : null; + The `removedItem` and `addedItem` functions both have the following signature: - if (!template) { - template = container.lookup('template:' + templateName); - } + ```javascript + function(accumulatedValue, item, changeMeta, instanceMeta) + ``` - if (!view && !template) { - Ember.assert("Could not find \"" + name + "\" template or view.", Ember.isEmpty(arguments[0])); - if (get(this.router, 'namespace.LOG_VIEW_LOOKUPS')) { - Ember.Logger.info("Could not find \"" + name + "\" template or view. Nothing will be rendered", { fullName: 'template:' + name }); - } - return; - } + `accumulatedValue` - The value returned from the last time + `removedItem` or `addedItem` was called or an empty array. - options = normalizeOptions(this, name, template, options); - view = setupView(view, container, options); + `item` - the element added or removed from the array - if (options.outlet === 'main') { this.lastRenderedTemplate = name; } + `changeMeta` - An object which contains meta information about the + change. It contains the following properties: - appendView(this, view, options); - }, + - `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. - /** - Disconnects a view that has been rendered into an outlet. + For property changes triggered on an item property change (when + depKey is something like `someArray.@each.someProperty`), + `changeMeta` will also contain the following property: - You may pass any or all of the following options to `disconnectOutlet`: + - `previousValues` an object whose keys are the properties that changed on + the item, and whose values are the item's previous values. - * `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) + `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). - Example: + `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 - 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' - }); - } + 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. + + Example + + ```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; } - }); - ``` + }; - Alternatively, you can pass the `outlet` name directly as a string. + return Ember.arrayComputed(dependentKey, options); + }; + ``` - Example: + @method arrayComputed + @for Ember + @param {String} [dependentKeys*] + @param {Object} options + @return {Ember.ComputedProperty} + */ + function arrayComputed (options) { + var args; - ```javascript - hideModal: function(evt) { - this.disconnectOutlet('modal'); - } - ``` + if (arguments.length > 1) { + args = a_slice.call(arguments, 0, -1); + options = a_slice.call(arguments, -1)[0]; + } - @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'; + if (typeof options !== 'object') { + throw new EmberError('Array Computed Property declared without an options hash'); + } - var parentView = this.router._lookupActiveView(options.parentView); - if (parentView) { parentView.disconnectOutlet(options.outlet); } - }, + var cp = new ArrayComputedProperty(options); - willDestroy: function() { - this.teardownViews(); - }, + if (args) { + cp.property.apply(cp, args); + } - /** - @private + return cp; + } - @method teardownViews - */ - teardownViews: function() { - // Tear down the top level view - if (this.teardownTopLevelView) { this.teardownTopLevelView(); } + __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; - // Tear down any outlets rendered with 'into' - var teardownOutletViews = this.teardownOutletViews || []; - forEach(teardownOutletViews, function(teardownOutletView) { - teardownOutletView(); - }); + var cacheSet = cacheFor.set; + var cacheGet = cacheFor.get; + var cacheRemove = cacheFor.remove; + var a_slice = [].slice; + // 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. + var eachPropertyPattern = /^(.*)\.@each\.(.*)/; + var doubleEachPropertyPattern = /(.*\.@each){2,}/; + var arrayBracketPattern = /\.\[\]$/; - delete this.teardownTopLevelView; - delete this.teardownOutletViews; - delete this.lastRenderedTemplate; + function get(obj, key) { + if (key === '@this') { + return obj; } - }); - - // TODO add mixin directly to `Route` class definition above, once this - // feature is merged: - Route.reopen(Evented); - + return e_get(obj, key); + } - var defaultQPMeta = { - qps: [], - map: {}, - states: {} - }; + /* + Tracks changes to dependent arrays, as well as to properties of items in + dependent arrays. - function parentRoute(route) { - var handlerInfo = handlerInfoFor(route, route.router.router.state.handlerInfos, -1); - return handlerInfo && handlerInfo.handler; - } + @class DependentArraysObserver + */ + function DependentArraysObserver(callbacks, cp, instanceMeta, context, propertyName, sugarMeta) { + // user specified callbacks for `addedItem` and `removedItem` + this.callbacks = callbacks; - function handlerInfoFor(route, handlerInfos, _offset) { - if (!handlerInfos) { return; } + // the computed property: remember these are shared across instances + this.cp = cp; - var offset = _offset || 0, current; - for (var i=0, l=handlerInfos.length; i TrackedArray instances. We use + // this to lazily recompute indexes for item property observers. + this.trackedArraysByGuid = {}; + + // 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; + + // 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 (!parent) { return; } + function ItemPropertyObserverContext (dependentArray, index, trackedArray) { + Ember.assert('Internal error: trackedArray is null or undefined', trackedArray); - if (template = parent.lastRenderedTemplate) { - return template; - } else { - return parentTemplate(parent); - } + this.dependentArray = dependentArray; + this.index = index; + this.item = dependentArray.objectAt(index); + this.trackedArray = trackedArray; + this.beforeObserver = null; + this.observer = null; + this.destroyed = false; } - 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'); + DependentArraysObserver.prototype = { + setValue: function (newValue) { + this.instanceMeta.setValue(newValue, true); + }, - Ember.assert("An outlet ("+options.outlet+") was specified but was not found.", options.outlet === 'main' || options.into); + getValue: function () { + return this.instanceMeta.getValue(); + }, - var controller = options.controller; - var model = options.model; + setupObservers: function (dependentArray, dependentKey) { + this.dependentKeysByGuid[guidFor(dependentArray)] = dependentKey; - if (options.controller) { - controller = options.controller; - } else if (options.namePassed) { - controller = route.container.lookup('controller:' + name) || route.controllerName || route.routeName; - } else { - controller = route.controllerName || route.container.lookup('controller:' + name); - } + dependentArray.addArrayObserver(this, { + willChange: 'dependentArrayWillChange', + didChange: 'dependentArrayDidChange' + }); - 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.cp._itemPropertyKeys[dependentKey]) { + this.setupPropertyObservers(dependentKey, this.cp._itemPropertyKeys[dependentKey]); } - } + }, - if (model) { - controller.set('model', model); - } + teardownObservers: function (dependentArray, dependentKey) { + var itemPropertyKeys = this.cp._itemPropertyKeys[dependentKey] || []; - options.controller = controller; + delete this.dependentKeysByGuid[guidFor(dependentArray)]; - return options; - } + this.teardownPropertyObservers(dependentKey, itemPropertyKeys); - function setupView(view, container, options) { - if (view) { - if (options.LOG_VIEW_LOOKUPS) { - Ember.Logger.info("Rendering " + options.name + " with " + view, { fullName: 'view:' + options.name }); - } - } else { - var defaultView = options.into ? 'view:default' : 'view:toplevel'; - view = container.lookup(defaultView); - if (options.LOG_VIEW_LOOKUPS) { - Ember.Logger.info("Rendering " + options.name + " with default view " + view, { fullName: 'view:' + options.name }); - } - } + dependentArray.removeArrayObserver(this, { + willChange: 'dependentArrayWillChange', + didChange: 'dependentArrayDidChange' + }); + }, - if (!get(view, 'templateName')) { - set(view, 'template', options.template); + suspendArrayObservers: function (callback, binding) { + var oldSuspended = this.suspended; + this.suspended = true; + callback.call(binding); + this.suspended = oldSuspended; + }, - set(view, '_debugTemplateName', options.name); - } + setupPropertyObservers: function (dependentKey, itemPropertyKeys) { + var dependentArray = get(this.instanceMeta.context, dependentKey); + var length = get(dependentArray, 'length'); + var observerContexts = new Array(length); - set(view, 'renderedName', options.name); - set(view, 'controller', options.controller); + this.resetTransformations(dependentKey, observerContexts); - return view; - } + forEach(dependentArray, function (item, index) { + var observerContext = this.createPropertyObserverContext(dependentArray, index, this.trackedArraysByGuid[dependentKey]); + observerContexts[index] = observerContext; - 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 = []; } - 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); - } - } + forEach(itemPropertyKeys, function (propertyKey) { + addBeforeObserver(item, propertyKey, this, observerContext.beforeObserver); + addObserver(item, propertyKey, this, observerContext.observer); + }, this); + }, this); + }, - function generateTopLevelTeardown(view) { - return function() { - view.destroy(); - }; - } + teardownPropertyObservers: function (dependentKey, itemPropertyKeys) { + var dependentArrayObserver = this; + var trackedArray = this.trackedArraysByGuid[dependentKey]; + var beforeObserver, observer, item; - function generateOutletTeardown(parentView, outlet) { - return function() { - parentView.disconnectOutlet(outlet); - }; - } + if (!trackedArray) { return; } - function getFullQueryParams(router, state) { - if (state.fullQueryParams) { return state.fullQueryParams; } + trackedArray.apply(function (observerContexts, offset, operation) { + if (operation === TrackedArray.DELETE) { return; } - state.fullQueryParams = {}; - merge(state.fullQueryParams, state.queryParams); + forEach(observerContexts, function (observerContext) { + observerContext.destroyed = true; + beforeObserver = observerContext.beforeObserver; + observer = observerContext.observer; + item = observerContext.item; - var targetRouteName = state.handlerInfos[state.handlerInfos.length-1].name; - router._deserializeQueryParams(targetRouteName, state.fullQueryParams); - return state.fullQueryParams; - } + forEach(itemPropertyKeys, function (propertyKey) { + removeBeforeObserver(item, propertyKey, dependentArrayObserver, beforeObserver); + removeObserver(item, propertyKey, dependentArrayObserver, observer); + }); + }); + }); + }, - function getQueryParamsFor(route, state) { - state.queryParamsFor = state.queryParamsFor || {}; - var name = route.routeName; + createPropertyObserverContext: function (dependentArray, index, trackedArray) { + var observerContext = new ItemPropertyObserverContext(dependentArray, index, trackedArray); - if (state.queryParamsFor[name]) { return state.queryParamsFor[name]; } + this.createPropertyObserver(observerContext); - var fullQueryParams = getFullQueryParams(route.router, state); + return observerContext; + }, - var params = state.queryParamsFor[name] = {}; + createPropertyObserver: function (observerContext) { + var dependentArrayObserver = this; - // Copy over all the query params for this route/controller into params hash. - var qpMeta = get(route, '_qp'); - var qps = qpMeta.qps; - for (var i = 0, len = qps.length; i < len; ++i) { - // Put deserialized qp on params hash. - var qp = qps[i]; + observerContext.beforeObserver = function (obj, keyName) { + return dependentArrayObserver.itemPropertyWillChange(obj, keyName, observerContext.dependentArray, observerContext); + }; - var qpValueWasPassedIn = (qp.prop in fullQueryParams); - params[qp.prop] = qpValueWasPassedIn ? - fullQueryParams[qp.prop] : - copyDefaultValue(qp.def); - } + observerContext.observer = function (obj, keyName) { + return dependentArrayObserver.itemPropertyDidChange(obj, keyName, observerContext.dependentArray, observerContext); + }; + }, - return params; - } + resetTransformations: function (dependentKey, observerContexts) { + this.trackedArraysByGuid[dependentKey] = new TrackedArray(observerContexts); + }, - function copyDefaultValue(value) { - if (isArray(value)) { - return Ember.A(value.slice()); - } - return value; - } + trackAdd: function (dependentKey, index, newItems) { + var trackedArray = this.trackedArraysByGuid[dependentKey]; - __exports__["default"] = Route; - }); -enifed("ember-routing/system/router", - ["ember-metal/core","ember-metal/error","ember-metal/property_get","ember-metal/property_set","ember-metal/properties","ember-metal/computed","ember-metal/merge","ember-metal/run_loop","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","ember-routing/utils","ember-metal/platform","router","router/transition","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, Logger, K, assert - var EmberError = __dependency2__["default"]; - var get = __dependency3__.get; - var set = __dependency4__.set; - var defineProperty = __dependency5__.defineProperty; - var computed = __dependency6__.computed; - var merge = __dependency7__["default"]; - var run = __dependency8__["default"]; + if (trackedArray) { + trackedArray.addItems(index, newItems); + } + }, - var fmt = __dependency9__.fmt; - var EmberObject = __dependency10__["default"]; - var Evented = __dependency11__["default"]; - var EmberRouterDSL = __dependency12__["default"]; - var EmberView = __dependency13__["default"]; - var EmberLocation = __dependency14__["default"]; - var _MetamorphView = __dependency15__["default"]; - var routeArgs = __dependency16__.routeArgs; - var getActiveTargetName = __dependency16__.getActiveTargetName; - var stashParamNames = __dependency16__.stashParamNames; - var create = __dependency17__.create; + trackRemove: function (dependentKey, index, removedCount) { + var trackedArray = this.trackedArraysByGuid[dependentKey]; - /** - @module ember - @submodule ember-routing - */ + if (trackedArray) { + return trackedArray.removeItems(index, removedCount); + } - var Router = __dependency18__["default"]; + return []; + }, - var slice = [].slice; + 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; + } - /** - The `Ember.Router` class manages the application state and URLs. Refer to - the [routing guide](http://emberjs.com/guides/routing/) for documentation. + forEach(observerContexts, function (context, index) { + context.index = index + offset; + }); + }); + }, - @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. + dependentArrayWillChange: function (dependentArray, index, removedCount, addedCount) { + if (this.suspended) { return; } - The following location types are currently available: + 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; - * `hash` - * `history` - * `none` + observerContexts = this.trackRemove(dependentKey, normalizedIndex, normalizedRemoveCount); - @property location - @default 'hash' - @see {Ember.Location} - */ - location: 'hash', + function removeObservers(propertyKey) { + observerContexts[sliceIndex].destroyed = true; + removeBeforeObserver(item, propertyKey, this, observerContexts[sliceIndex].beforeObserver); + removeObserver(item, propertyKey, this, observerContexts[sliceIndex].observer); + } - /** - Represents the URL of the root of the application, often '/'. This prefix is - assumed on all routes defined on this router. + for (sliceIndex = normalizedRemoveCount - 1; sliceIndex >= 0; --sliceIndex) { + itemIndex = normalizedIndex + sliceIndex; + if (itemIndex >= length) { break; } - @property rootURL - @default '/' - */ - rootURL: '/', + item = dependentArray.objectAt(itemIndex); - init: function() { - this.router = this.constructor.router || this.constructor.map(Ember.K); - this._activeViews = {}; - this._setupLocation(); - this._qpCache = {}; - this._queuedQPChanges = {}; + forEach(itemPropertyKeys, removeObservers, this); - if (get(this, 'namespace.LOG_TRANSITIONS_INTERNAL')) { - this.router.log = Ember.Logger.debug; + 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); }, - /** - Represents the current URL. + dependentArrayDidChange: function (dependentArray, index, removedCount, addedCount) { + if (this.suspended) { return; } - @method url - @return {String} The current URL. - */ - url: computed(function() { - return get(this, 'location').getURL(); - }), + 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; - /** - Initializes the current router instance and sets up the change handling - event listeners used by the instances `location` implementation. + forEach(dependentArray.slice(normalizedIndex, endIndex), function (item, sliceIndex) { + if (itemPropertyKeys) { + observerContext = this.createPropertyObserverContext(dependentArray, normalizedIndex + sliceIndex, + this.trackedArraysByGuid[dependentKey]); + observerContexts[sliceIndex] = observerContext; - A property named `initialURL` will be used to determine the initial URL. - If no value is found `/` will be used. + forEach(itemPropertyKeys, function (propertyKey) { + addBeforeObserver(item, propertyKey, this, observerContext.beforeObserver); + addObserver(item, propertyKey, this, observerContext.observer); + }, this); + } - @method startRouting - @private - */ - startRouting: function() { - this.router = this.router || this.constructor.map(Ember.K); + 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); + }, - var router = this.router; - var location = get(this, 'location'); - var container = this.container; - var self = this; - var initialURL = get(this, 'initialURL'); - var initialTransition; + itemPropertyWillChange: function (obj, keyName, array, observerContext) { + var guid = guidFor(obj); - // Allow the Location class to cancel the router setup while it refreshes - // the page - if (get(location, 'cancelRouterSetup')) { - return; + if (!this.changedItems[guid]) { + this.changedItems[guid] = { + array: array, + observerContext: observerContext, + obj: obj, + previousValues: {} + }; } - this._setupRouter(router, location); - - container.register('view:default', _MetamorphView); - container.register('view:toplevel', EmberView.extend()); - - location.onUpdateURL(function(url) { - self.handleURL(url); - }); + ++this.changedItemCount; + this.changedItems[guid].previousValues[keyName] = get(obj, keyName); + }, - if (typeof initialURL === "undefined") { - initialURL = location.getURL(); - } - initialTransition = this.handleURL(initialURL); - if (initialTransition && initialTransition.error) { - throw initialTransition.error; + itemPropertyDidChange: function (obj, keyName, array, observerContext) { + if (--this.changedItemCount === 0) { + this.flushChanges(); } }, - /** - Handles updating the paths and notifying any listeners of the URL - change. + flushChanges: function () { + var changedItems = this.changedItems; + var key, c, changeMeta; - Triggers the router level `didTransition` hook. + for (key in changedItems) { + c = changedItems[key]; + if (c.observerContext.destroyed) { continue; } - @method didTransition - @private - @since 1.2.0 - */ - didTransition: function(infos) { - updatePaths(this); + this.updateIndexes(c.observerContext.trackedArray, c.observerContext.dependentArray); - this._cancelLoadingEvent(); + 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)); + } - this.notifyPropertyChange('url'); + this.changedItems = {}; + this.callbacks.flushedChanges.call(this.instanceMeta.context, this.getValue(), this.instanceMeta.sugarMeta); + } + }; - // 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'); + 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); + } + } - if (get(this, 'namespace').LOG_TRANSITIONS) { - Ember.Logger.log("Transitioned into '" + EmberRouter._routePath(infos) + "'"); - } - }, + function normalizeRemoveCount(index, length, removedCount) { + return Math.min(removedCount, length - index); + } - handleURL: function(url) { - // Until we have an ember-idiomatic way of accessing #hashes, we need to - // remove it because router.js doesn't know how to handle it. - url = url.split(/#(.+)?/)[0]; - return this._doURLTransition('handleURL', url); - }, + 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; - _doURLTransition: function(routerJsMethod, url) { - var transition = this.router[routerJsMethod](url || '/'); - listenForTransitionErrors(transition); - return transition; - }, + if (previousValues) { + // previous values only available for item property changes + this.previousValues = previousValues; + } + } - transitionTo: function() { - var args = slice.call(arguments), queryParams; - if (resemblesURL(args[0])) { - return this._doURLTransition('transitionTo', args[0]); - } + 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); + } - var possibleQueryParams = args[args.length-1]; - if (possibleQueryParams && possibleQueryParams.hasOwnProperty('queryParams')) { - queryParams = args.pop().queryParams; - } else { - queryParams = {}; - } + function reset(cp, propertyName) { + var hadMeta = cp._hasInstanceMeta(this, propertyName); + var meta = cp._instanceMeta(this, propertyName); - var targetRouteName = args.shift(); - return this._doTransition(targetRouteName, args, queryParams); - }, + if (hadMeta) { meta.setValue(cp.resetValue(meta.getValue())); } - intermediateTransitionTo: function() { - this.router.intermediateTransitionTo.apply(this.router, arguments); + if (cp.options.initialize) { + cp.options.initialize.call(this, meta.getValue(), { + property: cp, + propertyName: propertyName + }, meta.sugarMeta); + } + } - updatePaths(this); + function partiallyRecomputeFor(obj, dependentKey) { + if (arrayBracketPattern.test(dependentKey)) { + return false; + } - var infos = this.router.currentHandlerInfos; - if (get(this, 'namespace').LOG_TRANSITIONS) { - Ember.Logger.log("Intermediate-transitioned into '" + EmberRouter._routePath(infos) + "'"); + 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; + this.dependentArrays = {}; + this.sugarMeta = {}; + this.initialValue = initialValue; + } + + ReduceComputedPropertyInstanceMeta.prototype = { + getValue: function () { + var value = cacheGet(this.cache, this.propertyName); + + if (value !== undefined) { + return value; + } else { + return this.initialValue; } }, - replaceWith: function() { - return this.transitionTo.apply(this, arguments).method('replace'); - }, + 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; + } - generate: function() { - var url = this.router.generate.apply(this.router, arguments); - return this.location.formatURL(url); - }, + if (triggerObservers) { + propertyWillChange(this.context, this.propertyName); + } - /** - Determines if the supplied route is currently active. + if (newValue === undefined) { + cacheRemove(this.cache, this.propertyName); + } else { + cacheSet(this.cache, this.propertyName, newValue); + } - @method isActive - @param routeName - @return {Boolean} - @private - */ - isActive: function(routeName) { - var router = this.router; - return router.isActive.apply(router, arguments); - }, + if (triggerObservers) { + propertyDidChange(this.context, this.propertyName); + } + } + }; - /** - An alternative form of `isActive` that doesn't require - manual concatenation of the arguments into a single - array. + /** + A computed property whose dependent keys are arrays and which is updated with + "one at a time" semantics. - @method isActiveIntent - @param routeName - @param models - @param queryParams - @return {Boolean} - @private - @since 1.7.0 - */ - isActiveIntent: function(routeName, models, queryParams) { - var router = this.router; - return router.isActive.apply(router, arguments); - }, + @class ReduceComputedProperty + @namespace Ember + @extends Ember.ComputedProperty + @constructor + */ - send: function(name, context) { - this.router.trigger.apply(this.router, arguments); - }, + __exports__.ReduceComputedProperty = ReduceComputedProperty; + // TODO: default export - /** - Does this router instance have the given route. + function ReduceComputedProperty(options) { + var cp = this; - @method hasRoute - @return {Boolean} - @private - */ - hasRoute: function(route) { - return this.router.hasRoute(route); - }, + 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 = {}; - /** - Resets the state of the router by clearing the current route - handlers and deactivating them. + this.readOnly(); - @private - @method reset - */ - reset: function() { - this.router.reset(); - }, + 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); + }; - _lookupActiveView: function(templateName) { - var active = this._activeViews[templateName]; - return active && active[0]; - }, + var recompute = function(propertyName) { + var meta = cp._instanceMeta(this, propertyName); + var callbacks = cp._callbacks(); - _connectActiveView: function(templateName, view) { - var existing = this._activeViews[templateName]; + reset.call(this, cp, propertyName); - if (existing) { - existing[0].off('willDestroyElement', this, existing[1]); - } + 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)))); - function disconnectActiveView() { - delete this._activeViews[templateName]; - } + if (!partiallyRecomputeFor(this, dependentKey)) { return; } - this._activeViews[templateName] = [view, disconnectActiveView]; - view.one('willDestroyElement', this, disconnectActiveView); - }, + var dependentArray = get(this, dependentKey); + var previousDependentArray = meta.dependentArrays[dependentKey]; - _setupLocation: function() { - var location = get(this, 'location'); - var rootURL = get(this, 'rootURL'); + 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; - if (rootURL && this.container && !this.container.has('-location-setting:root-url')) { - this.container.register('-location-setting:root-url', rootURL, { - instantiate: false - }); - } + if (previousDependentArray) { + meta.dependentArraysObserver.teardownObservers(previousDependentArray, dependentKey); + } - if ('string' === typeof location && this.container) { - var resolvedLocation = this.container.lookup('location:' + location); + if (dependentArray) { + meta.dependentArraysObserver.setupObservers(dependentArray, dependentKey); + } + } + }, this); + }, this); - if ('undefined' !== typeof resolvedLocation) { - location = set(this, 'location', resolvedLocation); - } else { - // Allow for deprecated registration of custom location API's - var options = { - implementation: location - }; + forEach(cp._dependentKeys, function(dependentKey) { + if (!partiallyRecomputeFor(this, dependentKey)) { return; } - location = set(this, 'location', EmberLocation.create(options)); - } - } + var dependentArray = get(this, dependentKey); - if (location !== null && typeof location === 'object') { - if (rootURL && typeof rootURL === 'string') { - location.rootURL = rootURL; + if (dependentArray) { + addItems.call(this, dependentArray, callbacks, cp, propertyName, meta); } + }, this); + }; - // ensure that initState is called AFTER the rootURL is set on - // the location instance - if (typeof location.initState === 'function') { - location.initState(); - } - } - }, - _getHandlerFunction: function() { - var seen = create(null); - var container = this.container; - var DefaultRoute = container.lookupFactory('route:basic'); - var self = this; + this.func = function (propertyName) { + Ember.assert('Computed reduce values require at least one dependent key', cp._dependentKeys); - return function(name) { - var routeName = 'route:' + name; - var handler = container.lookup(routeName); + recompute.call(this, propertyName); - if (seen[name]) { - return handler; - } + return cp._instanceMeta(this, propertyName).getValue(); + }; + } - seen[name] = true; + ReduceComputedProperty.prototype = o_create(ComputedProperty.prototype); - if (!handler) { - container.register(routeName, DefaultRoute.extend()); - handler = container.lookup(routeName); + function defaultCallback(computedValue) { + return computedValue; + } - if (get(self, 'namespace.LOG_ACTIVE_GENERATION')) { - Ember.Logger.info("generated -> " + routeName, { fullName: routeName }); - } - } + ReduceComputedProperty.prototype._callbacks = function () { + if (!this.callbacks) { + var options = this.options; - handler.routeName = name; - return handler; + this.callbacks = { + removedItem: options.removedItem || defaultCallback, + addedItem: options.addedItem || defaultCallback, + flushedChanges: options.flushedChanges || defaultCallback }; - }, + } - _setupRouter: function(router, location) { - var lastURL, emberRouter = this; + return this.callbacks; + }; - router.getHandler = this._getHandlerFunction(); + ReduceComputedProperty.prototype._hasInstanceMeta = function (context, propertyName) { + return !!metaFor(context).cacheMeta[propertyName]; + }; - var doUpdateURL = function() { - location.setURL(lastURL); - }; + ReduceComputedProperty.prototype._instanceMeta = function (context, propertyName) { + var cacheMeta = metaFor(context).cacheMeta; + var meta = cacheMeta[propertyName]; - router.updateURL = function(path) { - lastURL = path; - run.once(doUpdateURL); - }; + if (!meta) { + meta = cacheMeta[propertyName] = new ReduceComputedPropertyInstanceMeta(context, propertyName, this.initialValue()); + meta.dependentArraysObserver = new DependentArraysObserver(this._callbacks(), this, meta, context, propertyName, meta.sugarMeta); + } - if (location.replaceURL) { - var doReplaceURL = function() { - location.replaceURL(lastURL); - }; + return meta; + }; - router.replaceURL = function(path) { - lastURL = path; - run.once(doReplaceURL); - }; - } + ReduceComputedProperty.prototype.initialValue = function () { + if (typeof this.options.initialValue === 'function') { + return this.options.initialValue(); + } + else { + return this.options.initialValue; + } + }; - router.didTransition = function(infos) { - emberRouter.didTransition(infos); - }; - }, + ReduceComputedProperty.prototype.resetValue = function (value) { + return this.initialValue(); + }; - _serializeQueryParams: function(targetRouteName, queryParams) { - var groupedByUrlKey = {}; + ReduceComputedProperty.prototype.itemPropertyKey = function (dependentArrayKey, itemPropertyKey) { + this._itemPropertyKeys[dependentArrayKey] = this._itemPropertyKeys[dependentArrayKey] || []; + this._itemPropertyKeys[dependentArrayKey].push(itemPropertyKey); + }; - 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]; - }); + ReduceComputedProperty.prototype.clearItemPropertyKeys = function (dependentArrayKey) { + if (this._itemPropertyKeys[dependentArrayKey]) { + this._previousItemPropertyKeys[dependentArrayKey] = this._itemPropertyKeys[dependentArrayKey]; + this._itemPropertyKeys[dependentArrayKey] = []; + } + }; - for (var key in groupedByUrlKey) { - var qps = groupedByUrlKey[key]; - if (qps.length > 1) { - var qp0 = qps[0].qp, qp1=qps[1].qp; - 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-%@' }`", [qp0.fprop, qp1.fprop, qp0.urlKey, qp0.prop, qp0.prop]), false); - } - var qp = qps[0].qp; - queryParams[qp.urlKey] = qp.route.serializeQueryParam(qps[0].value, qp.urlKey, qp.type); - } - }, + ReduceComputedProperty.prototype.property = function () { + var cp = this; + var args = a_slice.call(arguments); + var propertyArgs = {}; + var match, dependentArrayKey; - _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); - }); - }, + 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]; - _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]; - } + var itemPropertyKeyPattern = match[2]; + var addItemPropertyKey = function (itemPropertyKey) { + cp.itemPropertyKey(dependentArrayKey, itemPropertyKey); + }; + + expandProperties(itemPropertyKeyPattern, addItemPropertyKey); + propertyArgs[guidFor(dependentArrayKey)] = dependentArrayKey; + } else { + propertyArgs[guidFor(dependentKey)] = dependentKey; } - }, + }); - _doTransition: function(_targetRouteName, models, _queryParams) { - var targetRouteName = _targetRouteName || getActiveTargetName(this.router); - Ember.assert("The route " + targetRouteName + " was not found", targetRouteName && this.router.hasRoute(targetRouteName)); + var propertyArgsToArray = []; + for (var guid in propertyArgs) { + propertyArgsToArray.push(propertyArgs[guid]); + } - var queryParams = {}; - merge(queryParams, _queryParams); - this._prepareQueryParams(targetRouteName, models, queryParams); + return ComputedProperty.prototype.property.apply(this, propertyArgsToArray); + }; - var transitionArgs = routeArgs(targetRouteName, models, queryParams); - var transitionPromise = this.router.transitionTo.apply(this.router, transitionArgs); + /** + 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. - listenForTransitionErrors(transitionPromise); + 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: - return transitionPromise; - }, + `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. - _prepareQueryParams: function(targetRouteName, models, queryParams) { - this._hydrateUnsuppliedQueryParams(targetRouteName, models, queryParams); - this._serializeQueryParams(targetRouteName, queryParams); - this._pruneDefaultQueryParamValues(targetRouteName, queryParams); - }, + `initialize` - An optional initialize function. Typically this will be used + to set up state on the instanceMeta object. - /** - 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]; - } + `removedItem` - A function that is called each time an element is removed + from the array. - var map = {}, qps = []; - this._qpCache[leafRouteName] = { - map: map, - qps: qps - }; + `addedItem` - A function that is called each time an element is added to + the array. - var routerjs = this.router; - var recogHandlerInfos = routerjs.recognizer.handlersFor(leafRouteName); - 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'); + The `initialize` function has the following signature: - if (!qpMeta) { continue; } + ```javascript + function(initialValue, changeMeta, instanceMeta) + ``` - merge(map, qpMeta.map); - qps.push.apply(qps, qpMeta.qps); - } + `initialValue` - The value of the `initialValue` property from the + options object. - return { - qps: qps, - map: map - }; - }, + `changeMeta` - An object which contains meta information about the + computed. It contains the following properties: - /* - becomeResolved: function(payload, resolvedContext) { - var params = this.serialize(resolvedContext); + - `property` the computed property + - `propertyName` the name of the property on the object - if (payload) { - this.stashResolvedModel(payload, resolvedContext); - payload.params = payload.params || {}; - payload.params[this.name] = params; - } + `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. - return this.factory('resolved', { - context: resolvedContext, - name: this.name, - handler: this.handler, - params: params - }); - }, - */ - _hydrateUnsuppliedQueryParams: function(leafRouteName, contexts, queryParams) { - var state = calculatePostTransitionState(this, leafRouteName, contexts); - var handlerInfos = state.handlerInfos; - var appCache = this._bucketCache; + The `removedItem` and `addedItem` functions both have the following signature: - stashParamNames(this, handlerInfos); + ```javascript + function(accumulatedValue, item, changeMeta, instanceMeta) + ``` - for (var i = 0, len = handlerInfos.length; i < len; ++i) { - var route = handlerInfos[i].handler; - var qpMeta = get(route, '_qp'); + `accumulatedValue` - The value returned from the last time + `removedItem` or `addedItem` was called or `initialValue`. - 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; + `item` - the element added or removed from the array - if (presentProp) { - if (presentProp !== qp.fprop) { - queryParams[qp.fprop] = queryParams[presentProp]; - delete queryParams[presentProp]; - } - } else { - var controllerProto = qp.cProto; - var cacheMeta = get(controllerProto, '_cacheMeta'); + `changeMeta` - An object which contains meta information about the + change. It contains the following properties: - var cacheKey = controllerProto._calculateCacheKey(qp.ctrl, cacheMeta[qp.prop].parts, state.params); - queryParams[qp.fprop] = appCache.lookup(cacheKey, qp.prop, qp.def); - } - } - } - }, + - `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. - _scheduleLoadingEvent: function(transition, originRoute) { - this._cancelLoadingEvent(); - this._loadingStateTimer = run.scheduleOnce('routerTransitions', this, '_fireLoadingEvent', transition, originRoute); - }, + For property changes triggered on an item property change (when + depKey is something like `someArray.@each.someProperty`), + `changeMeta` will also contain the following property: - _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; - } + - `previousValues` an object whose keys are the properties that changed on + the item, and whose values are the item's previous values. - transition.trigger(true, 'loading', transition, originRoute); - }, + `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). - _cancelLoadingEvent: function () { - if (this._loadingStateTimer) { - run.cancel(this._loadingStateTimer); - } - this._loadingStateTimer = null; - } - }); + `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. - /* - Helper function for iterating root-ward, starting - from (but not including) the provided `originRoute`. + 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. - Returns true if the last callback fired requested - to bubble upward. + Example - @private - */ - function forEachRouteAbove(originRoute, transition, callback) { - var handlerInfos = transition.state.handlerInfos; - var originRouteFound = false; - var handlerInfo, route; + ```javascript + Ember.computed.max = function(dependentKey) { + return Ember.reduceComputed(dependentKey, { + initialValue: -Infinity, - for (var i = handlerInfos.length - 1; i >= 0; --i) { - handlerInfo = handlerInfos[i]; - route = handlerInfo.handler; + addedItem: function(accumulatedValue, item, changeMeta, instanceMeta) { + return Math.max(accumulatedValue, item); + }, - if (!originRouteFound) { - if (originRoute === route) { - originRouteFound = true; + removedItem: function(accumulatedValue, item, changeMeta, instanceMeta) { + if (item < accumulatedValue) { + return accumulatedValue; + } } - continue; - } + }); + }; + ``` - if (callback(route, handlerInfos[i + 1].handler) !== true) { - return false; - } - } - return true; - } + 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. - // These get invoked when an action bubbles above ApplicationRoute - // and are not meant to be overridable. - var defaultActionHandlers = { + Example - willResolveModel: function(transition, originRoute) { - originRoute.router._scheduleLoadingEvent(transition, originRoute); - }, + ```javascript + App.PeopleController = Ember.ArrayController.extend({ + itemController: 'person', - error: function(error, transition, originRoute) { - // Attempt to find an appropriate error substate to enter. - var router = originRoute.router; + 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'); - var tryTopLevel = forEachRouteAbove(originRoute, transition, function(route, childRoute) { - var childErrorRouteName = findChildRouteName(route, childRoute, 'error'); - if (childErrorRouteName) { - router.intermediateTransitionTo(childErrorRouteName, error); - return; - } - return true; - }); + return Ember.compare(reversedNameA, reversedNameB); + }) + }); - if (tryTopLevel) { - // Check for top-level error state to enter. - if (routeHasBeenDefined(originRoute.router, 'application_error')) { - router.intermediateTransitionTo('application_error', error); - return; - } - } + App.PersonController = Ember.ObjectController.extend({ + reversedName: function() { + return reverse(get(this, 'name')); + }.property('name') + }); + ``` - logError(error, 'Error while processing route: ' + transition.targetName); - }, + 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. - loading: function(transition, originRoute) { - // Attempt to find an appropriate loading substate to enter. - var router = originRoute.router; + 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. - var tryTopLevel = forEachRouteAbove(originRoute, transition, function(route, childRoute) { - var childLoadingRouteName = findChildRouteName(route, childRoute, 'loading'); + Example - if (childLoadingRouteName) { - router.intermediateTransitionTo(childLoadingRouteName); - return; - } + ```javascript + Ember.Object.extend({ + // When `string` is changed, `computed` is completely recomputed. + string: 'a string', - // Don't bubble above pivot route. - if (transition.pivotHandler !== route) { - return true; - } - }); + // When an item is added to `array`, `addedItem` is called. + array: [], - if (tryTopLevel) { - // Check for top-level loading state to enter. - if (routeHasBeenDefined(originRoute.router, 'application_loading')) { - router.intermediateTransitionTo('application_loading'); - return; - } - } - } - }; + // When an item is added to `anotherArray`, `computed` is completely + // recomputed. + anotherArray: [], - function logError(error, initialMessage) { - var errorArgs = []; + computed: Ember.reduceComputed('string', 'array', 'anotherArray.[]', { + addedItem: addedItemCallback, + removedItem: removedItemCallback + }) + }); + ``` - if (initialMessage) { errorArgs.push(initialMessage); } + @method reduceComputed + @for Ember + @param {String} [dependentKeys*] + @param {Object} options + @return {Ember.ComputedProperty} + */ + function reduceComputed(options) { + var args; - if (error) { - if (error.message) { errorArgs.push(error.message); } - if (error.stack) { errorArgs.push(error.stack); } + if (arguments.length > 1) { + args = a_slice.call(arguments, 0, -1); + options = a_slice.call(arguments, -1)[0]; + } - if (typeof error === "string") { errorArgs.push(error); } + if (typeof options !== 'object') { + throw new EmberError('Reduce Computed Property declared without an options hash'); } - Ember.Logger.error.apply(this, errorArgs); - } + if (!('initialValue' in options)) { + throw new EmberError('Reduce Computed Property declared without an initial value'); + } - function findChildRouteName(parentRoute, originatingChildRoute, name) { - var router = parentRoute.router; - var childName; - var targetChildRouteName = originatingChildRoute.routeName.split('.').pop(); - var namespace = parentRoute.routeName === 'application' ? '' : parentRoute.routeName + '.'; + var cp = new ReduceComputedProperty(options); - - // Second, try general loading state, e.g. 'loading' - childName = namespace + name; - if (routeHasBeenDefined(router, childName)) { - return childName; + if (args) { + cp.property.apply(cp, args); } - } - function routeHasBeenDefined(router, name) { - var container = router.container; - return router.hasRoute(name) && - (container.has('template:' + name) || container.has('route:' + name)); + return cp; } - function triggerEvent(handlerInfos, ignoreFailure, args) { - var name = args.shift(); + __exports__.reduceComputed = reduceComputed; + }); +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 + @submodule ember-runtime + */ - if (!handlerInfos) { - if (ignoreFailure) { return; } - throw new EmberError("Can't trigger action '" + name + "' because your app hasn't finished transitioning into its first route. To trigger an action on destination routes during a transition, you can call `.send()` on the `Transition` object passed to the `model/beforeModel/afterModel` hooks."); - } + var Ember = __dependency1__["default"]; + // Ember.assert + var get = __dependency2__.get; + 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 eventWasHandled = false; - var handlerInfo, handler; + var a_slice = [].slice; - for (var i = handlerInfos.length - 1; i >= 0; i--) { - handlerInfo = handlerInfos[i]; - handler = handlerInfo.handler; + /** + A computed property that returns the sum of the value + in the dependent array. - if (handler._actions && handler._actions[name]) { - if (handler._actions[name].apply(handler, args) === true) { - eventWasHandled = true; - } else { - return; - } - } - } + @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 + */ - if (defaultActionHandlers[name]) { - defaultActionHandlers[name].apply(null, args); - return; - } + function sum(dependentKey){ + return reduceComputed(dependentKey, { + initialValue: 0, - 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."); - } + addedItem: function(accumulatedValue, item, changeMeta, instanceMeta){ + return accumulatedValue + item; + }, + + removedItem: function(accumulatedValue, item, changeMeta, instanceMeta){ + return accumulatedValue - item; + } + }); } - function calculatePostTransitionState(emberRouter, leafRouteName, contexts) { - var routerjs = emberRouter.router; - var state = routerjs.applyIntent(leafRouteName, contexts); - var handlerInfos = state.handlerInfos; - var params = state.params; + __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. - for (var i = 0, len = handlerInfos.length; i < len; ++i) { - var handlerInfo = handlerInfos[i]; - if (!handlerInfo.isResolved) { - handlerInfo = handlerInfo.becomeResolved(null, handlerInfo.context); + ```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; + } } - params[handlerInfo.name] = handlerInfo.params; - } - return state; + }); } - function updatePaths(router) { - var appController = router.container.lookup('controller:application'); + __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. - 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; - } + ```javascript + var Person = Ember.Object.extend({ + childAges: Ember.computed.mapBy('children', 'age'), + minChildAge: Ember.computed.min('childAges') + }); - var infos = router.router.currentHandlerInfos; - var path = EmberRouter._routePath(infos); + var lordByron = Person.create({ children: [] }); - if (!('currentPath' in appController)) { - defineProperty(appController, 'currentPath'); - } + 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 + ``` - set(appController, 'currentPath', path); + @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, - if (!('currentRouteName' in appController)) { - defineProperty(appController, 'currentRouteName'); - } + addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { + return Math.min(accumulatedValue, item); + }, - set(appController, 'currentRouteName', infos[infos.length - 1].name); + removedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { + if (item > accumulatedValue) { + return accumulatedValue; + } + } + }); } - EmberRouter.reopenClass({ - router: null, + __exports__.min = min;/** + Returns an array mapped via the callback - /** - 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`. + 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. - ```javascript - App.Router.map(function({ - this.route('about'); - this.resource('article'); - })); - ``` + ```javascript + function(item, index); + ``` - For more detailed examples please see - [the guides](http://emberjs.com/guides/routing/defining-your-routes/). + Example - @method map - @param callback - */ - map: function(callback) { - var router = this.router; - if (!router) { - router = new Router(); + ```javascript + var Hamster = Ember.Object.extend({ + excitingChores: Ember.computed.map('chores', function(chore, index) { + return chore.toUpperCase() + '!'; + }) + }); - - router._triggerWillChangeContext = Ember.K; - router._triggerWillLeave = Ember.K; - + var hamster = Hamster.create({ + chores: ['clean', 'write more unit tests'] + }); - router.callbacks = []; - router.triggerEvent = triggerEvent; - this.reopenClass({ router: router }); + hamster.get('excitingChores'); // ['CLEAN!', 'WRITE MORE UNIT TESTS!'] + ``` + + @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; } + }; - 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); - }); - }); + return arrayComputed(dependentKey, options); + } - router.callbacks.push(callback); - router.map(dsl.generate()); - return router; - }, + __exports__.map = map;/** + Returns an array mapped to the specified key. - _routePath: function(handlerInfos) { - var path = []; + ```javascript + var Person = Ember.Object.extend({ + childAges: Ember.computed.mapBy('children', 'age') + }); - // 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' + var lordByron = Person.create({ children: [] }); - function intersectionMatches(a1, a2) { - for (var i = 0, len = a1.length; i < len; ++i) { - if (a1[i] !== a2[i]) { - return false; - } - } - return true; - } + 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] + ``` - var name, nameParts, oldNameParts; - for (var i=1, l=handlerInfos.length; i -1) { + array.removeAt(filterIndex); + } - __exports__.routeArgs = routeArgs;function getActiveTargetName(router) { - var handlerInfos = router.activeTransition ? - router.activeTransition.state.handlerInfos : - router.state.handlerInfos; - return handlerInfos[handlerInfos.length - 1].name; + return array; + } + }; + + return arrayComputed(dependentKey, options); } - __exports__.getActiveTargetName = getActiveTargetName;function stashParamNames(router, handlerInfos) { - if (handlerInfos._namesStashed) { return; } + __exports__.filter = filter;/** + Filters the array by the property and value - // 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; + ```javascript + var Hamster = Ember.Object.extend({ + remainingChores: Ember.computed.filterBy('chores', 'done', false) + }); - for (var i = 0, len = handlerInfos.length; i < len; ++i) { - var handlerInfo = handlerInfos[i]; - var names = recogHandlers[i].names; + var hamster = Hamster.create({ + chores: [ + { name: 'cook', done: true }, + { name: 'clean', done: true }, + { name: 'write more unit tests', done: false } + ] + }); - if (names.length) { - dynamicParent = handlerInfo; - } + hamster.get('remainingChores'); // [{ name: 'write more unit tests', done: false }] + ``` - handlerInfo._names = names; + @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 route = handlerInfo.handler; - route._stashNames(handlerInfo, dynamicParent); + if (arguments.length === 2) { + callback = function(item) { + return get(item, propertyKey); + }; + } else { + callback = function(item) { + return get(item, propertyKey) === value; + }; } - handlerInfos._namesStashed = true; + return filter(dependentKey + '.@each.' + propertyKey, callback); } - __exports__.stashParamNames = stashParamNames; - }); -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"; + __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; /** - Ember Runtime + A computed property which returns a new array with all the unique + elements from one or more dependent arrays. - @module ember - @submodule ember-runtime - @requires ember-metal - */ + Example - // BEGIN IMPORTS - var Ember = __dependency1__["default"]; - var isEqual = __dependency2__.isEqual; - var compare = __dependency3__["default"]; - var copy = __dependency4__["default"]; - var inject = __dependency5__["default"]; + ```javascript + var Hamster = Ember.Object.extend({ + uniqueFruits: Ember.computed.uniq('fruits') + }); - 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 hamster = Hamster.create({ + fruits: [ + 'banana', + 'grape', + 'kale', + 'banana' + ] + }); - var NativeArray = __dependency15__["default"]; - var Set = __dependency16__["default"]; - var EmberStringUtils = __dependency17__["default"]; - var Deferred = __dependency18__["default"]; - var onLoad = __dependency19__.onLoad; - var runLoadHooks = __dependency19__.runLoadHooks; + hamster.get('uniqueFruits'); // ['banana', 'grape', 'kale'] + ``` - 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"]; + @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); - 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; + args.push({ + initialize: function(array, changeMeta, instanceMeta) { + instanceMeta.itemCounts = {}; + }, - var reduceComputed = __dependency36__.reduceComputed; - var ReduceComputedProperty = __dependency36__.ReduceComputedProperty; + addedItem: function(array, item, changeMeta, instanceMeta) { + var guid = guidFor(item); - 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; + if (!instanceMeta.itemCounts[guid]) { + instanceMeta.itemCounts[guid] = 1; + array.pushObject(item); + } else { + ++instanceMeta.itemCounts[guid]; + } + return array; + }, - var ArrayController = __dependency38__["default"]; - var ObjectController = __dependency39__["default"]; - var Controller = __dependency40__["default"]; - var ControllerMixin = __dependency41__["default"]; + removedItem: function(array, item, _, instanceMeta) { + var guid = guidFor(item); + var itemCounts = instanceMeta.itemCounts; - var Service = __dependency42__["default"]; + if (--itemCounts[guid] === 0) { + array.removeObject(item); + } - 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 + return array; + } + }); - // BEGIN EXPORTS - Ember.compare = compare; - Ember.copy = copy; - Ember.isEqual = isEqual; + return arrayComputed.apply(null, args); + } - - Ember.Array = EmberArray; + __exports__.uniq = uniq;/** + Alias for [Ember.computed.uniq](/api/#method_computed_uniq). - Ember.Comparable = Comparable; - Ember.Copyable = Copyable; + @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 computed property which returns a new array with all the duplicated + elements from two or more dependent arrays. - Ember.SortableMixin = SortableMixin; + Example - Ember.Freezable = Freezable; - Ember.FROZEN_ERROR = FROZEN_ERROR; + ```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') + }); - Ember.DeferredMixin = DeferredMixin; + obj.get('friendsInCommon'); // ['William King', 'Mary Somerville'] + ``` - Ember.MutableEnumerable = MutableEnumerable; - Ember.MutableArray = MutableArray; + @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 args = a_slice.call(arguments); - Ember.TargetActionSupport = TargetActionSupport; - Ember.Evented = Evented; + args.push({ + initialize: function (array, changeMeta, instanceMeta) { + instanceMeta.itemCounts = {}; + }, - Ember.PromiseProxyMixin = PromiseProxyMixin; + 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; - Ember.Observable = Observable; + if (!itemCounts[itemGuid]) { + itemCounts[itemGuid] = {}; + } - Ember.arrayComputed = arrayComputed; - Ember.ArrayComputedProperty = ArrayComputedProperty; - Ember.reduceComputed = reduceComputed; - Ember.ReduceComputedProperty = ReduceComputedProperty; + if (itemCounts[itemGuid][dependentGuid] === undefined) { + itemCounts[itemGuid][dependentGuid] = 0; + } - // ES6TODO: this seems a less than ideal way/place to add properties to Ember.computed - var EmComputed = Ember.computed; + if (++itemCounts[itemGuid][dependentGuid] === 1 && + numberOfDependentArrays === keys(itemCounts[itemGuid]).length) { + array.addObject(item); + } - 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; + return array; + }, - 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; + removedItem: function(array, item, changeMeta, instanceMeta) { + var itemGuid = guidFor(item); + var dependentGuid = guidFor(changeMeta.arrayChanged); + var numberOfArraysItemAppearsIn; + var itemCounts = instanceMeta.itemCounts; - Ember.ArrayController = ArrayController; - Ember.ObjectController = ObjectController; - Ember.Controller = Controller; - Ember.ControllerMixin = ControllerMixin; + if (itemCounts[itemGuid][dependentGuid] === undefined) { + itemCounts[itemGuid][dependentGuid] = 0; + } - - Ember._ProxyMixin = _ProxyMixin; + if (--itemCounts[itemGuid][dependentGuid] === 0) { + delete itemCounts[itemGuid][dependentGuid]; + numberOfArraysItemAppearsIn = keys(itemCounts[itemGuid]).length; - Ember.RSVP = RSVP; - // END EXPORTS + if (numberOfArraysItemAppearsIn === 0) { + delete itemCounts[itemGuid]; + } - __exports__["default"] = Ember; - }); -enifed("ember-runtime/compare", - ["ember-metal/utils","ember-runtime/mixins/comparable","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var typeOf = __dependency1__.typeOf; - var Comparable = __dependency2__["default"]; + array.removeObject(item); + } - 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 - }; + return array; + } + }); - // - // the spaceship operator - // - function spaceship(a, b) { - var diff = a - b; - return (diff > 0) - (diff < 0); + return arrayComputed.apply(null, args); } - /** - 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. + __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. - 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. + Example ```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 Hamster = Ember.Object.extend({ + likes: ['banana', 'grape', 'kale'], + wants: Ember.computed.setDiff('likes', 'fruits') + }); - var type1 = typeOf(v); - var type2 = typeOf(w); + var hamster = Hamster.create({ + fruits: [ + 'grape', + 'kale', + ] + }); - if (Comparable) { - if (type1 ==='instance' && Comparable.detect(v.constructor)) { - return v.constructor.compare(v, w); - } + hamster.get('wants'); // ['banana'] + ``` - if (type2 === 'instance' && Comparable.detect(w.constructor)) { - return 1 - w.constructor.compare(w, v); - } + @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.'); } - var res = spaceship(TYPE_ORDER[type1], TYPE_ORDER[type2]); - if (res !== 0) { - return res; - } + return arrayComputed(setAProperty, setBProperty, { + addedItem: function (array, item, changeMeta, instanceMeta) { + var setA = get(this, setAProperty); + var setB = get(this, setBProperty); - // types are equal - so we have to check values now - switch (type1) { - case 'boolean': - case 'number': - return spaceship(v,w); + if (changeMeta.arrayChanged === setA) { + if (!setB.contains(item)) { + array.addObject(item); + } + } else { + array.removeObject(item); + } - case 'string': - return spaceship(v.localeCompare(w), 0); + return array; + }, - case 'array': - var vLen = v.length; - var wLen = w.length; - var len = Math.min(vLen, wLen); + removedItem: function (array, item, changeMeta, instanceMeta) { + var setA = get(this, setAProperty); + var setB = get(this, setBProperty); - for (var i = 0; i < len; i++) { - var r = compare(v[i], w[i]); - if (r !== 0) { - return r; + if (changeMeta.arrayChanged === setB) { + if (setA.contains(item)) { + array.addObject(item); } + } else { + array.removeObject(item); } - // all elements are equal now - // shorter array should be ordered first - return spaceship(vLen, wLen); + return array; + } + }); + } - case 'instance': - if (Comparable && Comparable.detect(v)) { - return v.compare(v, w); - } - return 0; + __exports__.setDiff = setDiff;function binarySearch(array, item, low, high) { + var mid, midItem, res, guidMid, guidItem; - case 'date': - return spaceship(v.getTime(), w.getTime()); + if (arguments.length < 4) { + high = get(array, 'length'); + } - default: - return 0; + if (arguments.length < 3) { + low = 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"]; - var a_slice = [].slice; + if (low === high) { + return low; + } - function ArrayComputedProperty() { - var cp = this; + mid = low + Math.floor((high - low) / 2); + midItem = array.objectAt(mid); - ReduceComputedProperty.apply(this, arguments); + guidMid = guidFor(midItem); + guidItem = guidFor(item); - 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); - } + if (guidMid === guidItem) { + return mid; + } - return reduceFunc.apply(this, arguments); - }; - })(this.func); + res = this.order(midItem, item); - return this; - } + if (res === 0) { + res = guidMid < guidItem ? -1 : 1; + } - ArrayComputedProperty.prototype = o_create(ReduceComputedProperty.prototype); - ArrayComputedProperty.prototype.initialValue = function () { - return Ember.A(); - }; + if (res < 0) { + return this.binarySearch(array, item, mid+1, high); + } else if (res > 0) { + return this.binarySearch(array, item, low, mid); + } - ArrayComputedProperty.prototype.resetValue = function (array) { - array.clear(); - return array; - }; + return mid; + } - // 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`. + 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. - 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. + The callback method you provide should have the following signature: - `initialize` - An optional initialize function. Typically this will be used - to set up state on the instanceMeta object. + ```javascript + function(itemA, itemB); + ``` - `removedItem` - A function that is called each time an element is - removed from the array. + - `itemA` the first item to compare. + - `itemB` the second item to compare. - `addedItem` - A function that is called each time an element is - added to the array. + 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`. + 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`. - The `initialize` function has the following signature: + Example ```javascript - function(array, changeMeta, instanceMeta) - ``` + var ToDoList = Ember.Object.extend({ + // using standard ascending sort + todosSorting: ['name'], + sortedTodos: Ember.computed.sort('todos', 'todosSorting'), - `array` - The initial value of the arrayComputed, an empty array. + // using descending sort + todosSortingDesc: ['name:desc'], + sortedTodosDesc: Ember.computed.sort('todos', 'todosSortingDesc'), - `changeMeta` - An object which contains meta information about the - computed. It contains the following properties: + // 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; + } - - `property` the computed property - - `propertyName` the name of the property on the object + return 0; + }) + }); - `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 todoList = ToDoList.create({todos: [ + { name: 'Unit Test', priority: 2 }, + { name: 'Documentation', priority: 3 }, + { name: 'Release', priority: 1 } + ]}); + 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 }] + ``` - The `removedItem` and `addedItem` functions both have the following signature: + @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); - ```javascript - function(accumulatedValue, item, changeMeta, instanceMeta) - ``` + if (typeof sortDefinition === 'function') { + return customSort(itemsKey, sortDefinition); + } else { + return propertySort(itemsKey, sortDefinition); + } + } - `accumulatedValue` - The value returned from the last time - `removedItem` or `addedItem` was called or an empty array. + __exports__.sort = sort;function customSort(itemsKey, comparator) { + return arrayComputed(itemsKey, { + initialize: function (array, changeMeta, instanceMeta) { + instanceMeta.order = comparator; + instanceMeta.binarySearch = binarySearch; + instanceMeta.waitingInsertions = []; + instanceMeta.insertWaiting = function() { + var index, item; + var waiting = instanceMeta.waitingInsertions; + instanceMeta.waitingInsertions = []; + for (var i=0; i 1) { - args = a_slice.call(arguments, 0, -1); - options = a_slice.call(arguments, -1)[0]; - } + instanceMeta.order = function (itemA, itemB) { + var sortProperty, result, asc; + var keyA = this.keyFor(itemA); + var keyB = this.keyFor(itemB); - if (typeof options !== 'object') { - throw new EmberError('Array Computed Property declared without an options hash'); - } + for (var i = 0; i < this.sortProperties.length; ++i) { + sortProperty = this.sortProperties[i]; - var cp = new ArrayComputedProperty(options); + result = compare(keyA[sortProperty], keyB[sortProperty]); - if (args) { - cp.property.apply(cp, args); - } + if (result !== 0) { + asc = this.sortPropertyAscending[sortProperty]; + return asc ? result : (-1 * result); + } + } - return cp; + return 0; + }; + + instanceMeta.binarySearch = binarySearch; + setupKeyCache(instanceMeta); + }, + + addedItem: function (array, item, changeMeta, instanceMeta) { + var index = instanceMeta.binarySearch(array, item); + array.insertAt(index, item); + return array; + }, + + removedItem: function (array, item, changeMeta, instanceMeta) { + var index = instanceMeta.binarySearch(array, item); + array.removeAt(index); + instanceMeta.dropKeyFor(item); + return array; + } + }); } - __exports__.arrayComputed = arrayComputed; - __exports__.ArrayComputedProperty = ArrayComputedProperty; + 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; + }; + + instanceMeta.dropKeyFor = function(item) { + var guid = guidFor(item); + this.keyCache[guid] = null; + }; + + instanceMeta.keyCache = {}; + } }); -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__) { +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"; - 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; + /** + @module ember + @submodule ember-runtime + */ - var cacheSet = cacheFor.set; - var cacheGet = cacheFor.get; - var cacheRemove = cacheFor.remove; - var a_slice = [].slice; - // 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. - var eachPropertyPattern = /^(.*)\.@each\.(.*)/; - var doubleEachPropertyPattern = /(.*\.@each){2,}/; - var arrayBracketPattern = /\.\[\]$/; + var Ember = __dependency1__["default"]; + var get = __dependency2__.get; + 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 EmberError = __dependency8__["default"]; - function get(obj, key) { - if (key === '@this') { - return obj; - } - return e_get(obj, key); - } + /** + `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. - /* - Tracks changes to dependent arrays, as well as to properties of items in - dependent arrays. + 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. - @class DependentArraysObserver - */ - function DependentArraysObserver(callbacks, cp, instanceMeta, context, propertyName, sugarMeta) { - // user specified callbacks for `addedItem` and `removedItem` - this.callbacks = callbacks; + 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: - // the computed property: remember these are shared across instances - this.cp = cp; + ```javascript + MyApp.listController = Ember.ArrayController.create(); - // the ReduceComputedPropertyInstanceMeta this DependentArraysObserver is - // associated with - this.instanceMeta = instanceMeta; + $.get('people.json', function(data) { + MyApp.listController.set('model', data); + }); + ``` - // 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 = {}; + Then, create a view that binds to your new controller: - // a map of dependent array guids -> TrackedArray instances. We use - // this to lazily recompute indexes for item property observers. - this.trackedArraysByGuid = {}; + ```handlebars + {{#each person in MyApp.listController}} + {{person.firstName}} {{person.lastName}} + {{/each}} + ``` - // 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; + 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. - // 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; - } + 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. - function ItemPropertyObserverContext (dependentArray, index, trackedArray) { - Ember.assert('Internal error: trackedArray is null or undefined', trackedArray); + For example: - this.dependentArray = dependentArray; - this.index = index; - this.item = dependentArray.objectAt(index); - this.trackedArray = trackedArray; - this.beforeObserver = null; - this.observer = null; - this.destroyed = false; - } + ```handlebars + {{#each post in controller}} +
  • {{post.title}} ({{post.titleLength}} characters)
  • + {{/each}} + ``` - DependentArraysObserver.prototype = { - setValue: function (newValue) { - this.instanceMeta.setValue(newValue, true); - }, + ```javascript + App.PostsController = Ember.ArrayController.extend({ + itemController: 'post' + }); - getValue: function () { - return this.instanceMeta.getValue(); - }, + App.PostController = Ember.ObjectController.extend({ + // the `title` property will be proxied to the underlying post. + titleLength: function() { + return this.get('title').length; + }.property('title') + }); + ``` - setupObservers: function (dependentArray, dependentKey) { - this.dependentKeysByGuid[guidFor(dependentArray)] = dependentKey; + In some cases it is helpful to return a different `itemController` depending + on the particular item. Subclasses can do this by overriding + `lookupItemController`. - dependentArray.addArrayObserver(this, { - willChange: 'dependentArrayWillChange', - didChange: 'dependentArrayDidChange' - }); + For example: - if (this.cp._itemPropertyKeys[dependentKey]) { - this.setupPropertyObservers(dependentKey, this.cp._itemPropertyKeys[dependentKey]); + ```javascript + App.MyArrayController = Ember.ArrayController.extend({ + lookupItemController: function( object ) { + if (object.get('isSpecial')) { + return "special"; // use App.SpecialController + } else { + return "regular"; // use App.RegularController + } } - }, + }); + ``` - teardownObservers: function (dependentArray, dependentKey) { - var itemPropertyKeys = this.cp._itemPropertyKeys[dependentKey] || []; + The itemController instances will have a `parentController` property set to + the `ArrayController` instance. - delete this.dependentKeysByGuid[guidFor(dependentArray)]; + @class ArrayController + @namespace Ember + @extends Ember.ArrayProxy + @uses Ember.SortableMixin + @uses Ember.ControllerMixin + */ - this.teardownPropertyObservers(dependentKey, itemPropertyKeys); + __exports__["default"] = ArrayProxy.extend(ControllerMixin, SortableMixin, { - dependentArray.removeArrayObserver(this, { - willChange: 'dependentArrayWillChange', - didChange: 'dependentArrayDidChange' + /** + A string containing the controller name used to wrap items. + + For example: + + ```javascript + App.MyArrayController = Ember.ArrayController.extend({ + itemController: 'myItem' // use App.MyItemController }); - }, + ``` - suspendArrayObservers: function (callback, binding) { - var oldSuspended = this.suspended; - this.suspended = true; - callback.call(binding); - this.suspended = oldSuspended; - }, + @property itemController + @type String + @default null + */ + itemController: null, - setupPropertyObservers: function (dependentKey, itemPropertyKeys) { - var dependentArray = get(this.instanceMeta.context, dependentKey); - var length = get(dependentArray, 'length'); - var observerContexts = new Array(length); + /** + 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. - this.resetTransformations(dependentKey, observerContexts); + For example: - forEach(dependentArray, function (item, index) { - var observerContext = this.createPropertyObserverContext(dependentArray, index, this.trackedArraysByGuid[dependentKey]); - observerContexts[index] = observerContext; + ```javascript + App.MyArrayController = Ember.ArrayController.extend({ + lookupItemController: function( object ) { + if (object.get('isSpecial')) { + return "special"; // use App.SpecialController + } else { + return "regular"; // use App.RegularController + } + } + }); + ``` - forEach(itemPropertyKeys, function (propertyKey) { - addBeforeObserver(item, propertyKey, this, observerContext.beforeObserver); - addObserver(item, propertyKey, this, observerContext.observer); - }, this); - }, this); + @method lookupItemController + @param {Object} object + @return {String} + */ + lookupItemController: function(object) { + return get(this, 'itemController'); }, - teardownPropertyObservers: function (dependentKey, itemPropertyKeys) { - var dependentArrayObserver = this; - var trackedArray = this.trackedArraysByGuid[dependentKey]; - var beforeObserver, observer, item; + objectAtContent: function(idx) { + var length = get(this, 'length'); + var arrangedContent = get(this, 'arrangedContent'); + var object = arrangedContent && arrangedContent.objectAt(idx); + var controllerClass; - if (!trackedArray) { return; } + if (idx >= 0 && idx < length) { + controllerClass = this.lookupItemController(object); - trackedArray.apply(function (observerContexts, offset, operation) { - if (operation === TrackedArray.DELETE) { return; } + if (controllerClass) { + return this.controllerAt(idx, object, controllerClass); + } + } - forEach(observerContexts, function (observerContext) { - observerContext.destroyed = true; - beforeObserver = observerContext.beforeObserver; - observer = observerContext.observer; - item = observerContext.item; + // When `controllerClass` is falsy, we have not opted in to using item + // controllers, so return the object directly. - forEach(itemPropertyKeys, function (propertyKey) { - removeBeforeObserver(item, propertyKey, dependentArrayObserver, beforeObserver); - removeObserver(item, propertyKey, dependentArrayObserver, observer); - }); - }); - }); + // 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; }, - createPropertyObserverContext: function (dependentArray, index, trackedArray) { - var observerContext = new ItemPropertyObserverContext(dependentArray, index, trackedArray); + arrangedContentDidChange: function() { + this._super(); + this._resetSubControllers(); + }, - this.createPropertyObserver(observerContext); + arrayContentDidChange: function(idx, removedCnt, addedCnt) { + var subControllers = this._subControllers; - return observerContext; - }, + if (subControllers.length) { + var subControllersToRemove = subControllers.slice(idx, idx + removedCnt); - createPropertyObserver: function (observerContext) { - var dependentArrayObserver = this; + forEach(subControllersToRemove, function(subController) { + if (subController) { + subController.destroy(); + } + }); - observerContext.beforeObserver = function (obj, keyName) { - return dependentArrayObserver.itemPropertyWillChange(obj, keyName, observerContext.dependentArray, observerContext); - }; + replace(subControllers, idx, removedCnt, new Array(addedCnt)); + } - observerContext.observer = function (obj, keyName) { - return dependentArrayObserver.itemPropertyDidChange(obj, keyName, observerContext.dependentArray, observerContext); - }; + // 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); }, - resetTransformations: function (dependentKey, observerContexts) { - this.trackedArraysByGuid[dependentKey] = new TrackedArray(observerContexts); + init: function() { + this._super(); + this._subControllers = []; }, - trackAdd: function (dependentKey, index, newItems) { - var trackedArray = this.trackedArraysByGuid[dependentKey]; + model: computed(function () { + return Ember.A(); + }), - if (trackedArray) { - trackedArray.addItems(index, newItems); - } - }, + /** + * Flag to mark as being "virtual". Used to keep this instance + * from participating in the parentController hierarchy. + * + * @private + * @property _isVirtual + * @type Boolean + */ + _isVirtual: false, - trackRemove: function (dependentKey, index, removedCount) { - var trackedArray = this.trackedArraysByGuid[dependentKey]; + controllerAt: function(idx, object, controllerClass) { + var container = get(this, 'container'); + var subControllers = this._subControllers; + var fullName, subController, subControllerFactory, parentController, options; - if (trackedArray) { - return trackedArray.removeItems(index, removedCount); + if (subControllers.length > idx) { + subController = subControllers[idx]; + + if (subController) { + return subController; + } } - return []; - }, + if (this._isVirtual) { + parentController = get(this, 'parentController'); + } else { + parentController = this; + } - 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; + + fullName = 'controller:' + controllerClass; + + if (!container.has(fullName)) { + throw new EmberError('Could not resolve itemController: "' + controllerClass + '"'); } - forEach(observerContexts, function (context, index) { - context.index = index + offset; + subController = container.lookupFactory(fullName).create({ + target: parentController, + parentController: parentController, + model: object }); - }); - }, - - dependentArrayWillChange: function (dependentArray, index, removedCount, addedCount) { - if (this.suspended) { return; } + - 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; + subControllers[idx] = subController; - observerContexts = this.trackRemove(dependentKey, normalizedIndex, normalizedRemoveCount); + return subController; + }, - function removeObservers(propertyKey) { - observerContexts[sliceIndex].destroyed = true; - removeBeforeObserver(item, propertyKey, this, observerContexts[sliceIndex].beforeObserver); - removeObserver(item, propertyKey, this, observerContexts[sliceIndex].observer); - } + _subControllers: null, - for (sliceIndex = normalizedRemoveCount - 1; sliceIndex >= 0; --sliceIndex) { - itemIndex = normalizedIndex + sliceIndex; - if (itemIndex >= length) { break; } + _resetSubControllers: function() { + var controller; + var subControllers = this._subControllers; - item = dependentArray.objectAt(itemIndex); + if (subControllers.length) { + for (var i = 0, length = subControllers.length; length > i; i++) { + controller = subControllers[i]; - forEach(itemPropertyKeys, removeObservers, this); + if (controller) { + controller.destroy(); + } + } - 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)); + subControllers.length = 0; } - this.callbacks.flushedChanges.call(this.instanceMeta.context, this.getValue(), this.instanceMeta.sugarMeta); }, - dependentArrayDidChange: function (dependentArray, index, removedCount, addedCount) { - if (this.suspended) { return; } + 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; - 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; + /** + @module ember + @submodule ember-runtime + */ - forEach(dependentArray.slice(normalizedIndex, endIndex), function (item, sliceIndex) { - if (itemPropertyKeys) { - observerContext = this.createPropertyObserverContext(dependentArray, normalizedIndex + sliceIndex, - this.trackedArraysByGuid[dependentKey]); - observerContexts[sliceIndex] = observerContext; + /** + @class Controller + @namespace Ember + @extends Ember.Object + @uses Ember.ControllerMixin + */ + var Controller = EmberObject.extend(Mixin); - forEach(itemPropertyKeys, function (propertyKey) { - addBeforeObserver(item, propertyKey, this, observerContext.beforeObserver); - addObserver(item, propertyKey, this, observerContext.observer); - }, this); - } + function controllerInjectionHelper(factory) { + Ember.assert("Defining an injected controller property on a " + + "non-controller is not allowed.", Controller.detect(factory)); + } - 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); - }, + + /** + Creates a property that lazily looks up another controller in the container. + Can only be used when defining another controller. - itemPropertyWillChange: function (obj, keyName, array, observerContext) { - var guid = guidFor(obj); + Example: - if (!this.changedItems[guid]) { - this.changedItems[guid] = { - array: array, - observerContext: observerContext, - obj: obj, - previousValues: {} - }; - } + ```javascript + App.PostController = Ember.Controller.extend({ + posts: Ember.inject.controller() + }); + ``` - ++this.changedItemCount; - this.changedItems[guid].previousValues[keyName] = get(obj, keyName); - }, + 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: - itemPropertyDidChange: function (obj, keyName, array, observerContext) { - if (--this.changedItemCount === 0) { - this.flushChanges(); - } - }, + ```javascript + App.PostController = Ember.Controller.extend({ + needs: 'posts', + posts: Ember.computed.alias('controllers.posts') + }); + ``` - flushChanges: function () { - var changedItems = this.changedItems; - var key, c, changeMeta; + @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); + - for (key in changedItems) { - c = changedItems[key]; - if (c.observerContext.destroyed) { continue; } + __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"]; - this.updateIndexes(c.observerContext.trackedArray, c.observerContext.dependentArray); + /** + @module ember + @submodule ember-runtime + */ - 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)); - } + /** + `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`. - this.changedItems = {}; - this.callbacks.flushedChanges.call(this.instanceMeta.context, this.getValue(), this.instanceMeta.sugarMeta); + `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; } - }; - 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); + // avoid cyclical loops + if (deep && (loc = indexOf(seen, obj)) >= 0) { + return copies[loc]; } - } - function normalizeRemoveCount(index, length, removedCount) { - return Math.min(removedCount, length - index); - } + 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 = {}; + + for (key in obj) { + // support Null prototype + if (!Object.prototype.hasOwnProperty.call(obj, key)) { + continue; + } + + // Prevents browsers that don't respect non-enumerability from + // copying internal Ember properties + if (key.substring(0, 2) === '__') { + continue; + } - 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; + ret[key] = deep ? _copy(obj[key], deep, seen, copies) : obj[key]; + } + } - if (previousValues) { - // previous values only available for item property changes - this.previousValues = previousValues; + if (deep) { + seen.push(obj); + copies.push(ret); } - } - 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); + return ret; } - function reset(cp, propertyName) { - var hadMeta = cp._hasInstanceMeta(this, propertyName); - var meta = cp._instanceMeta(this, propertyName); + /** + 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). - if (hadMeta) { meta.setValue(cp.resetValue(meta.getValue())); } + 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. - if (cp.options.initialize) { - cp.options.initialize.call(this, meta.getValue(), { - property: cp, - propertyName: propertyName - }, meta.sugarMeta); + @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 } - } - function partiallyRecomputeFor(obj, dependentKey) { - if (arrayBracketPattern.test(dependentKey)) { - return false; + if (Copyable && Copyable.detect(obj)) { + return obj.copy(deep); } - 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; - this.dependentArrays = {}; - this.sugarMeta = {}; - this.initialValue = initialValue; + return _copy(obj, deep, deep ? [] : null, deep ? [] : null); } + }); +enifed("ember-runtime/core", + ["exports"], + function(__exports__) { + "use strict"; + /** + @module ember + @submodule ember-runtime + */ - ReduceComputedPropertyInstanceMeta.prototype = { - getValue: function () { - var value = cacheGet(this.cache, this.propertyName); - - if (value !== undefined) { - return value; - } else { - return this.initialValue; - } - }, - - 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; - } + /** + 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. - if (triggerObservers) { - propertyWillChange(this.context, this.propertyName); - } + ```javascript + Ember.isEqual('hello', 'hello'); // true + Ember.isEqual(1, 2); // false + Ember.isEqual([4, 2], [4, 2]); // false + ``` - if (newValue === undefined) { - cacheRemove(this.cache, this.propertyName); - } else { - cacheSet(this.cache, this.propertyName, newValue); - } + @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 (triggerObservers) { - propertyDidChange(this.context, this.propertyName); - } + 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"; /** - A computed property whose dependent keys are arrays and which is updated with - "one at a time" semantics. - - @class ReduceComputedProperty - @namespace Ember - @extends Ember.ComputedProperty - @constructor + @module ember + @submodule ember-runtime */ - __exports__.ReduceComputedProperty = ReduceComputedProperty; - // TODO: default export + var Ember = __dependency1__["default"]; + // Ember.EXTEND_PROTOTYPES, Ember.assert + var expandProperties = __dependency2__["default"]; + var computed = __dependency3__.computed; + var observer = __dependency4__.observer; - function ReduceComputedProperty(options) { - var cp = this; + var a_slice = Array.prototype.slice; + var FunctionPrototype = Function.prototype; - this.options = options; - 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 = {}; + if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) { - this.readOnly(); - this.cacheable(); + /** + 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.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); - }; + Computed properties allow you to treat a function like a property: - var recompute = function(propertyName) { - var meta = cp._instanceMeta(this, propertyName); - var callbacks = cp._callbacks(); + ```javascript + MyApp.President = Ember.Object.extend({ + firstName: '', + lastName: '', - reset.call(this, cp, propertyName); + fullName: function() { + return this.get('firstName') + ' ' + this.get('lastName'); + }.property() // Call this flag to mark the function as a property + }); - 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 president = MyApp.President.create({ + firstName: 'Barack', + lastName: 'Obama' + }); - if (!partiallyRecomputeFor(this, dependentKey)) { return; } + president.get('fullName'); // 'Barack Obama' + ``` - var dependentArray = get(this, dependentKey); - var previousDependentArray = meta.dependentArrays[dependentKey]; + Treating a function like a property is useful because they can work with + bindings, just like any other property. - 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; + 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 (previousDependentArray) { - meta.dependentArraysObserver.teardownObservers(previousDependentArray, dependentKey); - } + ```javascript + MyApp.President = Ember.Object.extend({ + firstName: '', + lastName: '', - if (dependentArray) { - meta.dependentArraysObserver.setupObservers(dependentArray, dependentKey); - } - } - }, this); - }, this); + fullName: function() { + return this.get('firstName') + ' ' + this.get('lastName'); - forEach(cp._dependentKeys, function(dependentKey) { - if (!partiallyRecomputeFor(this, dependentKey)) { return; } + // Tell Ember.js that this computed property depends on firstName + // and lastName + }.property('firstName', 'lastName') + }); + ``` - var dependentArray = get(this, dependentKey); + 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. - if (dependentArray) { - addItems.call(this, dependentArray, callbacks, cp, propertyName, meta); - } - }, this); + See [Ember.ComputedProperty](/api/classes/Ember.ComputedProperty.html), [Ember.computed](/api/#method_computed). + + @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. - this.func = function (propertyName) { - Ember.assert('Computed reduce values require at least one dependent key', cp._dependentKeys); + 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: - recompute.call(this, propertyName); + ```javascript + Ember.Object.extend({ + valueObserver: function() { + // Executes whenever the "value" property changes + }.observes('value') + }); + ``` - return cp._instanceMeta(this, propertyName).getValue(); + In the future this method may become asynchronous. If you want to ensure + synchronous behavior, use `observesImmediately`. + + See `Ember.observer`. + + @method observes + @for Function + */ + 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)); }; - } - ReduceComputedProperty.prototype = o_create(ComputedProperty.prototype); + /** + 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. - function defaultCallback(computedValue) { - return computedValue; - } + 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: - ReduceComputedProperty.prototype._callbacks = function () { - if (!this.callbacks) { - var options = this.options; + ```javascript + Ember.Object.extend({ + valueObserver: function() { + // Executes immediately after the "value" property changes + }.observesImmediately('value') + }); + ``` - this.callbacks = { - removedItem: options.removedItem || defaultCallback, - addedItem: options.addedItem || defaultCallback, - flushedChanges: options.flushedChanges || defaultCallback - }; - } + In the future, `observes` may become asynchronous. In this event, + `observesImmediately` will maintain the synchronous behavior. - return this.callbacks; - }; + See `Ember.immediateObserver`. - ReduceComputedProperty.prototype._hasInstanceMeta = function (context, propertyName) { - return !!metaFor(context).cacheMeta[propertyName]; - }; + @method observesImmediately + @for Function + */ + 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; + }); - ReduceComputedProperty.prototype._instanceMeta = function (context, propertyName) { - var cacheMeta = metaFor(context).cacheMeta; - var meta = cacheMeta[propertyName]; + // observes handles property expansion + return this.observes.apply(this, arguments); + }; - if (!meta) { - meta = cacheMeta[propertyName] = new ReduceComputedPropertyInstanceMeta(context, propertyName, this.initialValue()); - meta.dependentArraysObserver = new DependentArraysObserver(this._callbacks(), this, meta, context, propertyName, meta.sugarMeta); - } + /** + 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. - return meta; - }; + 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: - ReduceComputedProperty.prototype.initialValue = function () { - if (typeof this.options.initialValue === 'function') { - return this.options.initialValue(); - } - else { - return this.options.initialValue; - } - }; + ```javascript + Ember.Object.extend({ + valueObserver: function() { + // Executes whenever the "value" property is about to change + }.observesBefore('value') + }); + ``` - ReduceComputedProperty.prototype.resetValue = function (value) { - return this.initialValue(); - }; + See `Ember.beforeObserver`. - ReduceComputedProperty.prototype.itemPropertyKey = function (dependentArrayKey, itemPropertyKey) { - this._itemPropertyKeys[dependentArrayKey] = this._itemPropertyKeys[dependentArrayKey] || []; - this._itemPropertyKeys[dependentArrayKey].push(itemPropertyKey); - }; + @method observesBefore + @for Function + */ + FunctionPrototype.observesBefore = function () { + var watched = []; + var addWatchedProperty = function (obs) { + watched.push(obs); + }; - ReduceComputedProperty.prototype.clearItemPropertyKeys = function (dependentArrayKey) { - if (this._itemPropertyKeys[dependentArrayKey]) { - this._previousItemPropertyKeys[dependentArrayKey] = this._itemPropertyKeys[dependentArrayKey]; - this._itemPropertyKeys[dependentArrayKey] = []; - } - }; + for (var i = 0, l = arguments.length; i < l; ++i) { + expandProperties(arguments[i], addWatchedProperty); + } - ReduceComputedProperty.prototype.property = function () { - var cp = this; - var args = a_slice.call(arguments); - var propertyArgs = {}; - var match, dependentArrayKey; + this.__ember_observesBefore__ = watched; - 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]; + return this; + }; - var itemPropertyKeyPattern = match[2]; - var addItemPropertyKey = function (itemPropertyKey) { - cp.itemPropertyKey(dependentArrayKey, itemPropertyKey); - }; + /** + The `on` extension of Javascript's Function prototype is available + when `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Function` is + true, which is the default. - expandProperties(itemPropertyKeyPattern, addItemPropertyKey); - propertyArgs[guidFor(dependentArrayKey)] = dependentArrayKey; - } else { - propertyArgs[guidFor(dependentKey)] = dependentKey; - } - }); + You can listen for events simply by adding the `on` call to the end of + your method declarations in classes or mixins that you write. For example: - var propertyArgsToArray = []; - for (var guid in propertyArgs) { - propertyArgsToArray.push(propertyArgs[guid]); - } + ```javascript + Ember.Mixin.create({ + doSomethingWithElement: function() { + // Executes whenever the "didInsertElement" event fires + }.on('didInsertElement') + }); + ``` - return ComputedProperty.prototype.property.apply(this, propertyArgsToArray); - }; + See `Ember.on`. - /** - 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. + @method on + @for Function + */ + FunctionPrototype.on = function () { + var events = a_slice.call(arguments); + this.__ember_listens__ = events; - 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: + return this; + }; + } + }); +enifed("ember-runtime/ext/rsvp", + ["ember-metal/core","ember-metal/logger","ember-metal/run_loop","rsvp","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + /* globals RSVP:true */ - `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. + var Ember = __dependency1__["default"]; + var Logger = __dependency2__["default"]; + var run = __dependency3__["default"]; - `initialize` - An optional initialize function. Typically this will be used - to set up state on the instanceMeta object. + // this is technically incorrect (per @wycats) + // it should be `import * as RSVP from 'rsvp';` but + // Esprima does not support this syntax yet (and neither does + // es6-module-transpiler 0.4.0 - 0.6.2). + var RSVP = __dependency4__; - `removedItem` - A function that is called each time an element is removed - from the array. + var testModuleName = 'ember-testing/test'; + var Test; - `addedItem` - A function that is called each time an element is added to - the array. + var asyncStart = function() { + if (Ember.Test && Ember.Test.adapter) { + Ember.Test.adapter.asyncStart(); + } + }; + var asyncEnd = function() { + if (Ember.Test && Ember.Test.adapter) { + Ember.Test.adapter.asyncEnd(); + } + }; - The `initialize` function has the following signature: + RSVP.configure('async', function(callback, promise) { + var async = !run.currentRunLoop; - ```javascript - function(initialValue, changeMeta, instanceMeta) - ``` + if (Ember.testing && async) { asyncStart(); } + + run.backburner.schedule('actions', function(){ + if (Ember.testing && async) { asyncEnd(); } + callback(promise); + }); + }); + + RSVP.Promise.prototype.fail = function(callback, label){ + Ember.deprecate('RSVP.Promise.fail has been renamed as RSVP.Promise.catch'); + return this['catch'](callback, label); + }; - `initialValue` - The value of the `initialValue` property from the - options object. + RSVP.onerrorDefault = function (e) { + var error; - `changeMeta` - An object which contains meta information about the - computed. It contains the following properties: + if (e && e.errorThrown) { + // jqXHR provides this + error = e.errorThrown; + if (typeof error === 'string') { + error = new Error(error); + } + error.__reason_with_error_thrown__ = e; + } else { + error = e; + } - - `property` the computed property - - `propertyName` the name of the property on the object + if (error && error.name !== 'TransitionAborted') { + if (Ember.testing) { + // ES6TODO: remove when possible + if (!Test && Ember.__loader.registry[testModuleName]) { + Test = requireModule(testModuleName)['default']; + } - `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 (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); + } + } + }; + RSVP.on('error', RSVP.onerrorDefault); - The `removedItem` and `addedItem` functions both have the following signature: + __exports__["default"] = RSVP; + }); +enifed("ember-runtime/ext/string", + ["ember-metal/core","ember-runtime/system/string"], + function(__dependency1__, __dependency2__) { + "use strict"; + /** + @module ember + @submodule ember-runtime + */ - ```javascript - function(accumulatedValue, item, changeMeta, instanceMeta) - ``` + var Ember = __dependency1__["default"]; + // Ember.EXTEND_PROTOTYPES, Ember.assert, Ember.FEATURES + var fmt = __dependency2__.fmt; + var w = __dependency2__.w; + var loc = __dependency2__.loc; + var camelize = __dependency2__.camelize; + var decamelize = __dependency2__.decamelize; + var dasherize = __dependency2__.dasherize; + var underscore = __dependency2__.underscore; + var capitalize = __dependency2__.capitalize; + var classify = __dependency2__.classify; - `accumulatedValue` - The value returned from the last time - `removedItem` or `addedItem` was called or `initialValue`. + var StringPrototype = String.prototype; - `item` - the element added or removed from the array + if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { - `changeMeta` - An object which contains meta information about the - change. It contains the following properties: + /** + See [Ember.String.fmt](/api/classes/Ember.String.html#method_fmt). - - `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. + @method fmt + @for String + */ + StringPrototype.fmt = function () { + return fmt(this, arguments); + }; - For property changes triggered on an item property change (when - depKey is something like `someArray.@each.someProperty`), - `changeMeta` will also contain the following property: + /** + See [Ember.String.w](/api/classes/Ember.String.html#method_w). - - `previousValues` an object whose keys are the properties that changed on - the item, and whose values are the item's previous values. + @method w + @for String + */ + StringPrototype.w = function () { + return w(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). + /** + See [Ember.String.loc](/api/classes/Ember.String.html#method_loc). - `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. + @method loc + @for String + */ + StringPrototype.loc = function () { + return loc(this, arguments); + }; - 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. + /** + See [Ember.String.camelize](/api/classes/Ember.String.html#method_camelize). - 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. + @method camelize + @for String + */ + StringPrototype.camelize = function () { + return camelize(this); + }; - Example + /** + See [Ember.String.decamelize](/api/classes/Ember.String.html#method_decamelize). - ```javascript - Ember.computed.max = function(dependentKey) { - return Ember.reduceComputed(dependentKey, { - initialValue: -Infinity, + @method decamelize + @for String + */ + StringPrototype.decamelize = function () { + return decamelize(this); + }; - addedItem: function(accumulatedValue, item, changeMeta, instanceMeta) { - return Math.max(accumulatedValue, item); - }, + /** + See [Ember.String.dasherize](/api/classes/Ember.String.html#method_dasherize). - removedItem: function(accumulatedValue, item, changeMeta, instanceMeta) { - if (item < accumulatedValue) { - return accumulatedValue; - } - } - }); + @method dasherize + @for String + */ + StringPrototype.dasherize = function () { + return dasherize(this); }; - ``` - 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. + /** + See [Ember.String.underscore](/api/classes/Ember.String.html#method_underscore). - Example + @method underscore + @for String + */ + StringPrototype.underscore = function () { + return underscore(this); + }; - ```javascript - App.PeopleController = Ember.ArrayController.extend({ - itemController: 'person', + /** + See [Ember.String.classify](/api/classes/Ember.String.html#method_classify). - 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'); + @method classify + @for String + */ + StringPrototype.classify = function () { + return classify(this); + }; - return Ember.compare(reversedNameA, reversedNameB); - }) - }); + /** + See [Ember.String.capitalize](/api/classes/Ember.String.html#method_capitalize). - App.PersonController = Ember.ObjectController.extend({ - reversedName: function() { - return reverse(get(this, 'name')); - }.property('name') - }); - ``` + @method capitalize + @for String + */ + StringPrototype.capitalize = function () { + return capitalize(this); + }; + } + }); +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 Ember = __dependency1__["default"]; + // Ember.assert + var indexOf = __dependency2__.indexOf; + var meta = __dependency3__.meta; + var InjectedProperty = __dependency4__["default"]; + var keys = __dependency5__["default"]; - 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. + /** + Namespace for injection helper methods. - 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. + @class inject + @namespace Ember + */ + function inject() { + Ember.assert("Injected properties must be created through helpers, see `" + + keys(inject).join("`, `") + "`"); + } - Example + // Dictionary of injection validations by type, added to by `createInjectionHelper` + var typeValidators = {}; - ```javascript - Ember.Object.extend({ - // When `string` is changed, `computed` is completely recomputed. - string: 'a string', + /** + 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. - // When an item is added to `array`, `addedItem` is called. - array: [], + @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 createInjectionHelper(type, validator) { + typeValidators[type] = validator; - // When an item is added to `anotherArray`, `computed` is completely - // recomputed. - anotherArray: [], + inject[type] = function(name) { + return new InjectedProperty(type, name); + }; + } - computed: Ember.reduceComputed('string', 'array', 'anotherArray.[]', { - addedItem: addedItemCallback, - removedItem: removedItemCallback - }) - }); - ``` + __exports__.createInjectionHelper = createInjectionHelper;/** + Validation function that runs per-type validation functions once for each + injected type encountered. - @method reduceComputed - @for Ember - @param {String} [dependentKeys*] - @param {Object} options - @return {Ember.ComputedProperty} + @private + @method validatePropertyInjections + @namespace Ember + @param {Object} factory The factory object */ - function reduceComputed(options) { - var args; - - if (arguments.length > 1) { - args = a_slice.call(arguments, 0, -1); - options = a_slice.call(arguments, -1)[0]; - } - - if (typeof options !== 'object') { - throw new EmberError('Reduce Computed Property declared without an options hash'); - } + function validatePropertyInjections(factory) { + var proto = factory.proto(); + var descs = meta(proto).descs; + var types = []; + var key, desc, validator, i, l; - if (!('initialValue' in options)) { - throw new EmberError('Reduce Computed Property declared without an initial value'); + for (key in descs) { + desc = descs[key]; + if (desc instanceof InjectedProperty && indexOf(types, desc.type) === -1) { + types.push(desc.type); + } } - var cp = new ReduceComputedProperty(options); + if (types.length) { + for (i = 0, l = types.length; i < l; i++) { + validator = typeValidators[types[i]]; - if (args) { - cp.property.apply(cp, args); + if (typeof validator === 'function') { + validator(factory); + } + } } - return cp; + return true; } - __exports__.reduceComputed = reduceComputed; + __exports__.validatePropertyInjections = validatePropertyInjections;__exports__["default"] = inject; }); -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__) { +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 @@ -27889,11109 +28817,11591 @@ enifed("ember-runtime/computed/reduce_computed_macros", var Ember = __dependency1__["default"]; // Ember.assert var get = __dependency2__.get; - 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 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; - var a_slice = [].slice; + function contentPropertyWillChange(content, contentKey) { + var key = contentKey.slice(8); // remove "content." + if (key in this) { return; } // if shadowed in proxy + propertyWillChange(this, key); + } + + function contentPropertyDidChange(content, contentKey) { + var key = contentKey.slice(8); // remove "content." + if (key in this) { return; } // if shadowed in proxy + propertyDidChange(this, key); + } /** - A computed property that returns the sum of the value - in the dependent array. + `Ember.ProxyMixin` forwards all properties not defined by the proxy itself + to a proxied `content` object. See Ember.ObjectProxy for more details. - @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 + @class ProxyMixin + @namespace Ember */ + __exports__["default"] = Mixin.create({ + /** + The object whose properties will be forwarded. - function sum(dependentKey){ - return reduceComputed(dependentKey, { - initialValue: 0, - - addedItem: function(accumulatedValue, item, changeMeta, instanceMeta){ - return accumulatedValue + item; - }, - - removedItem: function(accumulatedValue, item, changeMeta, instanceMeta){ - return accumulatedValue - item; - } - }); - } - - __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. + @property content + @type Ember.Object + @default null + */ + content: null, + _contentDidChange: observer('content', function() { + Ember.assert("Can't set Proxy's content to itself", get(this, 'content') !== this); + }), - ```javascript - var Person = Ember.Object.extend({ - childAges: Ember.computed.mapBy('children', 'age'), - maxChildAge: Ember.computed.max('childAges') - }); + isTruthy: computed.bool('content'), - var lordByron = Person.create({ children: [] }); + _debugContainerKey: null, - 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 - ``` + willWatchProperty: function (key) { + var contentKey = 'content.' + key; + addBeforeObserver(this, contentKey, null, contentPropertyWillChange); + addObserver(this, contentKey, null, contentPropertyDidChange); + }, - @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, + didUnwatchProperty: function (key) { + var contentKey = 'content.' + key; + removeBeforeObserver(this, contentKey, null, contentPropertyWillChange); + removeObserver(this, contentKey, null, contentPropertyDidChange); + }, - addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { - return Math.max(accumulatedValue, item); - }, + unknownProperty: function (key) { + var content = get(this, 'content'); + if (content) { + return get(content, key); + } + }, - removedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { - if (item < accumulatedValue) { - return accumulatedValue; - } + 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; } - }); - } - __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. + 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); + } - ```javascript - var Person = Ember.Object.extend({ - childAges: Ember.computed.mapBy('children', 'age'), - minChildAge: Ember.computed.min('childAges') - }); + }); + }); +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"; + /** + @module ember + @submodule ember-runtime + */ + var merge = __dependency1__["default"]; + var Mixin = __dependency2__.Mixin; + var get = __dependency3__.get; + var typeOf = __dependency4__.typeOf; - var lordByron = Person.create({ children: [] }); + /** + 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. - 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 - ``` + `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.) - @method computed.min - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computes the smallest value in the dependentKey's array + @class ActionHandler + @namespace Ember */ - function min(dependentKey) { - return reduceComputed(dependentKey, { - initialValue: Infinity, + var ActionHandler = Mixin.create({ + mergedProperties: ['_actions'], - addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { - return Math.min(accumulatedValue, item); - }, + /** + The collection of functions, keyed by name, available on this + `ActionHandler` as action targets. - removedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { - if (item > accumulatedValue) { - return accumulatedValue; - } - } - }); - } + These functions will be invoked when a matching `{{action}}` is triggered + from within a template and the application's current route is this route. - __exports__.min = min;/** - Returns an array mapped via the callback + Actions can also be invoked from other parts of your application + via `ActionHandler#send`. - 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. + 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.: - ```javascript - function(item, index); - ``` + ```js + App.CanDisplayBanner = Ember.Mixin.create({ + actions: { + displayBanner: function(msg) { + // ... + } + } + }); - Example + App.WelcomeRoute = Ember.Route.extend(App.CanDisplayBanner, { + actions: { + playMusic: function() { + // ... + } + } + }); - ```javascript - var Hamster = Ember.Object.extend({ - excitingChores: Ember.computed.map('chores', function(chore, index) { - return chore.toUpperCase() + '!'; - }) - }); + // `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 hamster = Hamster.create({ - chores: ['clean', 'write more unit tests'] - }); + Within a Controller, Route, View or Component's action handler, + the value of the `this` context is the Controller, Route, View or + Component object: - hamster.get('excitingChores'); // ['CLEAN!', 'WRITE MORE UNIT TESTS!'] - ``` + ```js + App.SongRoute = Ember.Route.extend({ + actions: { + myAction: function() { + this.controllerFor("song"); + this.transitionTo("other.route"); + ... + } + } + }); + ``` - @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; - } - }; + 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: - return arrayComputed(dependentKey, options); - } + Take for example the following routes: - __exports__.map = map;/** - Returns an array mapped to the specified key. + ```js + App.DebugRoute = Ember.Mixin.create({ + actions: { + debugRouteInformation: function() { + console.debug("trololo"); + } + } + }); - ```javascript - var Person = Ember.Object.extend({ - childAges: Ember.computed.mapBy('children', 'age') - }); + App.AnnoyingDebugRoute = Ember.Route.extend(App.DebugRoute, { + actions: { + debugRouteInformation: function() { + // also call the debugRouteInformation of mixed in App.DebugRoute + this._super(); - var lordByron = Person.create({ children: [] }); + // show additional annoyance + window.alert(...); + } + } + }); + ``` - 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] - ``` + ## Bubbling - @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); - } + 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: - __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. + ```js + App.Router.map(function() { + this.resource("album", function() { + this.route("song"); + }); + }); - 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. + App.AlbumRoute = Ember.Route.extend({ + actions: { + startPlaying: function() { + } + } + }); - ```javascript - function(item, index); - ``` + App.AlbumSongRoute = Ember.Route.extend({ + actions: { + startPlaying: function() { + // ... - ```javascript - var Hamster = Ember.Object.extend({ - remainingChores: Ember.computed.filter('chores', function(chore, index) { - return !chore.done; - }) - }); + if (actionShouldAlsoBeTriggeredOnParentRoute) { + return true; + } + } + } + }); + ``` - var hamster = Hamster.create({ - chores: [ - { name: 'cook', done: true }, - { name: 'clean', done: true }, - { name: 'write more unit tests', done: false } - ] - }); + @property actions + @type Hash + @default null + */ - hamster.get('remainingChores'); // [{name: 'write more unit tests', done: false}] - ``` + /** + 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. - @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(); - }, + @private + @method willMergeMixin + */ + willMergeMixin: function(props) { + var hashName; - addedItem: function (array, item, changeMeta, instanceMeta) { - var match = !!callback.call(this, item, changeMeta.index); - var filterIndex = instanceMeta.filteredArrayIndexes.addItem(changeMeta.index, match); + if (!props._actions) { + Ember.assert("'actions' should not be a function", typeof(props.actions) !== 'function'); - if (match) { - array.insertAt(filterIndex, item); + 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'; } - return array; - }, - - removedItem: function(array, item, changeMeta, instanceMeta) { - var filterIndex = instanceMeta.filteredArrayIndexes.removeItem(changeMeta.index); - - if (filterIndex > -1) { - array.removeAt(filterIndex); + if (hashName) { + props._actions = merge(props._actions || {}, props[hashName]); } - return array; + delete props[hashName]; } - }; + }, - return arrayComputed(dependentKey, options); - } + /** + Triggers a named action on the `ActionHandler`. Any parameters + supplied after the `actionName` string will be passed as arguments + to the action target function. - __exports__.filter = filter;/** - Filters the array by the property and value + 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`. - ```javascript - var Hamster = Ember.Object.extend({ - remainingChores: Ember.computed.filterBy('chores', 'done', false) - }); + Example - var hamster = Hamster.create({ - chores: [ - { name: 'cook', done: true }, - { name: 'clean', done: true }, - { name: 'write more unit tests', done: false } - ] - }); + ```js + App.WelcomeRoute = Ember.Route.extend({ + actions: { + playTheme: function() { + this.send('playMusic', 'theme.mp3'); + }, + playMusic: function(track) { + // ... + } + } + }); + ``` - hamster.get('remainingChores'); // [{ name: 'write more unit tests', done: false }] - ``` + @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; - @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; + if (this._actions && this._actions[actionName]) { + if (this._actions[actionName].apply(this, args) === true) { + // handler returned true, so this action will bubble + } else { + return; + } + } - if (arguments.length === 2) { - callback = function(item) { - return get(item, propertyKey); - }; - } else { - callback = function(item) { - return get(item, propertyKey) === value; - }; + 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); + } } + }); - 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; + __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"; /** - A computed property which returns a new array with all the unique - elements from one or more dependent arrays. + @module ember + @submodule ember-runtime + */ - Example + // .......................................................... + // HELPERS + // + var Ember = __dependency1__["default"]; + // ES6TODO: Ember.A - ```javascript - var Hamster = Ember.Object.extend({ - uniqueFruits: Ember.computed.uniq('fruits') - }); + 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; - var hamster = Hamster.create({ - fruits: [ - 'banana', - 'grape', - 'kale', - 'banana' - ] - }); + function arrayObserversHelper(obj, target, opts, operation, notify) { + var willChange = (opts && opts.willChange) || 'arrayWillChange'; + var didChange = (opts && opts.didChange) || 'arrayDidChange'; + var hasObservers = get(obj, 'hasArrayObservers'); - hamster.get('uniqueFruits'); // ['banana', 'grape', 'kale'] - ``` + if (hasObservers === notify) { + propertyWillChange(obj, 'hasArrayObservers'); + } - @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); + operation(obj, '@array:before', target, willChange); + operation(obj, '@array:change', target, didChange); - args.push({ - initialize: function(array, changeMeta, instanceMeta) { - instanceMeta.itemCounts = {}; - }, + if (hasObservers === notify) { + propertyDidChange(obj, 'hasArrayObservers'); + } - addedItem: function(array, item, changeMeta, instanceMeta) { - var guid = guidFor(item); + return obj; + } - if (!instanceMeta.itemCounts[guid]) { - instanceMeta.itemCounts[guid] = 1; - array.pushObject(item); - } else { - ++instanceMeta.itemCounts[guid]; - } - return array; - }, + // .......................................................... + // 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. - removedItem: function(array, item, _, instanceMeta) { - var guid = guidFor(item); - var itemCounts = instanceMeta.itemCounts; + 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 (--itemCounts[guid] === 0) { - array.removeObject(item); - } + 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. - return array; - } - }); + 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. - return arrayComputed.apply(null, args); - } + 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.[]')`. - __exports__.uniq = uniq;/** - Alias for [Ember.computed.uniq](/api/#method_computed_uniq). + To support `Ember.Array` in your own class, you must override two + primitives to use it: `replace()` and `objectAt()`. - @method computed.union - @for Ember - @param {String} propertyKey* - @return {Ember.ComputedProperty} computes a new array with all the - unique elements from the dependent array + Note that the Ember.Array mixin also incorporates the `Ember.Enumerable` + mixin. All `Ember.Array`-like objects are also enumerable. + + @class Array + @namespace Ember + @uses Ember.Enumerable + @since Ember 0.9.0 */ - var union = uniq; - __exports__.union = union; - /** - A computed property which returns a new array with all the duplicated - elements from two or more dependent arrays. + __exports__["default"] = Mixin.create(Enumerable, { - Example + /** + Your array must support the `length` property. Your replace methods should + set this property whenever it changes. - ```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') - }); + @property {Number} length + */ + length: required(), - obj.get('friendsInCommon'); // ['William King', 'Mary Somerville'] - ``` + /** + Returns the object at the given `index`. If the given `index` is negative + or is greater or equal than the array length, returns `undefined`. - @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 args = a_slice.call(arguments); + 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. - args.push({ - initialize: function (array, changeMeta, instanceMeta) { - instanceMeta.itemCounts = {}; - }, + ```javascript + var arr = ['a', 'b', 'c', 'd']; - 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; + arr.objectAt(0); // 'a' + arr.objectAt(3); // 'd' + arr.objectAt(-1); // undefined + arr.objectAt(4); // undefined + arr.objectAt(5); // undefined + ``` - if (!itemCounts[itemGuid]) { - itemCounts[itemGuid] = {}; - } + @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; + } - if (itemCounts[itemGuid][dependentGuid] === undefined) { - itemCounts[itemGuid][dependentGuid] = 0; - } + return get(this, idx); + }, - if (++itemCounts[itemGuid][dependentGuid] === 1 && - numberOfDependentArrays === keys(itemCounts[itemGuid]).length) { - array.addObject(item); - } + /** + This returns the objects at the specified indexes, using `objectAt`. - return array; - }, + ```javascript + var arr = ['a', 'b', 'c', 'd']; - removedItem: function(array, item, changeMeta, instanceMeta) { - var itemGuid = guidFor(item); - var dependentGuid = guidFor(changeMeta.arrayChanged); - var numberOfArraysItemAppearsIn; - var itemCounts = instanceMeta.itemCounts; + arr.objectsAt([0, 1, 2]); // ['a', 'b', 'c'] + arr.objectsAt([2, 3, 4]); // ['c', 'd', undefined] + ``` - if (itemCounts[itemGuid][dependentGuid] === undefined) { - itemCounts[itemGuid][dependentGuid] = 0; - } + @method objectsAt + @param {Array} indexes An array of indexes of items to return. + @return {Array} + */ + objectsAt: function(indexes) { + var self = this; - if (--itemCounts[itemGuid][dependentGuid] === 0) { - delete itemCounts[itemGuid][dependentGuid]; - numberOfArraysItemAppearsIn = keys(itemCounts[itemGuid]).length; + return map(indexes, function(idx) { + return self.objectAt(idx); + }); + }, - if (numberOfArraysItemAppearsIn === 0) { - delete itemCounts[itemGuid]; - } + // overrides Ember.Enumerable version + nextObject: function(idx) { + return this.objectAt(idx); + }, - array.removeObject(item); - } + /** + 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. - return array; + This property overrides the default property defined in `Ember.Enumerable`. + + @property [] + @return this + */ + '[]': computed(function(key, value) { + if (value !== undefined) { + this.replace(0, get(this, 'length'), value); } - }); - return arrayComputed.apply(null, args); - } + return this; + }), - __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. + firstObject: computed(function() { + return this.objectAt(0); + }), - Example + lastObject: computed(function() { + return this.objectAt(get(this, 'length') - 1); + }), - ```javascript - var Hamster = Ember.Object.extend({ - likes: ['banana', 'grape', 'kale'], - wants: Ember.computed.setDiff('likes', 'fruits') - }); + // optimized version from Enumerable + contains: function(obj) { + return this.indexOf(obj) >= 0; + }, - var hamster = Hamster.create({ - fruits: [ - 'grape', - 'kale', - ] - }); + // 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. - hamster.get('wants'); // ['banana'] - ``` + ```javascript + var arr = ['red', 'green', 'blue']; - @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.'); - } + arr.slice(0); // ['red', 'green', 'blue'] + arr.slice(0, 2); // ['red', 'green'] + arr.slice(1, 100); // ['green', 'blue'] + ``` - return arrayComputed(setAProperty, setBProperty, { - addedItem: function (array, item, changeMeta, instanceMeta) { - var setA = get(this, setAProperty); - var setB = get(this, setBProperty); + @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 (changeMeta.arrayChanged === setA) { - if (!setB.contains(item)) { - array.addObject(item); - } - } else { - array.removeObject(item); - } + if (isNone(beginIndex)) { + beginIndex = 0; + } - return array; - }, + if (isNone(endIndex) || (endIndex > length)) { + endIndex = length; + } - removedItem: function (array, item, changeMeta, instanceMeta) { - var setA = get(this, setAProperty); - var setB = get(this, setBProperty); + if (beginIndex < 0) { + beginIndex = length + beginIndex; + } - if (changeMeta.arrayChanged === setB) { - if (setA.contains(item)) { - array.addObject(item); - } - } else { - array.removeObject(item); - } + if (endIndex < 0) { + endIndex = length + endIndex; + } - return array; + while (beginIndex < endIndex) { + ret[ret.length] = this.objectAt(beginIndex++); } - }); - } - __exports__.setDiff = setDiff;function binarySearch(array, item, low, high) { - var mid, midItem, res, guidMid, guidItem; + return ret; + }, - if (arguments.length < 4) { - high = get(array, 'length'); - } + /** + 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 (arguments.length < 3) { - low = 0; - } + ```javascript + var arr = ['a', 'b', 'c', 'd', 'a']; - if (low === high) { - return low; - } + 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 + ``` - mid = low + Math.floor((high - low) / 2); - midItem = array.objectAt(mid); + @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 len = get(this, 'length'); + var idx; - guidMid = guidFor(midItem); - guidItem = guidFor(item); + if (startAt === undefined) { + startAt = 0; + } - if (guidMid === guidItem) { - return mid; - } + if (startAt < 0) { + startAt += len; + } - res = this.order(midItem, item); + for (idx = startAt; idx < len; idx++) { + if (this.objectAt(idx) === object) { + return idx; + } + } - if (res === 0) { - res = guidMid < guidItem ? -1 : 1; - } + return -1; + }, + /** + 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. - if (res < 0) { - return this.binarySearch(array, item, mid+1, high); - } else if (res > 0) { - return this.binarySearch(array, item, low, mid); - } + ```javascript + var arr = ['a', 'b', 'c', 'd', 'a']; - return mid; - } + 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 + ``` + @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 + */ + lastIndexOf: function(object, startAt) { + var len = get(this, 'length'); + var idx; - /** - 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. + if (startAt === undefined || startAt >= len) { + startAt = len-1; + } - The callback method you provide should have the following signature: + if (startAt < 0) { + startAt += len; + } - ```javascript - function(itemA, itemB); - ``` + for (idx = startAt; idx >= 0; idx--) { + if (this.objectAt(idx) === object) { + return idx; + } + } - - `itemA` the first item to compare. - - `itemB` the second item to compare. + return -1; + }, - 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`. + // .......................................................... + // ARRAY OBSERVERS + // - 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`. + /** + Adds an array observer to the receiving array. The array observer object + normally must implement two methods: - Example + * `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. - ```javascript - var ToDoList = Ember.Object.extend({ - // using standard ascending sort - todosSorting: ['name'], - sortedTodos: Ember.computed.sort('todos', 'todosSorting'), + 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. - // using descending sort - todosSortingDesc: ['name:desc'], - sortedTodosDesc: Ember.computed.sort('todos', 'todosSortingDesc'), + 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. - // 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; - } + @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 + */ - return 0; - }) - }); + addArrayObserver: function(target, opts) { + return arrayObserversHelper(this, target, opts, addListener, false); + }, - var todoList = ToDoList.create({todos: [ - { name: 'Unit Test', priority: 2 }, - { name: 'Documentation', priority: 3 }, - { name: 'Release', priority: 1 } - ]}); + /** + 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. - 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 }] - ``` + @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); + }, - @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); + /** + Becomes true whenever the array currently has observers watching changes + on the array. - if (typeof sortDefinition === 'function') { - return customSort(itemsKey, sortDefinition); - } else { - return propertySort(itemsKey, sortDefinition); - } - } + @property {Boolean} hasArrayObservers + */ + hasArrayObservers: computed(function() { + return hasListeners(this, '@array:change') || hasListeners(this, '@array:before'); + }), - __exports__.sort = sort;function customSort(itemsKey, comparator) { - return arrayComputed(itemsKey, { - initialize: function (array, changeMeta, instanceMeta) { - instanceMeta.order = comparator; - instanceMeta.binarySearch = binarySearch; - instanceMeta.waitingInsertions = []; - instanceMeta.insertWaiting = function() { - var index, item; - var waiting = instanceMeta.waitingInsertions; - instanceMeta.waitingInsertions = []; - for (var i=0; i= 0 && removeAmt >= 0 && get(this, 'hasEnumerableObservers')) { + removing = []; + lim = startIdx + removeAmt; - 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; - } + for (var idx = startIdx; idx < lim; idx++) { + removing.push(this.objectAt(idx)); + } + } else { + removing = removeAmt; + } - sortProperties.push(sortProperty); - sortPropertyAscending[sortProperty] = asc; - changeMeta.property.itemPropertyKey(itemsKey, sortProperty); - }); + this.enumerableContentWillChange(removing, addAmt); + + return this; + }, + + /** + 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. + + @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; - sortPropertyDefinitions.addObserver('@each', this, updateSortPropertiesOnce); + // if no args are passed assume everything changes + if (startIdx === undefined) { + startIdx = 0; + removeAmt = addAmt = -1; + } else { + if (removeAmt === undefined) { + removeAmt = -1; } - function updateSortPropertiesOnce() { - run.once(this, updateSortProperties, changeMeta.propertyName); + if (addAmt === undefined) { + addAmt = -1; } + } - function updateSortProperties(propertyName) { - setupSortProperties.call(this); - changeMeta.property.recomputeOnce.call(this, propertyName); + if (startIdx >= 0 && addAmt >= 0 && get(this, 'hasEnumerableObservers')) { + adding = []; + lim = startIdx + addAmt; + + for (var idx = startIdx; idx < lim; idx++) { + adding.push(this.objectAt(idx)); } + } else { + adding = addAmt; + } - addObserver(this, sortPropertiesKey, updateSortPropertiesOnce); - setupSortProperties.call(this); + this.enumerableContentDidChange(removeAmt, adding); + sendEvent(this, '@array:change', [this, startIdx, removeAmt, addAmt]); - instanceMeta.order = function (itemA, itemB) { - var sortProperty, result, asc; - var keyA = this.keyFor(itemA); - var keyB = this.keyFor(itemB); + var length = get(this, 'length'); + var cachedFirst = cacheFor(this, 'firstObject'); + var cachedLast = cacheFor(this, 'lastObject'); - for (var i = 0; i < this.sortProperties.length; ++i) { - sortProperty = this.sortProperties[i]; + if (this.objectAt(0) !== cachedFirst) { + propertyWillChange(this, 'firstObject'); + propertyDidChange(this, 'firstObject'); + } - result = compare(keyA[sortProperty], keyB[sortProperty]); + if (this.objectAt(length-1) !== cachedLast) { + propertyWillChange(this, 'lastObject'); + propertyDidChange(this, 'lastObject'); + } - if (result !== 0) { - asc = this.sortPropertyAscending[sortProperty]; - return asc ? result : (-1 * result); - } - } + return this; + }, - return 0; - }; + // .......................................................... + // ENUMERATED PROPERTIES + // - instanceMeta.binarySearch = binarySearch; - setupKeyCache(instanceMeta); - }, + /** + 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. - addedItem: function (array, item, changeMeta, instanceMeta) { - var index = instanceMeta.binarySearch(array, item); - array.insertAt(index, item); - return array; - }, + If you merely want to watch for any items being added or removed to the array, + use the `[]` property instead of `@each`. - removedItem: function (array, item, changeMeta, instanceMeta) { - var index = instanceMeta.binarySearch(array, item); - array.removeAt(index); - instanceMeta.dropKeyFor(item); - return array; - } - }); - } + @property @each + */ + '@each': computed(function() { + if (!this.__each) { + // ES6TODO: GRRRRR + var EachProxy = requireModule('ember-runtime/system/each_proxy')['EachProxy']; - 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); + this.__each = new EachProxy(this); } - return this.keyCache[guid] = key; - }; - - instanceMeta.dropKeyFor = function(item) { - var guid = guidFor(item); - this.keyCache[guid] = null; - }; - instanceMeta.keyCache = {}; - } + return this.__each; + }) + }); }); -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__) { +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-runtime */ - var Ember = __dependency1__["default"]; - var get = __dependency2__.get; - 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 EmberError = __dependency8__["default"]; - - /** - `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 - MyApp.listController = Ember.ArrayController.create(); - - $.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. - - For example: + Implements some standard methods for comparing objects. Add this mixin to + any class you create that can compare its instances. - ```handlebars - {{#each post in controller}} -
  • {{post.title}} ({{post.titleLength}} characters)
  • - {{/each}} - ``` + You should implement the `compare()` method. - ```javascript - App.PostsController = Ember.ArrayController.extend({ - itemController: 'post' - }); + @class Comparable + @namespace Ember + @since Ember 0.9 + */ + __exports__["default"] = Mixin.create({ - App.PostController = Ember.ObjectController.extend({ - // the `title` property will be proxied to the underlying post. - titleLength: function() { - return this.get('title').length; - }.property('title') - }); - ``` + /** + Override to return the result of the comparison of the two parameters. The + compare method should return: - In some cases it is helpful to return a different `itemController` depending - on the particular item. Subclasses can do this by overriding - `lookupItemController`. + - `-1` if `a < b` + - `0` if `a == b` + - `1` if `a > b` - For example: + Default implementation raises an exception. - ```javascript - App.MyArrayController = Ember.ArrayController.extend({ - lookupItemController: function( object ) { - if (object.get('isSpecial')) { - return "special"; // use App.SpecialController - } else { - return "regular"; // use App.RegularController - } - } - }); - ``` + @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"]; - The itemController instances will have a `parentController` property set to - the `ArrayController` instance. + /** + `Ember.ControllerMixin` provides a standard interface for all classes that + compose Ember's controller layer: `Ember.Controller`, + `Ember.ArrayController`, and `Ember.ObjectController`. - @class ArrayController + @class ControllerMixin @namespace Ember - @extends Ember.ArrayProxy - @uses Ember.SortableMixin - @uses Ember.ControllerMixin + @uses Ember.ActionHandler */ - - __exports__["default"] = ArrayProxy.extend(ControllerMixin, SortableMixin, { + __exports__["default"] = Mixin.create(ActionHandler, ControllerContentModelAliasDeprecation, { + /* ducktype as a controller */ + isController: true, /** - 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. + The object to which actions from the view should be sent. - For example: + For example, when a Handlebars template uses the `{{action}}` helper, + it will attempt to send the action to the view's controller's `target`. - ```javascript - App.MyArrayController = Ember.ArrayController.extend({ - itemController: Ember.ObjectController.extend({ - //Item Controller Implementation - }) - }); - ``` + 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. - @property itemController - @type String | Ember.Controller + @property target @default null */ - itemController: null, - - /** - 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. - - For example: + target: null, - ```javascript - App.MyArrayController = Ember.ArrayController.extend({ - lookupItemController: function( object ) { - if (object.get('isSpecial')) { - return "special"; // use App.SpecialController - } else { - return "regular"; // use App.RegularController - } - } - }); - ``` + container: null, - @method lookupItemController - @param {Object} object - @return {String} - */ - lookupItemController: function(object) { - return get(this, 'itemController'); - }, + parentController: null, - objectAtContent: function(idx) { - var length = get(this, 'length'); - var arrangedContent = get(this, 'arrangedContent'); - var object = arrangedContent && arrangedContent.objectAt(idx); - var controllerClass; + store: null, - if (idx >= 0 && idx < length) { - controllerClass = this.lookupItemController(object); + /** + The controller's current model. When retrieving or modifying a controller's + model, this property should be used instead of the `content` property. - if (controllerClass) { - return this.controllerAt(idx, object, controllerClass); - } - } + @property model + @public + */ + model: null, - // When `controllerClass` is falsy, we have not opted in to using item - // controllers, so return the object directly. + /** + @private + */ + content: computed.alias('model') - // 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; - }, + }); + }); +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; - arrangedContentDidChange: function() { - this._super(); - this._resetSubControllers(); - }, + /** + The ControllerContentModelAliasDeprecation mixin is used to provide a useful + deprecation warning when specifying `content` directly on a `Ember.Controller` + (without also specifying `model`). - arrayContentDidChange: function(idx, removedCnt, addedCnt) { - var subControllers = this._subControllers; + 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). - if (subControllers.length) { - var subControllersToRemove = subControllers.slice(idx, idx + removedCnt); + 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. - forEach(subControllersToRemove, function(subController) { - if (subController) { - subController.destroy(); - } - }); - replace(subControllers, idx, removedCnt, new Array(addedCnt)); - } + `Ember.ControllerContentModelAliasDeprecation` is used internally by Ember in + `Ember.Controller`. - // 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); - }, + @class ControllerContentModelAliasDeprecation + @namespace Ember + @private + @since 1.7.0 + */ + __exports__["default"] = Mixin.create({ + /** + @private - init: function() { - this._super(); - this._subControllers = []; - }, + Moves `content` to `model` at extend time if a `model` is not also specified. - model: computed(function () { - return Ember.A(); - }), + Note that this currently modifies the mixin themselves, which is technically + dubious but is practically of little consequence. This may change in the + future. - /** - * Flag to mark as being "virtual". Used to keep this instance - * from participating in the parentController hierarchy. - * - * @private - * @property _isVirtual - * @type Boolean - */ - _isVirtual: false, + @method willMergeMixin + @since 1.4.0 + */ + willMergeMixin: function(props) { + // Calling super is only OK here since we KNOW that + // there is another Mixin loaded first. + this._super.apply(this, arguments); - controllerAt: function(idx, object, controllerClass) { - var container = get(this, 'container'); - var subControllers = this._subControllers; - var fullName, subController, subControllerFactory, parentController, options; + var modelSpecified = !!props.model; - if (subControllers.length > idx) { - subController = subControllers[idx]; + if (props.content && !modelSpecified) { + props.model = props.content; + delete props['content']; - if (subController) { - return subController; - } + 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 + */ - if (this._isVirtual) { - parentController = get(this, 'parentController'); - } else { - parentController = this; - } - - fullName = 'controller:' + controllerClass; + var get = __dependency1__.get; + var required = __dependency2__.required; + var Freezable = __dependency3__.Freezable; + var Mixin = __dependency2__.Mixin; + var fmt = __dependency4__.fmt; + var EmberError = __dependency5__["default"]; - if (!container.has(fullName)) { - throw new EmberError('Could not resolve itemController: "' + controllerClass + '"'); - } - subController = container.lookupFactory(fullName).create({ - target: parentController, - parentController: parentController, - model: object - }); - + /** + 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. - subControllers[idx] = subController; + You should generally implement the `copy()` method to return a copy of the + receiver. - return subController; - }, + Note that `frozenCopy()` will only work if you also implement + `Ember.Freezable`. - _subControllers: null, + @class Copyable + @namespace Ember + @since Ember 0.9 + */ + __exports__["default"] = Mixin.create({ + /** + Override to return a copy of the receiver. Default implementation raises + an exception. - _resetSubControllers: function() { - var controller; - var subControllers = this._subControllers; + @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 (subControllers.length) { - for (var i = 0, length = subControllers.length; length > i; i++) { - controller = subControllers[i]; + /** + 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. - if (controller) { - controller.destroy(); - } - } + Raises an exception if you try to call this method on a object that does + not support freezing. - subControllers.length = 0; - } - }, + 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. - willDestroy: function() { - this._resetSubControllers(); - this._super(); + @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])); + } } }); }); -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__) { +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.assert - var EmberObject = __dependency2__["default"]; - var Mixin = __dependency3__["default"]; - var createInjectionHelper = __dependency4__.createInjectionHelper; + // Ember.FEATURES, Ember.Test + var get = __dependency2__.get; + var Mixin = __dependency3__.Mixin; + var computed = __dependency4__.computed; + var RSVP = __dependency5__["default"]; /** @module ember @submodule ember-runtime */ - /** - @class Controller - @namespace Ember - @extends Ember.Object - @uses Ember.ControllerMixin - */ - var Controller = EmberObject.extend(Mixin); - - function controllerInjectionHelper(factory) { - Ember.assert("Defining an injected controller property on a " + - "non-controller is not allowed.", Controller.detect(factory)); - } - - - __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"]; - - /** - @module ember - @submodule ember-runtime - */ /** - `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`. - - `Ember.ObjectController` derives this functionality from its superclass - `Ember.ObjectProxy` and the `Ember.ControllerMixin` mixin. - - @class ObjectController + @class Deferred @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))); + */ + __exports__["default"] = Mixin.create({ + /** + Add handlers to be called when the Deferred object is resolved or rejected. - // 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(); + @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; - if (deep) { - loc = ret.length; + entity = this; + deferred = get(this, '_deferred'); + promise = deferred.promise; - while (--loc >= 0) { - ret[loc] = _copy(ret[loc], deep, seen, copies); + function fulfillmentHandler(fulfillment) { + if (fulfillment === promise) { + return resolve(entity); + } else { + return resolve(fulfillment); } } - } 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) { - // support Null prototype - if (!Object.prototype.hasOwnProperty.call(obj, key)) { - continue; - } - // Prevents browsers that don't respect non-enumerability from - // copying internal Ember properties - if (key.substring(0, 2) === '__') { - continue; - } + return promise.then(resolve && fulfillmentHandler, reject, label); + }, - ret[key] = deep ? _copy(obj[key], deep, seen, copies) : obj[key]; - } - } + /** + Resolve a Deferred object and call any `doneCallbacks` with the given args. - if (deep) { - seen.push(obj); - copies.push(ret); - } + @method resolve + */ + resolve: function(value) { + var deferred, promise; - return ret; - } + deferred = get(this, '_deferred'); + promise = deferred.promise; - /** - 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). + if (value === this) { + deferred.resolve(promise); + } else { + deferred.resolve(value); + } + }, - 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. + /** + Reject a Deferred object and call any `failCallbacks` with the given args. - @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 - } + @method reject + */ + reject: function(value) { + get(this, '_deferred').reject(value); + }, - if (Copyable && Copyable.detect(obj)) { - return obj.copy(deep); - } + _deferred: computed(function() { + Ember.deprecate('Usage of Ember.DeferredMixin or Ember.Deferred is deprecated.', this._suppressDeferredDeprecation, { url: 'http://emberjs.com/guides/deprecations/#toc_deprecate-ember-deferredmixin-and-ember-deferred' }); - return _copy(obj, deep, deep ? [] : null, deep ? [] : null); - } + return RSVP.defer('Ember: DeferredMixin - ' + this); + }) + }); }); -enifed("ember-runtime/core", - ["exports"], - function(__exports__) { +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 */ - /** - 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. + // .......................................................... + // HELPERS + // - ```javascript - Ember.isEqual('hello', 'hello'); // true - Ember.isEqual(1, 2); // false - Ember.isEqual([4, 2], [4, 2]); // false - ``` + 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"]; - @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); - } + var a_slice = Array.prototype.slice; - if (a instanceof Date && b instanceof Date) { - return a.getTime() === b.getTime(); + var contexts = []; + + function popCtx() { + return contexts.length === 0 ? {} : contexts.pop(); + } + + function pushCtx(ctx) { + contexts.push(ctx); + return null; + } + + function iter(key, value) { + var valueProvided = arguments.length === 2; + + function i(item) { + var cur = get(item, key); + return valueProvided ? value === cur : !!cur; } - 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 - */ + return i; + } - var Ember = __dependency1__["default"]; - // Ember.EXTEND_PROTOTYPES, Ember.assert - var expandProperties = __dependency2__["default"]; - var computed = __dependency3__.computed; - var observer = __dependency4__.observer; + /** + 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). - var a_slice = Array.prototype.slice; - var FunctionPrototype = Function.prototype; + 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. - if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) { + ## Writing Your Own Enumerable - /** - 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. + To make your own custom class enumerable, you need two items: - Computed properties allow you to treat a function like a property: + 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().` - ```javascript - MyApp.President = Ember.Object.extend({ - firstName: '', - lastName: '', + 2. You must implement `nextObject().` See documentation. - fullName: function() { - return this.get('firstName') + ' ' + this.get('lastName'); - }.property() // Call this flag to mark the function as a property - }); + 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. - var president = MyApp.President.create({ - firstName: 'Barack', - lastName: 'Obama' - }); + ## Using Ember Enumeration with Other Libraries - president.get('fullName'); // 'Barack Obama' - ``` + 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. - Treating a function like a property is useful because they can work with - bindings, just like any other property. + @class Enumerable + @namespace Ember + @since Ember 0.9 + */ + __exports__["default"] = Mixin.create({ - 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: + /** + Implement this method to make your class enumerable. - ```javascript - MyApp.President = Ember.Object.extend({ - firstName: '', - lastName: '', + 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. - fullName: function() { - return this.get('firstName') + ' ' + this.get('lastName'); + 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. - // Tell Ember.js that this computed property depends on firstName - // and lastName - }.property('firstName', 'lastName') - }); - ``` + 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. - 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. + 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. - See [Ember.ComputedProperty](/api/classes/Ember.ComputedProperty.html), [Ember.computed](/api/#method_computed). + The default implementation of this method simply looks up the index. + This works great on any Array-like objects. - @method property - @for Function + @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 */ - 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); - }; + nextObject: required(Function), /** - 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. + 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. - 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: + 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`. ```javascript - Ember.Object.extend({ - valueObserver: function() { - // Executes whenever the "value" property changes - }.observes('value') - }); - ``` - - In the future this method may become asynchronous. If you want to ensure - synchronous behavior, use `observesImmediately`. + var arr = ['a', 'b', 'c']; + arr.get('firstObject'); // 'a' - See `Ember.observer`. + var arr = []; + arr.get('firstObject'); // undefined + ``` - @method observes - @for Function + @property firstObject + @return {Object} the object or undefined */ - FunctionPrototype.observes = function() { - var length = arguments.length; - var args = new Array(length); - for (var x = 0; x < length; x++) { - args[x] = arguments[x]; + firstObject: computed('[]', function() { + if (get(this, 'length') === 0) { + return undefined; } - return observer.apply(this, args.concat(this)); - }; - /** - 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. + // handle generic enumerables + var context = popCtx(); + var ret = this.nextObject(0, null, context); - 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: + pushCtx(context); - ```javascript - Ember.Object.extend({ - valueObserver: function() { - // Executes immediately after the "value" property changes - }.observesImmediately('value') - }); - ``` + return ret; + }), - In the future, `observes` may become asynchronous. In this event, - `observesImmediately` will maintain the synchronous behavior. + /** + 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`. - See `Ember.immediateObserver`. + ```javascript + var arr = ['a', 'b', 'c']; + arr.get('lastObject'); // 'c' - @method observesImmediately - @for Function + var arr = []; + arr.get('lastObject'); // undefined + ``` + + @property lastObject + @return {Object} the last object or undefined */ - FunctionPrototype.observesImmediately = function () { - for (var i = 0, l = arguments.length; i < l; i++) { - var arg = arguments[i]; - Ember.assert('Immediate observers must observe internal properties only, ' + - 'not properties on other objects.', arg.indexOf('.') === -1); + lastObject: computed('[]', function() { + var len = get(this, 'length'); + + if (len === 0) { + return undefined; } - // observes handles property expansion - return this.observes.apply(this, arguments); - }; + var context = popCtx(); + var idx = 0; + var last = null; + var cur; + + do { + last = cur; + cur = this.nextObject(idx++, last, context); + } while (cur !== undefined); + + pushCtx(context); + + return last; + }), /** - 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. + 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. - 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 arr = ['a', 'b', 'c']; + + 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): ```javascript - Ember.Object.extend({ - valueObserver: function() { - // Executes whenever the "value" property is about to change - }.observesBefore('value') - }); + function(item, index, enumerable); ``` - See `Ember.beforeObserver`. + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. - @method observesBefore - @for Function + 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 forEach + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @return {Object} receiver */ - FunctionPrototype.observesBefore = function () { - var watched = []; - var addWatchedProperty = function (obs) { - watched.push(obs); - }; + forEach: function(callback, target) { + if (typeof callback !== 'function') { + throw new TypeError(); + } - for (var i = 0, l = arguments.length; i < l; ++i) { - expandProperties(arguments[i], addWatchedProperty); + var context = popCtx(); + var len = get(this, 'length'); + var last = null; + + if (target === undefined) { + target = null; } - this.__ember_observesBefore__ = watched; + for(var idx = 0; idx < len; idx++) { + var next = this.nextObject(idx, last, context) ; + callback.call(target, next, idx, this); + last = next ; + } - return this; - }; + last = null ; + context = pushCtx(context); + + return this ; + }, /** - The `on` extension of Javascript's Function prototype is available - when `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Function` is - true, which is the default. + Alias for `mapBy` - You can listen for events simply by adding the `on` call to the end of - your method declarations in classes or mixins that you write. For example: + @method getEach + @param {String} key name of the property + @return {Array} The mapped array. + */ + getEach: function(key) { + return this.mapBy(key); + }, - ```javascript - Ember.Mixin.create({ - doSomethingWithElement: function() { - // Executes whenever the "didInsertElement" event fires - }.on('didInsertElement') + /** + 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 setEach + @param {String} key The key to set + @param {Object} value The object to set + @return {Object} receiver + */ + setEach: function(key, value) { + return this.forEach(function(item) { + set(item, key, value); }); - ``` + }, - See `Ember.on`. + /** + 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. - @method on - @for Function - */ - FunctionPrototype.on = function () { - var events = a_slice.call(arguments); - this.__ember_listens__ = events; + The callback method you provide should have the following signature (all + parameters are optional): - return this; - }; - } - }); -enifed("ember-runtime/ext/rsvp", - ["ember-metal/core","ember-metal/logger","ember-metal/run_loop","rsvp","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - /* globals RSVP:true */ + ```javascript + function(item, index, enumerable); + ``` - var Ember = __dependency1__["default"]; - var Logger = __dependency2__["default"]; - var run = __dependency3__["default"]; + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. - // this is technically incorrect (per @wycats) - // it should be `import * as RSVP from 'rsvp';` but - // Esprima does not support this syntax yet (and neither does - // es6-module-transpiler 0.4.0 - 0.6.2). - var RSVP = __dependency4__; + It should return the mapped value. - var testModuleName = 'ember-testing/test'; - var Test; + 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 asyncStart = function() { - if (Ember.Test && Ember.Test.adapter) { - Ember.Test.adapter.asyncStart(); - } - }; + @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(); - var asyncEnd = function() { - if (Ember.Test && Ember.Test.adapter) { - Ember.Test.adapter.asyncEnd(); - } - }; + this.forEach(function(x, idx, i) { + ret[idx] = callback.call(target, x, idx,i); + }); - RSVP.configure('async', function(callback, promise) { - var async = !run.currentRunLoop; + return ret ; + }, - if (Ember.testing && async) { asyncStart(); } + /** + Similar to map, this specialized function returns the value of the named + property on all items in the enumeration. - run.backburner.schedule('actions', function(){ - if (Ember.testing && async) { asyncEnd(); } - callback(promise); - }); - }); + @method mapBy + @param {String} key name of the property + @return {Array} The mapped array. + */ + mapBy: function(key) { + return this.map(function(next) { + return get(next, key); + }); + }, - RSVP.Promise.prototype.fail = function(callback, label){ - Ember.deprecate('RSVP.Promise.fail has been renamed as RSVP.Promise.catch'); - return this['catch'](callback, label); - }; + /** + Similar to map, this specialized function returns the value of the named + property on all items in the enumeration. - RSVP.onerrorDefault = function (error) { - if (error && error.name !== 'TransitionAborted') { - if (Ember.testing) { - // ES6TODO: remove when possible - if (!Test && Ember.__loader.registry[testModuleName]) { - Test = requireModule(testModuleName)['default']; - } + @method mapProperty + @param {String} key name of the property + @return {Array} The mapped array. + @deprecated Use `mapBy` instead + */ - 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); - Ember.assert(error, false); - } - } - }; + mapProperty: aliasMethod('mapBy'), - RSVP.on('error', RSVP.onerrorDefault); + /** + 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. - __exports__["default"] = RSVP; - }); -enifed("ember-runtime/ext/string", - ["ember-metal/core","ember-runtime/system/string"], - function(__dependency1__, __dependency2__) { - "use strict"; - /** - @module ember - @submodule ember-runtime - */ + The callback method you provide should have the following signature (all + parameters are optional): - var Ember = __dependency1__["default"]; - // Ember.EXTEND_PROTOTYPES, Ember.assert, Ember.FEATURES - var fmt = __dependency2__.fmt; - var w = __dependency2__.w; - var loc = __dependency2__.loc; - var camelize = __dependency2__.camelize; - var decamelize = __dependency2__.decamelize; - var dasherize = __dependency2__.dasherize; - var underscore = __dependency2__.underscore; - var capitalize = __dependency2__.capitalize; - var classify = __dependency2__.classify; + ```javascript + function(item, index, enumerable); + ``` - var StringPrototype = String.prototype; + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. - if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { + It should return `true` to include the item in the results, `false` + otherwise. - /** - See [Ember.String.fmt](/api/classes/Ember.String.html#method_fmt). + 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 fmt - @for String + @method filter + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @return {Array} A filtered array. */ - StringPrototype.fmt = function () { - return fmt(this, arguments); - }; + filter: function(callback, target) { + var ret = Ember.A(); - /** - See [Ember.String.w](/api/classes/Ember.String.html#method_w). + this.forEach(function(x, idx, i) { + if (callback.call(target, x, idx, i)) { + ret.push(x); + } + }); - @method w - @for String - */ - StringPrototype.w = function () { - return w(this); - }; + return ret ; + }, /** - See [Ember.String.loc](/api/classes/Ember.String.html#method_loc). + 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(). - @method loc - @for String - */ - StringPrototype.loc = function () { - return loc(this, arguments); - }; + The callback method you provide should have the following signature (all + parameters are optional): - /** - See [Ember.String.camelize](/api/classes/Ember.String.html#method_camelize). + ```javascript + function(item, index, enumerable); + ``` - @method camelize - @for String - */ - StringPrototype.camelize = function () { - return camelize(this); - }; + - *item* is the current item in the iteration. + - *index* is the current index in the iteration + - *enumerable* is the enumerable object itself. - /** - See [Ember.String.decamelize](/api/classes/Ember.String.html#method_decamelize). + It should return the a falsey value to include the item in the results. - @method decamelize - @for String - */ - StringPrototype.decamelize = function () { - return decamelize(this); - }; + 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 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)); + }); + }, /** - See [Ember.String.dasherize](/api/classes/Ember.String.html#method_dasherize). + 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`. - @method dasherize - @for String + @method filterBy + @param {String} key the property to test + @param {*} [value] optional value to test against. + @return {Array} filtered array */ - StringPrototype.dasherize = function () { - return dasherize(this); - }; + filterBy: function(key, value) { + return this.filter(apply(this, iter, arguments)); + }, /** - See [Ember.String.underscore](/api/classes/Ember.String.html#method_underscore). + 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`. - @method underscore - @for String + @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 */ - StringPrototype.underscore = function () { - return underscore(this); - }; + filterProperty: aliasMethod('filterBy'), /** - See [Ember.String.classify](/api/classes/Ember.String.html#method_classify). + 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 classify - @for String + @method rejectBy + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Array} rejected array */ - StringPrototype.classify = function () { - return classify(this); - }; + rejectBy: function(key, value) { + var exactValue = function(item) { + return get(item, key) === value; + }; - /** - See [Ember.String.capitalize](/api/classes/Ember.String.html#method_capitalize). + var hasValue = function(item) { + return !!get(item, key); + }; - @method capitalize - @for String - */ - StringPrototype.capitalize = function () { - return capitalize(this); - }; - } - }); -enifed("ember-runtime/inject", - ["ember-metal/core","ember-metal/enumerable_utils","ember-metal/injected_property","ember-metal/keys","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // Ember.assert - var indexOf = __dependency2__.indexOf; - var InjectedProperty = __dependency3__["default"]; - var keys = __dependency4__["default"]; + var use = (arguments.length === 2 ? exactValue : hasValue); - /** - Namespace for injection helper methods. + return this.reject(use); + }, - @class inject - @namespace Ember + /** + 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 rejectProperty + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Array} rejected array + @deprecated Use `rejectBy` instead */ - function inject() { - Ember.assert("Injected properties must be created through helpers, see `" + - keys(inject).join("`, `") + "`"); - } + rejectProperty: aliasMethod('rejectBy'), - // Dictionary of injection validations by type, added to by `createInjectionHelper` - var typeValidators = {}; + /** + 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. - /** - 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. + The callback method you provide should have the following signature (all + parameters are optional): - @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 createInjectionHelper(type, validator) { - typeValidators[type] = validator; + ```javascript + function(item, index, enumerable); + ``` - inject[type] = function(name) { - return new InjectedProperty(type, name); - }; - } + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. - __exports__.createInjectionHelper = createInjectionHelper;/** - Validation function intended to be invoked at when extending a factory with - injected properties. Runs per-type validation functions once for each injected - type encountered. + It should return the `true` to include the item in the results, `false` + otherwise. - Note that this currently modifies the mixin themselves, which is technically - dubious but is practically of little consequence. This may change in the - future. + 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 validatePropertyInjections - @namespace Ember - @param {Object} factory The factory object being extended - @param {Object} props A hash of properties to be added to the factory - */ - function validatePropertyInjections(factory, props) { - var types = []; - var key, desc, validator, i, l; + @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'); - for (key in props) { - desc = props[key]; - if (desc instanceof InjectedProperty && indexOf(types, desc.type) === -1) { - types.push(desc.type); + if (target === undefined) { + target = null; } - } - if (types.length) { - for (i = 0, l = types.length; i < l; i++) { - validator = typeValidators[types[i]]; + var context = popCtx(); + var found = false; + var last = null; + var next, ret; - if (typeof validator === 'function') { - validator(factory); + for(var idx = 0; idx < len && !found; idx++) { + next = this.nextObject(idx, last, context); + + if (found = callback.call(target, next, idx, this)) { + ret = next; } - } - } - return true; - } + last = next; + } - __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 - */ + next = last = null; + context = pushCtx(context); - 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 ret; + }, - function contentPropertyWillChange(content, contentKey) { - var key = contentKey.slice(8); // remove "content." - if (key in this) { return; } // if shadowed in proxy - propertyWillChange(this, key); - } + /** + 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`. - function contentPropertyDidChange(content, contentKey) { - var key = contentKey.slice(8); // remove "content." - if (key in this) { return; } // if shadowed in proxy - propertyDidChange(this, key); - } + This method works much like the more generic `find()` method. - /** - `Ember.ProxyMixin` forwards all properties not defined by the proxy itself - to a proxied `content` object. See Ember.ObjectProxy for more details. + @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)); + }, - @class ProxyMixin - @namespace Ember - */ - __exports__["default"] = Mixin.create({ /** - The object whose properties will be forwarded. + 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`. - @property content - @type Ember.Object - @default null + This method works much like the more generic `find()` method. + + @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 */ - content: null, - _contentDidChange: observer('content', function() { - Ember.assert("Can't set Proxy's content to itself", get(this, 'content') !== this); - }), + findProperty: aliasMethod('findBy'), - isTruthy: computed.bool('content'), + /** + Returns `true` if the passed function returns true for every item in the + enumeration. This corresponds with the `every()` method in JavaScript 1.6. - _debugContainerKey: null, + The callback method you provide should have the following signature (all + parameters are optional): - willWatchProperty: function (key) { - var contentKey = 'content.' + key; - addBeforeObserver(this, contentKey, null, contentPropertyWillChange); - addObserver(this, contentKey, null, contentPropertyDidChange); - }, + ```javascript + function(item, index, enumerable); + ``` - didUnwatchProperty: function (key) { - var contentKey = 'content.' + key; - removeBeforeObserver(this, contentKey, null, contentPropertyWillChange); - removeObserver(this, contentKey, null, contentPropertyDidChange); - }, + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. - unknownProperty: function (key) { - var content = get(this, 'content'); - if (content) { - return get(content, key); - } - }, + It should return the `true` or `false`. - 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; + 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. + + Example Usage: + + ```javascript + if (people.every(isEngineer)) { + Paychecks.addBigBonus(); } + ``` - 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); - } + @method every + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @return {Boolean} + */ + every: function(callback, target) { + return !this.find(function(x, idx, i) { + return !callback.call(target, x, idx, i); + }); + }, - }); - }); -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"; - /** - @module ember - @submodule ember-runtime - */ - var merge = __dependency1__["default"]; - var Mixin = __dependency2__.Mixin; - var get = __dependency3__.get; - var typeOf = __dependency4__.typeOf; + /** + @method everyBy + @param {String} key the property to test + @param {String} [value] optional value to test against. + @deprecated Use `isEvery` instead + @return {Boolean} + */ + everyBy: aliasMethod('isEvery'), - /** - 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. + /** + @method everyProperty + @param {String} key the property to test + @param {String} [value] optional value to test against. + @deprecated Use `isEvery` instead + @return {Boolean} + */ + everyProperty: aliasMethod('isEvery'), - `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.) + /** + 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. - @class ActionHandler - @namespace Ember - */ - var ActionHandler = Mixin.create({ - mergedProperties: ['_actions'], + @method isEvery + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Boolean} + @since 1.3.0 + */ + isEvery: function(key, value) { + return this.every(apply(this, iter, arguments)); + }, /** - The collection of functions, keyed by name, available on this - `ActionHandler` as action targets. + Returns `true` if the passed function returns true for any item in the + enumeration. This corresponds with the `some()` method in JavaScript 1.6. - These functions will be invoked when a matching `{{action}}` is triggered - from within a template and the application's current route is this route. + The callback method you provide should have the following signature (all + parameters are optional): - Actions can also be invoked from other parts of your application - via `ActionHandler#send`. + ```javascript + function(item, index, enumerable); + ``` - 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.: + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. - ```js - App.CanDisplayBanner = Ember.Mixin.create({ - actions: { - displayBanner: function(msg) { - // ... - } - } - }); + It should return the `true` to include the item in the results, `false` + otherwise. - App.WelcomeRoute = Ember.Route.extend(App.CanDisplayBanner, { - actions: { - playMusic: function() { - // ... - } - } - }); + 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. - // `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'); + Usage Example: + + ```javascript + if (people.any(isManager)) { + Paychecks.addBiggerBonus(); + } ``` - Within a Controller, Route, View or Component's action handler, - the value of the `this` context is the Controller, Route, View or - Component object: + @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 + */ + any: function(callback, target) { + var len = get(this, 'length'); + var context = popCtx(); + var found = false; + var last = null; + var next, idx; - ```js - App.SongRoute = Ember.Route.extend({ - actions: { - myAction: function() { - this.controllerFor("song"); - this.transitionTo("other.route"); - ... - } - } - }); - ``` + if (target === undefined) { + target = null; + } - 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: + for (idx = 0; idx < len && !found; idx++) { + next = this.nextObject(idx, last, context); + found = callback.call(target, next, idx, this); + last = next; + } - Take for example the following routes: + next = last = null; + context = pushCtx(context); + return found; + }, - ```js - App.DebugRoute = Ember.Mixin.create({ - actions: { - debugRouteInformation: function() { - console.debug("trololo"); - } - } - }); + /** + Returns `true` if the passed function returns true for any item in the + enumeration. This corresponds with the `some()` method in JavaScript 1.6. - App.AnnoyingDebugRoute = Ember.Route.extend(App.DebugRoute, { - actions: { - debugRouteInformation: function() { - // also call the debugRouteInformation of mixed in App.DebugRoute - this._super(); + The callback method you provide should have the following signature (all + parameters are optional): - // show additional annoyance - window.alert(...); - } - } - }); + ```javascript + function(item, index, enumerable); ``` - ## Bubbling - - 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: + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. - ```js - App.Router.map(function() { - this.resource("album", function() { - this.route("song"); - }); - }); + It should return the `true` to include the item in the results, `false` + otherwise. - App.AlbumRoute = Ember.Route.extend({ - actions: { - startPlaying: function() { - } - } - }); + 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. - App.AlbumSongRoute = Ember.Route.extend({ - actions: { - startPlaying: function() { - // ... + Usage Example: - if (actionShouldAlsoBeTriggeredOnParentRoute) { - return true; - } - } - } - }); + ```javascript + if (people.some(isManager)) { + Paychecks.addBiggerBonus(); + } ``` - @property actions - @type Hash - @default null + @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'), /** - 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. + 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. - @private - @method willMergeMixin + @method isAny + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Boolean} + @since 1.3.0 */ - willMergeMixin: function(props) { - - var hashName; - - if (!props._actions) { - Ember.assert("'actions' should not be a function", typeof(props.actions) !== 'function'); - - 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 (hashName) { - props._actions = merge(props._actions || {}, props[hashName]); - } - - delete props[hashName]; - } + isAny: function(key, value) { + return this.any(apply(this, iter, arguments)); }, /** - Triggers a named action on the `ActionHandler`. Any parameters - supplied after the `actionName` string will be passed as arguments - to the action target function. + @method anyBy + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Boolean} + @deprecated Use `isAny` instead + */ + anyBy: aliasMethod('isAny'), - 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`. + /** + @method someProperty + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Boolean} + @deprecated Use `isAny` instead + */ + someProperty: aliasMethod('isAny'), - Example + /** + 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. - ```js - App.WelcomeRoute = Ember.Route.extend({ - actions: { - playTheme: function() { - this.send('playMusic', 'theme.mp3'); - }, - playMusic: function(track) { - // ... - } - } - }); - ``` + The callback method you provide should have the following signature (all + parameters are optional): - @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; + ```javascript + function(previousValue, item, index, enumerable); + ``` - if (this._actions && this._actions[actionName]) { - if (this._actions[actionName].apply(this, args) === true) { - // handler returned true, so this action will bubble - } else { - return; - } - } + - `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. - 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); - } - } - }); + Return the new cumulative value. - __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 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. - // .......................................................... - // HELPERS - // - var Ember = __dependency1__["default"]; - // ES6TODO: Ember.A + 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. - 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; + @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(); + } - function arrayObserversHelper(obj, target, opts, operation, notify) { - var willChange = (opts && opts.willChange) || 'arrayWillChange'; - var didChange = (opts && opts.didChange) || 'arrayDidChange'; - var hasObservers = get(obj, 'hasArrayObservers'); + var ret = initialValue; - if (hasObservers === notify) { - propertyWillChange(obj, 'hasArrayObservers'); - } + this.forEach(function(item, i) { + ret = callback(ret, item, i, this, reducerProperty); + }, this); - operation(obj, '@array:before', target, willChange); - operation(obj, '@array:change', target, didChange); + return ret; + }, - if (hasObservers === notify) { - propertyDidChange(obj, 'hasArrayObservers'); - } + /** + Invokes the named method on every object in the receiver that + implements it. This method corresponds to the implementation in + Prototype 1.6. - return obj; - } + @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. + */ + invoke: function(methodName) { + var ret = Ember.A(); + var args; - // .......................................................... - // 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. + if (arguments.length > 1) { + args = a_slice.call(arguments, 1); + } - 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. + this.forEach(function(x, idx) { + var method = x && x[methodName]; - 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 ('function' === typeof method) { + ret[idx] = args ? apply(x, method, args) : x[methodName](); + } + }, this); - 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. + return ret; + }, - 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.[]')`. + /** + Simply converts the enumerable into a genuine array. The order is not + guaranteed. Corresponds to the method implemented by Prototype. - To support `Ember.Array` in your own class, you must override two - primitives to use it: `replace()` and `objectAt()`. + @method toArray + @return {Array} the enumerable as an array. + */ + toArray: function() { + var ret = Ember.A(); - Note that the Ember.Array mixin also incorporates the `Ember.Enumerable` - mixin. All `Ember.Array`-like objects are also enumerable. + this.forEach(function(o, idx) { + ret[idx] = o; + }); - @class Array - @namespace Ember - @uses Ember.Enumerable - @since Ember 0.9.0 - */ - __exports__["default"] = Mixin.create(Enumerable, { + return ret; + }, /** - Your array must support the `length` property. Your replace methods should - set this property whenever it changes. + Returns a copy of the array with all `null` and `undefined` elements removed. - @property {Number} length + ```javascript + var arr = ['a', null, 'c', undefined]; + arr.compact(); // ['a', 'c'] + ``` + + @method compact + @return {Array} the array without null and undefined elements. */ - length: required(), + compact: function() { + return this.filter(function(value) { + return value != null; + }); + }, /** - Returns the object at the given `index`. If the given `index` is negative - or is greater or equal than the array length, returns `undefined`. - - 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. + 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. ```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 + var arr = ['a', 'b', 'a', 'c']; + arr.without('a'); // ['b', 'c'] ``` - @method objectAt - @param {Number} idx The index of the item to return. - @return {*} item at index or undefined + @method without + @param {Object} value + @return {Ember.Enumerable} */ - objectAt: function(idx) { - if (idx < 0 || idx >= get(this, 'length')) { - return undefined; + without: function(value) { + if (!this.contains(value)) { + return this; // nothing to do } - return get(this, idx); + var ret = Ember.A(); + + this.forEach(function(k) { + if (k !== value) { + ret[ret.length] = k; + } + }); + + return ret; }, /** - This returns the objects at the specified indexes, using `objectAt`. + Returns a new enumerable that contains only unique values. The default + implementation returns an array regardless of the receiver type. ```javascript - var arr = ['a', 'b', 'c', 'd']; - - arr.objectsAt([0, 1, 2]); // ['a', 'b', 'c'] - arr.objectsAt([2, 3, 4]); // ['c', 'd', undefined] + var arr = ['a', 'a', 'b', 'b']; + arr.uniq(); // ['a', 'b'] ``` - @method objectsAt - @param {Array} indexes An array of indexes of items to return. - @return {Array} - */ - objectsAt: function(indexes) { - var self = this; + This only works on primitive data types, e.g. Strings, Numbers, etc. - return map(indexes, function(idx) { - return self.objectAt(idx); + @method uniq + @return {Ember.Enumerable} + */ + uniq: function() { + var ret = Ember.A(); + + this.forEach(function(k) { + if (indexOf(ret, k) < 0) { + ret.push(k); + } }); - }, - // overrides Ember.Enumerable version - nextObject: function(idx) { - return this.objectAt(idx); + return ret; }, /** - 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. + This property will trigger anytime the enumerable's content changes. + You can observe this property to be notified of changes to the enumerables + content. - This property overrides the default property defined in `Ember.Enumerable`. + For plain enumerables, this property is read only. `Array` overrides + this method. @property [] + @type Array @return this */ '[]': computed(function(key, value) { - if (value !== undefined) { - this.replace(0, get(this, 'length'), value); - } - return this; }), - firstObject: computed(function() { - return this.objectAt(0); - }), - - lastObject: computed(function() { - return this.objectAt(get(this, 'length') - 1); - }), - - // optimized version from Enumerable - contains: function(obj) { - return this.indexOf(obj) >= 0; - }, + // .......................................................... + // ENUMERABLE OBSERVERS + // - // 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. - - ```javascript - var arr = ['red', 'green', 'blue']; - - arr.slice(0); // ['red', 'green', 'blue'] - arr.slice(0, 2); // ['red', 'green'] - arr.slice(1, 100); // ['green', 'blue'] - ``` + Registers an enumerable observer. Must implement `Ember.EnumerableObserver` + mixin. - @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 + @method addEnumerableObserver + @param {Object} target + @param {Hash} [opts] + @return this */ - 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; - } + addEnumerableObserver: function(target, opts) { + var willChange = (opts && opts.willChange) || 'enumerableWillChange'; + var didChange = (opts && opts.didChange) || 'enumerableDidChange'; + var hasObservers = get(this, 'hasEnumerableObservers'); - if (beginIndex < 0) { - beginIndex = length + beginIndex; + if (!hasObservers) { + propertyWillChange(this, 'hasEnumerableObservers'); } - if (endIndex < 0) { - endIndex = length + endIndex; - } + addListener(this, '@enumerable:before', target, willChange); + addListener(this, '@enumerable:change', target, didChange); - while (beginIndex < endIndex) { - ret[ret.length] = this.objectAt(beginIndex++); + if (!hasObservers) { + propertyDidChange(this, 'hasEnumerableObservers'); } - return ret; + return this; }, /** - 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. - - ```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 - ``` + Removes a registered enumerable observer. - @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 + @method removeEnumerableObserver + @param {Object} target + @param {Hash} [opts] + @return this */ - indexOf: function(object, startAt) { - var len = get(this, 'length'); - var idx; + removeEnumerableObserver: function(target, opts) { + var willChange = (opts && opts.willChange) || 'enumerableWillChange'; + var didChange = (opts && opts.didChange) || 'enumerableDidChange'; + var hasObservers = get(this, 'hasEnumerableObservers'); - if (startAt === undefined) { - startAt = 0; + if (hasObservers) { + propertyWillChange(this, 'hasEnumerableObservers'); } - if (startAt < 0) { - startAt += len; - } + removeListener(this, '@enumerable:before', target, willChange); + removeListener(this, '@enumerable:change', target, didChange); - for (idx = startAt; idx < len; idx++) { - if (this.objectAt(idx) === object) { - return idx; - } + if (hasObservers) { + propertyDidChange(this, 'hasEnumerableObservers'); } - return -1; + return this; }, /** - 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. + Becomes true whenever the array currently has observers watching changes + on the array. - ```javascript - var arr = ['a', 'b', 'c', 'd', 'a']; + @property hasEnumerableObservers + @type Boolean + */ + hasEnumerableObservers: computed(function() { + return hasListeners(this, '@enumerable:change') || hasListeners(this, '@enumerable:before'); + }), - 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 - ``` - @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 + /** + 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 */ - lastIndexOf: function(object, startAt) { - var len = get(this, 'length'); - var idx; + enumerableContentWillChange: function(removing, adding) { + var removeCnt, addCnt, hasDelta; - if (startAt === undefined || startAt >= len) { - startAt = len-1; + if ('number' === typeof removing) { + removeCnt = removing; + } else if (removing) { + removeCnt = get(removing, 'length'); + } else { + removeCnt = removing = -1; } - if (startAt < 0) { - startAt += len; + if ('number' === typeof adding) { + addCnt = adding; + } else if (adding) { + addCnt = get(adding,'length'); + } else { + addCnt = adding = -1; } - for (idx = startAt; idx >= 0; idx--) { - if (this.objectAt(idx) === object) { - return idx; - } + hasDelta = addCnt < 0 || removeCnt < 0 || addCnt - removeCnt !== 0; + + if (removing === -1) { + removing = null; } - return -1; - }, + if (adding === -1) { + adding = null; + } - // .......................................................... - // ARRAY OBSERVERS - // + propertyWillChange(this, '[]'); - /** - Adds an array observer to the receiving array. The array observer object - normally must implement two methods: + if (hasDelta) { + propertyWillChange(this, 'length'); + } - * `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. + sendEvent(this, '@enumerable:before', [this, removing, adding]); - 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. + return this; + }, - 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. + /** + 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 addArrayObserver - @param {Object} target The observer object. - @param {Hash} opts Optional hash of configuration options including - `willChange` and `didChange` option. - @return {Ember.Array} receiver + @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 */ + enumerableContentDidChange: function(removing, adding) { + var removeCnt, addCnt, hasDelta; - addArrayObserver: function(target, opts) { - return arrayObserversHelper(this, target, opts, addListener, false); - }, + if ('number' === typeof removing) { + removeCnt = removing; + } else if (removing) { + removeCnt = get(removing, 'length'); + } else { + removeCnt = removing = -1; + } - /** - 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. + if ('number' === typeof adding) { + addCnt = adding; + } else if (adding) { + addCnt = get(adding, 'length'); + } else { + addCnt = adding = -1; + } - @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); + hasDelta = addCnt < 0 || removeCnt < 0 || addCnt - removeCnt !== 0; + + if (removing === -1) { + removing = null; + } + + if (adding === -1) { + adding = null; + } + + sendEvent(this, '@enumerable:change', [this, removing, adding]); + + if (hasDelta) { + propertyDidChange(this, 'length'); + } + + propertyDidChange(this, '[]'); + + return this ; }, /** - Becomes true whenever the array currently has observers watching changes - on the array. + Converts the enumerable into an array and sorts by the keys + specified in the argument. - @property {Boolean} hasArrayObservers - */ - hasArrayObservers: computed(function() { - return hasListeners(this, '@array:change') || hasListeners(this, '@array:before'); - }), + You may provide multiple arguments to sort by multiple properties. - /** - 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 sortBy + @param {String} property name(s) to sort on + @return {Array} The sorted array. + @since 1.2.0 + */ + sortBy: function() { + var sortKeys = arguments; - @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; + 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); - // if no args are passed assume everything changes - if (startIdx === undefined) { - startIdx = 0; - removeAmt = addAmt = -1; - } else { - if (removeAmt === undefined) { - removeAmt = -1; + 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; + + /** + @module ember + @submodule ember-runtime + */ + + /** + This mixin allows for Ember objects to subscribe to and emit events. - if (addAmt === undefined) { - addAmt = -1; - } + ```javascript + App.Person = Ember.Object.extend(Ember.Evented, { + greet: function() { + // ... + this.trigger('greet'); } + }); - // Make sure the @each proxy is set up if anyone is observing @each - if (isWatching(this, '@each')) { - get(this, '@each'); - } + var person = App.Person.create(); - sendEvent(this, '@array:before', [this, startIdx, removeAmt, addAmt]); + person.on('greet', function() { + console.log('Our person has greeted'); + }); - if (startIdx >= 0 && removeAmt >= 0 && get(this, 'hasEnumerableObservers')) { - removing = []; - lim = startIdx + removeAmt; + person.greet(); - for (var idx = startIdx; idx < lim; idx++) { - removing.push(this.objectAt(idx)); - } - } else { - removing = removeAmt; - } + // outputs: 'Our person has greeted' + ``` - this.enumerableContentWillChange(removing, addAmt); + You can also chain multiple event subscriptions: - return this; - }, + ```javascript + person.on('greet', function() { + console.log('Our person has greeted'); + }).one('greet', function() { + console.log('Offer one-time special'); + }).off('event', this, forgetThis); + ``` + + @class Evented + @namespace Ember + */ + __exports__["default"] = Mixin.create({ /** - 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. + Subscribes to a named event with given function. - @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 + ```javascript + person.on('didLoad', function() { + // fired once the person has loaded + }); + ``` + + 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. + + @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 */ - arrayContentDidChange: function(startIdx, removeAmt, addAmt) { - var adding, lim; + on: function(name, target, method) { + addListener(this, name, target, method); + return this; + }, - // if no args are passed assume everything changes - if (startIdx === undefined) { - startIdx = 0; - removeAmt = addAmt = -1; - } else { - if (removeAmt === undefined) { - removeAmt = -1; - } + /** + 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. - if (addAmt === undefined) { - addAmt = -1; - } + 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. + + @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; } - if (startIdx >= 0 && addAmt >= 0 && get(this, 'hasEnumerableObservers')) { - adding = []; - lim = startIdx + addAmt; + addListener(this, name, target, method, true); + return this; + }, - for (var idx = startIdx; idx < lim; idx++) { - adding.push(this.objectAt(idx)); - } - } else { - adding = addAmt; - } + /** + Triggers a named event for the object. Any additional arguments + will be passed as parameters to the functions that are subscribed to the + event. - this.enumerableContentDidChange(removeAmt, adding); - sendEvent(this, '@array:change', [this, startIdx, removeAmt, addAmt]); + ```javascript + person.on('didEat', function(food) { + console.log('person ate some ' + food); + }); - var length = get(this, 'length'); - var cachedFirst = cacheFor(this, 'firstObject'); - var cachedLast = cacheFor(this, 'lastObject'); + person.trigger('didEat', 'broccoli'); - if (this.objectAt(0) !== cachedFirst) { - propertyWillChange(this, 'firstObject'); - propertyDidChange(this, 'firstObject'); - } + // 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); - if (this.objectAt(length-1) !== cachedLast) { - propertyWillChange(this, 'lastObject'); - propertyDidChange(this, 'lastObject'); + for (var i = 1; i < length; i++) { + args[i - 1] = arguments[i]; } - return this; + sendEvent(this, name, args); }, - // .......................................................... - // ENUMERATED PROPERTIES - // - /** - 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. - - If you merely want to watch for any items being added or removed to the array, - use the `[]` property instead of `@each`. + Cancels subscription for given name, target, and method. - @property @each + @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 */ - '@each': computed(function() { - if (!this.__each) { - // ES6TODO: GRRRRR - var EachProxy = requireModule('ember-runtime/system/each_proxy')['EachProxy']; + off: function(name, target, method) { + removeListener(this, name, target, method); + return this; + }, - this.__each = new EachProxy(this); - } + /** + Checks to see if object has any subscriptions for named event. - return this.__each; - }) + @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/comparable", - ["ember-metal/mixin","exports"], - function(__dependency1__, __exports__) { +enifed("ember-runtime/mixins/freezable", + ["ember-metal/mixin","ember-metal/property_get","ember-metal/property_set","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; - var Mixin = __dependency1__.Mixin; - var required = __dependency1__.required; - /** @module ember @submodule ember-runtime */ + var Mixin = __dependency1__.Mixin; + var get = __dependency2__.get; + var set = __dependency3__.set; + /** - Implements some standard methods for comparing objects. Add this mixin to - any class you create that can compare its instances. + 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. - You should implement the `compare()` method. + ## Enforcement - @class Comparable + 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. + + 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 Usage + + The example below shows a simple object that implement the `Ember.Freezable` + protocol. + + ```javascript + Contact = Ember.Object.extend(Ember.Freezable, { + firstName: null, + lastName: null, + + // 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; + } + + }); + + c = Contact.create({ firstName: "John", lastName: "Doe" }); + c.swapNames(); // returns c + c.freeze(); + c.swapNames(); // EXCEPTION + ``` + + ## Copying + + 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. + + @class Freezable @namespace Ember @since Ember 0.9 */ - __exports__["default"] = Mixin.create({ + var Freezable = Mixin.create({ /** - Override to return the result of the comparison of the two parameters. The - compare method should return: + Set to `true` when the object is frozen. Use this property to detect + whether your object is frozen or not. - - `-1` if `a < b` - - `0` if `a == b` - - `1` if `a > b` + @property isFrozen + @type Boolean + */ + isFrozen: false, - Default implementation raises an exception. + /** + Freezes the object. Once this method has been called the object should + no longer allow any properties to be edited. - @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 + @method freeze + @return {Object} receiver */ - compare: required(Function) + freeze: function() { + if (get(this, 'isFrozen')) return this; + set(this, 'isFrozen', true); + return this; + } + }); + __exports__.Freezable = Freezable; + var FROZEN_ERROR = "Frozen object cannot be modified."; + __exports__.FROZEN_ERROR = FROZEN_ERROR; }); -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__) { +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"; - var Mixin = __dependency1__.Mixin; - var computed = __dependency2__.computed; - var ActionHandler = __dependency3__["default"]; - var ControllerContentModelAliasDeprecation = __dependency4__["default"]; + /** + @module ember + @submodule ember-runtime + */ + + + // require('ember-runtime/mixins/array'); + // require('ember-runtime/mixins/mutable_enumerable'); + + // .......................................................... + // CONSTANTS + // + + var OUT_OF_RANGE_EXCEPTION = "Index out of range"; + var EMPTY = []; + + // .......................................................... + // HELPERS + // + var get = __dependency1__.get; + var isArray = __dependency2__.isArray; + var EmberError = __dependency3__["default"]; + var Mixin = __dependency4__.Mixin; + var required = __dependency4__.required; + var EmberArray = __dependency5__["default"]; + var MutableEnumerable = __dependency6__["default"]; + var Enumerable = __dependency7__["default"]; /** - `Ember.ControllerMixin` provides a standard interface for all classes that - compose Ember's controller layer: `Ember.Controller`, - `Ember.ArrayController`, and `Ember.ObjectController`. + 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. - @class ControllerMixin + 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. + + + 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. + + @class MutableArray @namespace Ember - @uses Ember.ActionHandler + @uses Ember.Array + @uses Ember.MutableEnumerable */ - __exports__["default"] = Mixin.create(ActionHandler, ControllerContentModelAliasDeprecation, { - /* ducktype as a controller */ - isController: true, + __exports__["default"] = Mixin.create(EmberArray, MutableEnumerable, { /** - The object to which actions from the view should be sent. - - For example, when a Handlebars template uses the `{{action}}` helper, - it will attempt to send the action to the view's controller's `target`. + __Required.__ You must implement this method to apply this mixin. - 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. + 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()` - @property target - @default null + @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* */ - target: null, + replace: required(), - container: null, + /** + Remove all elements from the array. This is useful if you + want to reuse an existing array without having to recreate it. - parentController: null, + ```javascript + var colors = ["red", "green", "blue"]; + color.length(); // 3 + colors.clear(); // [] + colors.length(); // 0 + ``` - store: null, + @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; + }, /** - The controller's current model. When retrieving or modifying a controller's - model, this property should be used instead of the `content` property. + This will use the primitive `replace()` method to insert an object at the + specified index. - @property model - @public - */ - model: null, + ```javascript + var colors = ["red", "green", "blue"]; + colors.insertAt(2, "yellow"); // ["red", "green", "yellow", "blue"] + colors.insertAt(5, "orange"); // Error: Index out of range + ``` + + @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; + }, /** - @private - */ - content: computed.alias('model') + 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. - }); - }); -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; + If you pass a start and length that is beyond the + length this method will throw an `OUT_OF_RANGE_EXCEPTION`. - /** - The ControllerContentModelAliasDeprecation mixin is used to provide a useful - deprecation warning when specifying `content` directly on a `Ember.Controller` - (without also specifying `model`). + ```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 + ``` - 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). + @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 change reduces many caveats with model/content, and also sets a - simple ground rule: Never set a controllers content, rather always set - it's model and ember will do the right thing. + if ((start < 0) || (start >= get(this, 'length'))) { + throw new EmberError(OUT_OF_RANGE_EXCEPTION); + } + // fast case + if (len === undefined) len = 1; + this.replace(start, len, EMPTY); + } - `Ember.ControllerContentModelAliasDeprecation` is used internally by Ember in - `Ember.Controller`. + return this; + }, - @class ControllerContentModelAliasDeprecation - @namespace Ember - @private - @since 1.7.0 - */ - __exports__["default"] = Mixin.create({ /** - @private - - Moves `content` to `model` at extend time if a `model` is not also specified. + Push the object onto the end of the array. Works just like `push()` but it + is KVO-compliant. - 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 colors = ["red", "green"]; + colors.pushObject("black"); // ["red", "green", "black"] + colors.pushObject(["yellow"]); // ["red", "green", ["yellow"]] + ``` - @method willMergeMixin - @since 1.4.0 + @method pushObject + @param {*} obj object to push + @return object same object passed as a param */ - willMergeMixin: function(props) { - // Calling super is only OK here since we KNOW that - // there is another Mixin loaded first. - this._super.apply(this, arguments); + pushObject: function(obj) { + this.insertAt(get(this, 'length'), obj); + return obj; + }, - var modelSpecified = !!props.model; + /** + Add the objects in the passed numerable to the end of the array. Defers + notifying observers of the change until all objects are added. - if (props.content && !modelSpecified) { - props.model = props.content; - delete props['content']; + ```javascript + var colors = ["red"]; + colors.pushObjects(["yellow", "orange"]); // ["red", "yellow", "orange"] + ``` - Ember.deprecate('Do not specify `content` on a Controller, use `model` instead.', false); + @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"); } - } - }); - }); -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 - */ + 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. - var get = __dependency1__.get; - var required = __dependency2__.required; - var Freezable = __dependency3__.Freezable; - var Mixin = __dependency2__.Mixin; - var fmt = __dependency4__.fmt; - var EmberError = __dependency5__["default"]; + ```javascript + var colors = ["red", "green", "blue"]; + colors.popObject(); // "blue" + console.log(colors); // ["red", "green"] + ``` + @method popObject + @return object + */ + popObject: function() { + var len = get(this, 'length'); + if (len === 0) return null; - /** - 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. + var ret = this.objectAt(len-1); + this.removeAt(len-1, 1); + return ret; + }, - You should generally implement the `copy()` method to return a copy of the - receiver. + /** + Shift an object from start of array or nil if none are left. Works just + like `shift()` but it is KVO-compliant. - Note that `frozenCopy()` will only work if you also implement - `Ember.Freezable`. + ```javascript + var colors = ["red", "green", "blue"]; + colors.shiftObject(); // "red" + console.log(colors); // ["green", "blue"] + ``` + + @method shiftObject + @return object + */ + shiftObject: function() { + if (get(this, 'length') === 0) return null; + var ret = this.objectAt(0); + this.removeAt(0); + return ret; + }, - @class Copyable - @namespace Ember - @since Ember 0.9 - */ - __exports__["default"] = Mixin.create({ /** - Override to return a copy of the receiver. Default implementation raises - an exception. + Unshift an object to start of array. Works just like `unshift()` but it is + KVO-compliant. - @method copy - @param {Boolean} deep if `true`, a deep copy of the object should be made - @return {Object} copy of receiver + ```javascript + var colors = ["red"]; + colors.unshiftObject("yellow"); // ["yellow", "red"] + colors.unshiftObject(["black"]); // [["black"], "yellow", "red"] + ``` + + @method unshiftObject + @param {*} obj object to unshift + @return object same object passed as a param */ - copy: required(Function), + unshiftObject: function(obj) { + this.insertAt(0, obj); + return obj; + }, /** - 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. - - Raises an exception if you try to call this method on a object that does - not support freezing. + Adds the named objects to the beginning of the array. Defers notifying + observers until all objects have been added. - 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. + ```javascript + var colors = ["red"]; + colors.unshiftObjects(["black", "white"]); // ["black", "white", "red"] + colors.unshiftObjects("yellow"); // Type Error: 'undefined' is not a function + ``` - @method frozenCopy - @return {Object} copy of receiver or receiver + @method unshiftObjects + @param {Ember.Enumerable} objects the objects to add + @return {Ember.Array} 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])); - } - } - }); - }); -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"]; + unshiftObjects: function(objects) { + this.replace(0, 0, objects); + return this; + }, - /** - @module ember - @submodule ember-runtime - */ + /** + 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; + }, - /** - @class Deferred - @namespace Ember - */ - __exports__["default"] = 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; + Replace all the receiver's content with content of the argument. + If argument is an empty array receiver will be cleared. - entity = this; - deferred = get(this, '_deferred'); - promise = deferred.promise; + ```javascript + var colors = ["red", "green", "blue"]; + colors.setObjects(["black", "white"]); // ["black", "white"] + colors.setObjects([]); // [] + ``` - function fulfillmentHandler(fulfillment) { - if (fulfillment === promise) { - return resolve(entity); - } else { - return resolve(fulfillment); - } - } + @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(); - return promise.then(resolve && fulfillmentHandler, reject, label); + var len = get(this, 'length'); + this.replace(0, len, objects); + return this; }, - /** - Resolve a Deferred object and call any `doneCallbacks` with the given args. + // .......................................................... + // IMPLEMENT Ember.MutableEnumerable + // - @method resolve - */ - resolve: function(value) { - var deferred, promise; + /** + Remove all occurrences of an object in the array. - deferred = get(this, '_deferred'); - promise = deferred.promise; + ```javascript + var cities = ["Chicago", "Berlin", "Lima", "Chicago"]; + cities.removeObject("Chicago"); // ["Berlin", "Lima"] + cities.removeObject("Lima"); // ["Berlin"] + cities.removeObject("Tokyo") // ["Berlin"] + ``` - if (value === this) { - deferred.resolve(promise); - } else { - deferred.resolve(value); + @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; }, /** - Reject a Deferred object and call any `failCallbacks` with the given args. + Push the object onto the end of the array if it is not already + present in the array. - @method reject - */ - reject: function(value) { - get(this, '_deferred').reject(value); - }, + ```javascript + var cities = ["Chicago", "Berlin"]; + cities.addObject("Lima"); // ["Chicago", "Berlin", "Lima"] + cities.addObject("Berlin"); // ["Chicago", "Berlin", "Lima"] + ``` - _deferred: computed(function() { - Ember.deprecate('Usage of Ember.DeferredMixin or Ember.Deferred is deprecated.', this._suppressDeferredDeprecation); + @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; + } - 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__) { +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 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-runtime */ - // .......................................................... - // HELPERS - // - - 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"]; - - var a_slice = Array.prototype.slice; - - var contexts = []; - - function popCtx() { - return contexts.length === 0 ? {} : contexts.pop(); - } - - function pushCtx(ctx) { - contexts.push(ctx); - return null; - } - - function iter(key, value) { - var valueProvided = arguments.length === 2; - - function i(item) { - var cur = get(item, key); - return valueProvided ? value === cur : !!cur; - } - - return i; - } - /** - 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). + 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. - 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. + 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. - ## Writing Your Own Enumerable + ## Adding Objects - To make your own custom class enumerable, you need two items: + 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. - 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().` + ```javascript + set.addObject(contact); + ``` - 2. You must implement `nextObject().` See documentation. + ## Removing Objects - 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. + 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. - ## Using Ember Enumeration with Other Libraries + ```javascript + set.removeObject(contact); + ``` - 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. + ## Implementing In Your Own Code - @class Enumerable + 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. + + @class MutableEnumerable @namespace Ember - @since Ember 0.9 + @uses Ember.Enumerable */ - __exports__["default"] = Mixin.create({ + __exports__["default"] = Mixin.create(Enumerable, { /** - Implement this method to make your class enumerable. - - 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. + __Required.__ You must implement this method to apply this mixin. - 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. + 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. - 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. + If the passed object is of a type not supported by the receiver, + then this method should raise an exception. - 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 addObject + @param {Object} object The object to add to the enumerable. + @return {Object} the passed object + */ + addObject: required(Function), - The default implementation of this method simply looks up the index. - This works great on any Array-like objects. + /** + Adds each object in the passed enumerable to the receiver. - @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 + @method addObjects + @param {Ember.Enumerable} objects the objects to add. + @return {Object} receiver */ - nextObject: required(Function), + addObjects: function(objects) { + beginPropertyChanges(this); + forEach(objects, function(obj) { this.addObject(obj); }, this); + endPropertyChanges(this); + return this; + }, /** - 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. - - 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`. + __Required.__ You must implement this method to apply this mixin. - ```javascript - var arr = ['a', 'b', 'c']; - arr.get('firstObject'); // 'a' + 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. - var arr = []; - arr.get('firstObject'); // undefined - ``` + If the passed object is of a type not supported by the receiver, + then this method should raise an exception. - @property firstObject - @return {Object} the object or undefined + @method removeObject + @param {Object} object The object to remove from the enumerable. + @return {Object} the passed object */ - firstObject: computed('[]', function() { - if (get(this, 'length') === 0) { - return undefined; - } - - // handle generic enumerables - var context = popCtx(); - var ret = this.nextObject(0, null, context); - - pushCtx(context); + removeObject: required(Function), - return ret; - }), /** - 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`. + Removes each object in the passed enumerable from the receiver. - ```javascript - var arr = ['a', 'b', 'c']; - arr.get('lastObject'); // 'c' + @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; + } + }); + }); +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"; + /** + @module ember + @submodule ember-runtime + */ + var Ember = __dependency1__["default"]; + // Ember.assert + + var get = __dependency2__.get; + var getWithDefault = __dependency2__.getWithDefault; + var set = __dependency3__.set; + var apply = __dependency4__.apply; + var getProperties = __dependency5__["default"]; + var setProperties = __dependency6__["default"]; + var Mixin = __dependency7__.Mixin; + var hasListeners = __dependency8__.hasListeners; + var beginPropertyChanges = __dependency9__.beginPropertyChanges; + var propertyWillChange = __dependency9__.propertyWillChange; + var propertyDidChange = __dependency9__.propertyDidChange; + var endPropertyChanges = __dependency9__.endPropertyChanges; + var addObserver = __dependency10__.addObserver; + var addBeforeObserver = __dependency10__.addBeforeObserver; + var removeObserver = __dependency10__.removeObserver; + var observersFor = __dependency10__.observersFor; + var cacheFor = __dependency11__.cacheFor; + var isNone = __dependency12__["default"]; - var arr = []; - arr.get('lastObject'); // undefined - ``` - @property lastObject - @return {Object} the last object or undefined - */ - lastObject: computed('[]', function() { - var len = get(this, 'length'); + var slice = Array.prototype.slice; + /** + ## Overview - if (len === 0) { - return undefined; - } + This mixin provides properties and property observing functionality, core + features of the Ember object model. - var context = popCtx(); - var idx = 0; - var last = null; - var cur; + 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. - do { - last = cur; - cur = this.nextObject(idx++, last, context); - } while (cur !== undefined); + 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. - pushCtx(context); + 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. - return last; - }), + ## Using `get()` and `set()` - /** - 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. + 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. - ```javascript - var arr = ['a', 'b', 'c']; + More documentation about `get` and `set` are below. - arr.contains('a'); // true - arr.contains('z'); // false - ``` + ## Observing Property Changes - @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; - }, + 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: - /** - Iterates through the enumerable, calling the passed function on each - item. This method corresponds to the `forEach()` method defined in - JavaScript 1.6. + ```javascript + Ember.Object.extend({ + valueObserver: function() { + // Executes whenever the "value" property changes + }.observes('value') + }); + ``` - The callback method you provide should have the following signature (all - parameters are optional): + 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. - ```javascript - function(item, index, enumerable); - ``` + To add an observer for a property, call: - - `item` is the current item in the iteration. - - `index` is the current index in the iteration. - - `enumerable` is the enumerable object itself. + ```javascript + object.addObserver('propertyKey', targetObject, targetAction) + ``` - 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 will call the `targetAction` method on the `targetObject` whenever + the value of the `propertyKey` changes. - @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(); - } + 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. - var context = popCtx(); - var len = get(this, 'length'); - var last = null; + @class Observable + @namespace Ember + */ + __exports__["default"] = Mixin.create({ - if (target === undefined) { - target = null; - } + /** + Retrieves the value of a property from the object. - for(var idx = 0; idx < len; idx++) { - var next = this.nextObject(idx, last, context) ; - callback.call(target, next, idx, this); - last = next ; - } + This method is usually similar to using `object[keyName]` or `object.keyName`, + however it supports both computed properties and the unknownProperty + handler. - last = null ; - context = pushCtx(context); + 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. - return this ; - }, + ### Computed Properties - /** - Alias for `mapBy` + Computed properties are methods defined with the `property` modifier + declared at the end, such as: - @method getEach - @param {String} key name of the property - @return {Array} The mapped array. - */ - getEach: function(key) { - return this.mapBy(key); - }, + ```javascript + fullName: function() { + return this.get('firstName') + ' ' + this.get('lastName'); + }.property('firstName', 'lastName') + ``` - /** - 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. + 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. - @method setEach - @param {String} key The key to set - @param {Object} value The object to set - @return {Object} receiver + ### Unknown Properties + + 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. + + @method get + @param {String} keyName The property to retrieve + @return {Object} The property value or undefined. */ - setEach: function(key, value) { - return this.forEach(function(item) { - set(item, key, value); - }); + get: function(keyName) { + return get(this, keyName); }, /** - 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. - - The callback method you provide should have the following signature (all - parameters are optional): + To get the values of multiple properties at once, call `getProperties` + with a list of strings or an array: ```javascript - function(item, index, enumerable); + record.getProperties('firstName', 'lastName', 'zipCode'); + // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } ``` - - `item` is the current item in the iteration. - - `index` is the current index in the iteration. - - `enumerable` is the enumerable object itself. - - It should return the mapped value. + is equivalent to: - 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 + record.getProperties(['firstName', 'lastName', 'zipCode']); + // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } + ``` - @method map - @param {Function} callback The callback to execute - @param {Object} [target] The target object to use - @return {Array} The mapped array. + @method getProperties + @param {String...|Array} list of keys to get + @return {Hash} */ - map: function(callback, target) { - var ret = Ember.A(); - - this.forEach(function(x, idx, i) { - ret[idx] = callback.call(target, x, idx,i); - }); - - return ret ; + getProperties: function() { + return apply(null, getProperties, [this].concat(slice.call(arguments))); }, /** - Similar to map, this specialized function returns the value of the named - property on all items in the enumeration. + Sets the provided key or path to the value. - @method mapBy - @param {String} key name of the property - @return {Array} The mapped array. - */ - mapBy: function(key) { - return this.map(function(next) { - return get(next, key); - }); - }, + 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. - /** - Similar to map, this specialized function returns the value of the named - property on all items in the enumeration. + ### Computed Properties - @method mapProperty - @param {String} key name of the property - @return {Array} The mapped array. - @deprecated Use `mapBy` instead - */ + 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. - mapProperty: aliasMethod('mapBy'), + ### Unknown Properties - /** - 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. + 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. - The callback method you provide should have the following signature (all - parameters are optional): + ### Property Observers - ```javascript - function(item, index, enumerable); - ``` + 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. - - `item` is the current item in the iteration. - - `index` is the current index in the iteration. - - `enumerable` is the enumerable object itself. + ### Chaining - It should return `true` to include the item in the results, `false` - otherwise. + In addition to property changes, `set()` returns the value of the object + itself so you can do chaining like this: - 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 + record.set('firstName', 'Charles').set('lastName', 'Jolley'); + ``` - @method filter - @param {Function} callback The callback to execute - @param {Object} [target] The target object to use - @return {Array} A filtered array. + @method set + @param {String} keyName The property to set + @param {Object} value The value to set or `null`. + @return {Ember.Observable} */ - 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 ; + set: function(keyName, value) { + set(this, keyName, value); + return this; }, - /** - 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(). - The callback method you provide should have the following signature (all - parameters are optional): + /** + Sets a list of properties at once. These properties are set inside + a single `beginPropertyChanges` and `endPropertyChanges` batch, so + observers will be buffered. ```javascript - function(item, index, enumerable); + record.setProperties({ firstName: 'Charles', lastName: 'Jolley' }); ``` - - *item* is the current item in the iteration. - - *index* is the current index in the iteration - - *enumerable* is the enumerable object itself. + @method setProperties + @param {Hash} hash the hash of keys and values to set + @return {Ember.Observable} + */ + setProperties: function(hash) { + return setProperties(this, hash); + }, - It should return the a falsey value to include the item in the results. + /** + Begins a grouping of property changes. - 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. + 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 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)); - }); + @method beginPropertyChanges + @return {Ember.Observable} + */ + beginPropertyChanges: function() { + beginPropertyChanges(); + return this; }, /** - 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`. + Ends a grouping of property changes. - @method filterBy - @param {String} key the property to test - @param {*} [value] optional value to test against. - @return {Array} filtered array + 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. + + @method endPropertyChanges + @return {Ember.Observable} */ - filterBy: function(key, value) { - return this.filter(apply(this, iter, arguments)); + endPropertyChanges: function() { + endPropertyChanges(); + return this; }, /** - 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`. + Notify the observer system that a property is about to change. - @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'), + 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. - /** - 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. + 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 rejectBy - @param {String} key the property to test - @param {String} [value] optional value to test against. - @return {Array} rejected array + @method propertyWillChange + @param {String} keyName The property key that is about to change. + @return {Ember.Observable} */ - rejectBy: function(key, value) { - var exactValue = function(item) { - return get(item, key) === value; - }; + propertyWillChange: function(keyName) { + propertyWillChange(this, keyName); + return this; + }, - var hasValue = function(item) { - return !!get(item, key); - }; + /** + Notify the observer system that a property has just changed. - var use = (arguments.length === 2 ? exactValue : hasValue); + 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. - return this.reject(use); + 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 propertyDidChange + @param {String} keyName The property key that has just changed. + @return {Ember.Observable} + */ + propertyDidChange: function(keyName) { + propertyDidChange(this, keyName); + return this; }, /** - 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. + Convenience method to call `propertyWillChange` and `propertyDidChange` in + succession. - @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 + @method notifyPropertyChange + @param {String} keyName The property key to be notified about. + @return {Ember.Observable} */ - rejectProperty: aliasMethod('rejectBy'), - - /** - 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. + notifyPropertyChange: function(keyName) { + this.propertyWillChange(keyName); + this.propertyDidChange(keyName); + return this; + }, - The callback method you provide should have the following signature (all - parameters are optional): + 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.', false, { url: 'http://emberjs.com/guides/deprecations/#toc_deprecate-beforeobservers' }); + addBeforeObserver(this, key, target, method); + }, - ```javascript - function(item, index, enumerable); - ``` + /** + Adds an observer on a property. - - `item` is the current item in the iteration. - - `index` is the current index in the iteration. - - `enumerable` is the enumerable object itself. + This is the core method used to register an observer for a property. - It should return the `true` to include the item in the results, `false` - otherwise. + 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. - 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. + 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. - @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'); + ### Observer Methods - if (target === undefined) { - target = null; - } + Observer methods you pass should generally have the following signature if + you do not pass a `context` parameter: - var context = popCtx(); - var found = false; - var last = null; - var next, ret; + ```javascript + fooDidChange: function(sender, key, value, rev) { }; + ``` - for(var idx = 0; idx < len && !found; idx++) { - next = this.nextObject(idx, last, context); + 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. - if (found = callback.call(target, next, idx, this)) { - ret = next; - } + If you pass a `context` parameter, the context will be passed before the + revision like so: - last = next; - } + ```javascript + fooDidChange: function(sender, key, value, context, rev) { }; + ``` - next = last = null; - context = pushCtx(context); + 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. - return ret; + @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); }, /** - 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`. - - This method works much like the more generic `find()` method. + 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. - @method findBy - @param {String} key the property to test - @param {String} [value] optional value to test against. - @return {Object} found item or `undefined` + @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. */ - findBy: function(key, value) { - return this.find(apply(this, iter, arguments)); + removeObserver: function(key, target, method) { + removeObserver(this, key, target, method); }, /** - 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`. - - This method works much like the more generic `find()` method. + 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. - @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 + @method hasObserverFor + @param {String} key Key to check + @return {Boolean} */ - findProperty: aliasMethod('findBy'), + hasObserverFor: function(key) { + return hasListeners(this, key+':change'); + }, /** - 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 callback method you provide should have the following signature (all - parameters are optional): + Retrieves the value of a property, or a default value in the case that the + property returns `undefined`. ```javascript - function(item, index, enumerable); + person.getWithDefault('lastName', 'Doe'); ``` - - `item` is the current item in the iteration. - - `index` is the current index in the iteration. - - `enumerable` is the enumerable object itself. - - It should return the `true` or `false`. - - 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 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); + }, - Example Usage: + /** + Set the value of a property to the current value plus some amount. ```javascript - if (people.every(isEngineer)) { - Paychecks.addBigBonus(); - } + person.incrementProperty('age'); + team.incrementProperty('score', 2); ``` - @method every - @param {Function} callback The callback to execute - @param {Object} [target] The target object to use - @return {Boolean} + @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 */ - every: function(callback, target) { - return !this.find(function(x, idx, i) { - return !callback.call(target, x, idx, i); - }); + 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); }, /** - @method everyBy - @param {String} key the property to test - @param {String} [value] optional value to test against. - @deprecated Use `isEvery` instead - @return {Boolean} + Set the value of a property to the current value minus some amount. + + ```javascript + player.decrementProperty('lives'); + orc.decrementProperty('health', 5); + ``` + + @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 */ - everyBy: aliasMethod('isEvery'), + 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); + }, /** - @method everyProperty - @param {String} key the property to test - @param {String} [value] optional value to test against. - @deprecated Use `isEvery` instead - @return {Boolean} + Set the value of a boolean property to the opposite of its + current value. + + ```javascript + starship.toggleProperty('warpDriveEngaged'); + ``` + + @method toggleProperty + @param {String} keyName The name of the property to toggle + @return {Object} The new property value */ - everyProperty: aliasMethod('isEvery'), + toggleProperty: function(keyName) { + set(this, keyName, !get(this, keyName)); + return get(this, keyName); + }, /** - 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. + 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. - @method isEvery - @param {String} key the property to test - @param {String} [value] optional value to test against. - @return {Boolean} - @since 1.3.0 + @method cacheFor + @param {String} keyName + @return {Object} The cached value of the computed property, if any */ - isEvery: function(key, value) { - return this.every(apply(this, iter, arguments)); + cacheFor: function(keyName) { + return cacheFor(this, keyName); }, - /** - Returns `true` if the passed function returns true for any item in the - enumeration. This corresponds with the `some()` method in JavaScript 1.6. + // 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"]; - The callback method you provide should have the following signature (all - parameters are optional): + var not = computed.not; + var or = computed.or; - ```javascript - function(item, index, enumerable); - ``` + /** + @module ember + @submodule ember-runtime + */ - - `item` is the current item in the iteration. - - `index` is the current index in the iteration. - - `enumerable` is the enumerable object itself. + function tap(proxy, promise) { + setProperties(proxy, { + isFulfilled: false, + isRejected: false + }); - It should return the `true` to include the item in the results, `false` - otherwise. + 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"); + } - 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. + /** + A low level mixin making ObjectProxy, ObjectController or ArrayController's promise aware. - Usage Example: + ```javascript + var ObjectPromiseController = Ember.ObjectController.extend(Ember.PromiseProxyMixin); - ```javascript - if (people.any(isManager)) { - Paychecks.addBiggerBonus(); - } - ``` + var controller = ObjectPromiseController.create({ + promise: $.getJSON('/some/remote/data.json') + }); - @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 - */ - any: function(callback, target) { - var len = get(this, 'length'); - var context = popCtx(); - var found = false; - var last = null; - var next, idx; + controller.then(function(json){ + // the json + }, function(reason) { + // the reason why you have no json + }); + ``` - if (target === undefined) { - target = null; - } + the controller has bindable attributes which + track the promises life cycle - for (idx = 0; idx < len && !found; idx++) { - next = this.nextObject(idx, last, context); - found = callback.call(target, next, idx, this); - last = next; - } + ```javascript + controller.get('isPending') //=> true + controller.get('isSettled') //=> false + controller.get('isRejected') //=> false + controller.get('isFulfilled') //=> false + ``` - next = last = null; - context = pushCtx(context); - return found; - }, + When the the $.getJSON completes, and the promise is fulfilled + with json, the life cycle attributes will update accordingly. - /** - Returns `true` if the passed function returns true for any item in the - enumeration. This corresponds with the `some()` method in JavaScript 1.6. + ```javascript + controller.get('isPending') //=> false + controller.get('isSettled') //=> true + controller.get('isRejected') //=> false + controller.get('isFulfilled') //=> true + ``` - The callback method you provide should have the following signature (all - parameters are optional): + As the controller is an ObjectController, and the json now its content, + all the json properties will be available directly from the controller. - ```javascript - function(item, index, enumerable); - ``` + ```javascript + // Assuming the following json: + { + firstName: 'Stefan', + lastName: 'Penner' + } - - `item` is the current item in the iteration. - - `index` is the current index in the iteration. - - `enumerable` is the enumerable object itself. + // both properties will accessible on the controller + controller.get('firstName') //=> 'Stefan' + controller.get('lastName') //=> 'Penner' + ``` - It should return the `true` to include the item in the results, `false` - otherwise. + If the controller is backing a template, the attributes are + bindable from within that template - 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. + ```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. - Usage Example: + @property reason + @default null + */ + reason: null, - ```javascript - if (people.some(isManager)) { - Paychecks.addBiggerBonus(); - } - ``` + /** + Once the proxied promise has settled this will become `false`. - @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 + @property isPending + @default true */ - some: aliasMethod('any'), + isPending: not('isSettled').readOnly(), /** - 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. + Once the proxied promise has settled this will become `true`. - @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 + @property isSettled + @default false */ - isAny: function(key, value) { - return this.any(apply(this, iter, arguments)); - }, + isSettled: or('isRejected', 'isFulfilled').readOnly(), /** - @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 + Will become `true` if the proxied promise is rejected. + + @property isRejected + @default false */ - anyBy: aliasMethod('isAny'), + isRejected: false, /** - @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 + Will become `true` if the proxied promise is fulfilled. + + @property isFulfilled + @default false */ - someProperty: aliasMethod('isAny'), + isFulfilled: false, /** - 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. + The promise whose fulfillment value is being proxied by this object. - The callback method you provide should have the following signature (all - parameters are optional): + This property must be specified upon creation, and should not be + changed once created. + + Example: ```javascript - function(previousValue, item, index, enumerable); + Ember.ObjectController.extend(Ember.PromiseProxyMixin).create({ + promise: + }); ``` - - `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. + @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"); + } + }), - Return the new cumulative value. + /** + An alias to the proxied promise's `then`. - 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. + See RSVP.Promise.then. - 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. + @method then + @param {Function} callback + @return {RSVP.Promise} + */ + then: promiseAlias('then'), + + /** + An alias to the proxied promise's `catch`. + + See RSVP.Promise.catch. + + @method catch + @param {Function} callback + @return {RSVP.Promise} + @since 1.3.0 + */ + 'catch': promiseAlias('catch'), + + /** + An alias to the proxied promise's `finally`. + + See RSVP.Promise.finally. + + @method finally + @param {Function} callback + @return {RSVP.Promise} + @since 1.3.0 + */ + 'finally': promiseAlias('finally') + + }); + + 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 + */ + + var Ember = __dependency1__["default"]; + // Ember.assert, Ember.A + + var get = __dependency2__.get; + var forEach = __dependency3__.forEach; + var Mixin = __dependency4__.Mixin; + var MutableEnumerable = __dependency5__["default"]; + var compare = __dependency6__["default"]; + var addObserver = __dependency7__.addObserver; + var removeObserver = __dependency7__.removeObserver; + var computed = __dependency8__.computed; + var beforeObserver = __dependency4__.beforeObserver; + var observer = __dependency4__.observer; + //ES6TODO: should we access these directly from their package or from how their exposed in ember-metal? + + /** + `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 + 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'}, + ]; - @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(); - } + songsController = Ember.ArrayController.create({ + model: songs, + sortProperties: ['trackNumber'], + sortAscending: true + }); - var ret = initialValue; + songsController.get('firstObject'); // {trackNumber: 2, title: 'Back in the U.S.S.R.'} - this.forEach(function(item, i) { - ret = callback(ret, item, i, this, reducerProperty); - }, this); + songsController.addObject({trackNumber: 1, title: 'Dear Prudence'}); + songsController.get('firstObject'); // {trackNumber: 1, title: 'Dear Prudence'} + ``` - return ret; - }, + If you add or remove the properties to sort by or change the sort direction the model + sort order will be automatically updated. - /** - Invokes the named method on every object in the receiver that - implements it. This method corresponds to the implementation in - Prototype 1.6. + ```javascript + songsController.set('sortProperties', ['title']); + songsController.get('firstObject'); // {trackNumber: 2, title: 'Back in the U.S.S.R.'} - @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. - */ - invoke: function(methodName) { - var ret = Ember.A(); - var args; + songsController.toggleProperty('sortAscending'); + songsController.get('firstObject'); // {trackNumber: 4, title: 'Ob-La-Di, Ob-La-Da'} + ``` - if (arguments.length > 1) { - args = a_slice.call(arguments, 1); - } + `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: - this.forEach(function(x, idx) { - var method = x && x[methodName]; + ```javascript + songsController.get('content').get('firstObject'); // Returns the unsorted original content + songsController.get('firstObject'); // Returns the sorted content. + ``` - if ('function' === typeof method) { - ret[idx] = args ? apply(x, method, args) : x[methodName](); - } - }, this); + 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. - return ret; - }, + @class SortableMixin + @namespace Ember + @uses Ember.MutableEnumerable + */ + __exports__["default"] = Mixin.create(MutableEnumerable, { /** - Simply converts the enumerable into a genuine array. The order is not - guaranteed. Corresponds to the method implemented by Prototype. - - @method toArray - @return {Array} the enumerable as an array. - */ - toArray: function() { - var ret = Ember.A(); + Specifies which properties dictate the `arrangedContent`'s sort order. - this.forEach(function(o, idx) { - ret[idx] = o; - }); + When specifying multiple properties the sorting will use properties + from the `sortProperties` array prioritized from first to last. - return ret; - }, + @property {Array} sortProperties + */ + sortProperties: null, /** - Returns a copy of the array with all `null` and `undefined` elements removed. - - ```javascript - var arr = ['a', null, 'c', undefined]; - arr.compact(); // ['a', 'c'] - ``` + Specifies the `arrangedContent`'s sort direction. + Sorts the content in ascending order by default. Set to `false` to + use descending order. - @method compact - @return {Array} the array without null and undefined elements. + @property {Boolean} sortAscending + @default true */ - compact: function() { - return this.filter(function(value) { - return value != null; - }); - }, + sortAscending: true, /** - 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. + 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: ```javascript - var arr = ['a', 'b', 'a', 'c']; - arr.without('a'); // ['b', 'c'] + function(x, y) { // These are assumed to be integers + if (x === y) + return 0; + return x < y ? -1 : 1; + } ``` - @method without - @param {Object} value - @return {Ember.Enumerable} + @property sortFunction + @type {Function} + @default Ember.compare */ - without: function(value) { - if (!this.contains(value)) { - return this; // nothing to do - } + sortFunction: compare, - var ret = Ember.A(); + orderBy: function(item1, item2) { + var result = 0; + var sortProperties = get(this, 'sortProperties'); + var sortAscending = get(this, 'sortAscending'); + var sortFunction = get(this, 'sortFunction'); - this.forEach(function(k) { - if (k !== value) { - ret[ret.length] = k; + Ember.assert("you need to define `sortProperties`", !!sortProperties); + + 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); - return ret; + return result; }, - /** - Returns a new enumerable that contains only unique values. The default - implementation returns an array regardless of the receiver type. + destroy: function() { + var content = get(this, 'content'); + var sortProperties = get(this, 'sortProperties'); - ```javascript - var arr = ['a', 'a', 'b', 'b']; - arr.uniq(); // ['a', 'b'] - ``` + if (content && sortProperties) { + forEach(content, function(item) { + forEach(sortProperties, function(sortProperty) { + removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); + }, this); + }, this); + } - This only works on primitive data types, e.g. Strings, Numbers, etc. + return this._super(); + }, - @method uniq - @return {Ember.Enumerable} + isSorted: computed.notEmpty('sortProperties'), + + /** + 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 */ - uniq: function() { - var ret = Ember.A(); + 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; - this.forEach(function(k) { - if (indexOf(ret, k) < 0) { - ret.push(k); - } - }); + 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); + } - return ret; - }, + return content; + }), - /** - This property will trigger anytime the enumerable's content changes. - You can observe this property to be notified of changes to the enumerables - content. + _contentWillChange: beforeObserver('content', function() { + var content = get(this, 'content'); + var sortProperties = get(this, 'sortProperties'); - For plain enumerables, this property is read only. `Array` overrides - this method. + if (content && sortProperties) { + forEach(content, function(item) { + forEach(sortProperties, function(sortProperty) { + removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); + }, this); + }, this); + } - @property [] - @type Array - @return this - */ - '[]': computed(function(key, value) { - return this; + this._super(); }), - // .......................................................... - // ENUMERABLE OBSERVERS - // + sortPropertiesWillChange: beforeObserver('sortProperties', function() { + this._lastSortAscending = undefined; + }), - /** - Registers an enumerable observer. Must implement `Ember.EnumerableObserver` - mixin. + sortPropertiesDidChange: observer('sortProperties', function() { + this._lastSortAscending = undefined; + }), - @method addEnumerableObserver - @param {Object} target - @param {Hash} [opts] - @return this - */ - addEnumerableObserver: function(target, opts) { - var willChange = (opts && opts.willChange) || 'enumerableWillChange'; - var didChange = (opts && opts.didChange) || 'enumerableDidChange'; - var hasObservers = get(this, 'hasEnumerableObservers'); + sortAscendingWillChange: beforeObserver('sortAscending', function() { + this._lastSortAscending = get(this, 'sortAscending'); + }), - if (!hasObservers) { - propertyWillChange(this, 'hasEnumerableObservers'); + sortAscendingDidChange: observer('sortAscending', function() { + if (this._lastSortAscending !== undefined && get(this, 'sortAscending') !== this._lastSortAscending) { + var arrangedContent = get(this, 'arrangedContent'); + arrangedContent.reverseObjects(); } + }), - addListener(this, '@enumerable:before', target, willChange); - addListener(this, '@enumerable:change', target, didChange); + contentArrayWillChange: function(array, idx, removedCount, addedCount) { + var isSorted = get(this, 'isSorted'); - if (!hasObservers) { - propertyDidChange(this, 'hasEnumerableObservers'); + if (isSorted) { + var arrangedContent = get(this, 'arrangedContent'); + var removedObjects = array.slice(idx, idx+removedCount); + var sortProperties = get(this, 'sortProperties'); + + forEach(removedObjects, function(item) { + arrangedContent.removeObject(item); + + forEach(sortProperties, function(sortProperty) { + removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); + }, this); + }, this); } - return this; + return this._super(array, idx, removedCount, addedCount); }, - /** - Removes a registered enumerable observer. - - @method removeEnumerableObserver - @param {Object} target - @param {Hash} [opts] - @return this - */ - removeEnumerableObserver: function(target, opts) { - var willChange = (opts && opts.willChange) || 'enumerableWillChange'; - var didChange = (opts && opts.didChange) || 'enumerableDidChange'; - var hasObservers = get(this, 'hasEnumerableObservers'); + contentArrayDidChange: function(array, idx, removedCount, addedCount) { + var isSorted = get(this, 'isSorted'); + var sortProperties = get(this, 'sortProperties'); - if (hasObservers) { - propertyWillChange(this, 'hasEnumerableObservers'); - } + if (isSorted) { + var addedObjects = array.slice(idx, idx+addedCount); - removeListener(this, '@enumerable:before', target, willChange); - removeListener(this, '@enumerable:change', target, didChange); + forEach(addedObjects, function(item) { + this.insertItemSorted(item); - if (hasObservers) { - propertyDidChange(this, 'hasEnumerableObservers'); + forEach(sortProperties, function(sortProperty) { + addObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); + }, this); + }, this); } - return this; + return this._super(array, idx, removedCount, addedCount); }, - /** - Becomes true whenever the array currently has observers watching changes - on the array. + insertItemSorted: function(item) { + var arrangedContent = get(this, 'arrangedContent'); + var length = get(arrangedContent, 'length'); - @property hasEnumerableObservers - @type Boolean - */ - hasEnumerableObservers: computed(function() { - return hasListeners(this, '@enumerable:change') || hasListeners(this, '@enumerable:before'); - }), + var idx = this._binarySearch(item, 0, length); + arrangedContent.insertAt(idx, item); + }, + 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); - /** - 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. + if (leftResult < 0 || rightResult > 0) { + arrangedContent.removeObject(item); + this.insertItemSorted(item); + } + }, - @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; + _binarySearch: function(item, low, high) { + var mid, midItem, res, arrangedContent; - if ('number' === typeof removing) { - removeCnt = removing; - } else if (removing) { - removeCnt = get(removing, 'length'); - } else { - removeCnt = removing = -1; + if (low === high) { + return low; } - if ('number' === typeof adding) { - addCnt = adding; - } else if (adding) { - addCnt = get(adding,'length'); - } else { - addCnt = adding = -1; - } + arrangedContent = get(this, 'arrangedContent'); - hasDelta = addCnt < 0 || removeCnt < 0 || addCnt - removeCnt !== 0; + mid = low + Math.floor((high - low) / 2); + midItem = arrangedContent.objectAt(mid); - if (removing === -1) { - removing = null; - } + res = this.orderBy(midItem, item); - if (adding === -1) { - adding = null; + if (res < 0) { + return this._binarySearch(item, mid+1, high); + } else if (res > 0) { + return this._binarySearch(item, low, mid); } - propertyWillChange(this, '[]'); + 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 - if (hasDelta) { - propertyWillChange(this, 'length'); - } + var get = __dependency2__.get; + var typeOf = __dependency3__.typeOf; + var Mixin = __dependency4__.Mixin; + var computed = __dependency5__.computed; - sendEvent(this, '@enumerable:before', [this, removing, adding]); + /** + `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. - return this; - }, + See also `Ember.ViewTargetActionSupport`, which has + view-aware defaults for target and actionContext. - /** - 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. + @class TargetActionSupport + @namespace Ember + @extends Ember.Mixin + */ + var TargetActionSupport = Mixin.create({ + target: null, + action: null, + actionContext: null, - @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 - */ - enumerableContentDidChange: function(removing, adding) { - var removeCnt, addCnt, hasDelta; + targetObject: computed(function() { + var target = get(this, 'target'); - if ('number' === typeof removing) { - removeCnt = removing; - } else if (removing) { - removeCnt = get(removing, 'length'); + if (typeOf(target) === "string") { + var value = get(this, target); + if (value === undefined) { value = get(Ember.lookup, target); } + return value; } else { - removeCnt = removing = -1; + return target; } + }).property('target'), - if ('number' === typeof adding) { - addCnt = adding; - } else if (adding) { - addCnt = get(adding, 'length'); + actionContextObject: computed(function() { + var actionContext = get(this, 'actionContext'); + + if (typeOf(actionContext) === "string") { + var value = get(this, actionContext); + if (value === undefined) { value = get(Ember.lookup, actionContext); } + return value; } else { - addCnt = adding = -1; + return actionContext; + } + }).property('actionContext'), + + /** + Send an `action` with an `actionContext` to a `target`. The action, actionContext + and target will be retrieved from properties of the object. For example: + + ```javascript + App.SaveButtonView = Ember.View.extend(Ember.TargetActionSupport, { + target: Ember.computed.alias('controller'), + action: 'save', + actionContext: Ember.computed.alias('context'), + click: function() { + this.triggerAction(); // Sends the `save` action, along with the current context + // to the current controller + } + }); + ``` + + The `target`, `action`, and `actionContext` can be provided as properties of + an optional object argument to `triggerAction` as well. + + ```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 } + }); + ``` - hasDelta = addCnt < 0 || removeCnt < 0 || addCnt - removeCnt !== 0; + 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: - if (removing === -1) { - removing = null; + ```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 } + }); + ``` - if (adding === -1) { - adding = null; - } + @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'); + var target = opts.target || get(this, 'targetObject'); + var actionContext = opts.actionContext; - sendEvent(this, '@enumerable:change', [this, removing, adding]); + function args(options, actionName) { + var ret = []; + if (actionName) { ret.push(actionName); } - if (hasDelta) { - propertyDidChange(this, 'length'); + return ret.concat(options); } - propertyDidChange(this, '[]'); - - return this ; - }, - - /** - Converts the enumerable into an array and sorts by the keys - specified in the argument. + if (typeof actionContext === 'undefined') { + actionContext = get(this, 'actionContextObject') || this; + } - You may provide multiple arguments to sort by multiple properties. + if (target && action) { + var ret; - @method sortBy - @param {String} property name(s) to sort on - @return {Array} The sorted array. - @since 1.2.0 - */ - sortBy: function() { - var sortKeys = arguments; + 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)); + } - 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); + if (ret !== false) ret = true; - if (compareValue) { - return compareValue; - } - } - return 0; - }); + return ret; + } else { + return false; + } } }); + + __exports__["default"] = TargetActionSupport; }); -enifed("ember-runtime/mixins/evented", - ["ember-metal/mixin","ember-metal/events","exports"], - function(__dependency1__, __dependency2__, __exports__) { +enifed("ember-runtime/system/application", + ["ember-runtime/system/namespace","exports"], + function(__dependency1__, __exports__) { "use strict"; - var Mixin = __dependency1__.Mixin; - var addListener = __dependency2__.addListener; - var removeListener = __dependency2__.removeListener; - var hasListeners = __dependency2__.hasListeners; - var sendEvent = __dependency2__.sendEvent; + var Namespace = __dependency1__["default"]; + + __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"]; /** @module ember @submodule ember-runtime */ - /** - This mixin allows for Ember objects to subscribe to and emit events. + var OUT_OF_RANGE_EXCEPTION = "Index out of range"; + var EMPTY = []; - ```javascript - App.Person = Ember.Object.extend(Ember.Evented, { - greet: function() { - // ... - this.trigger('greet'); - } - }); + function K() { return this; } - var person = App.Person.create(); + /** + 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. - person.on('greet', function() { - console.log('Our person has greeted'); - }); + A simple example of usage: - person.greet(); + ```javascript + var pets = ['dog', 'cat', 'fish']; + var ap = Ember.ArrayProxy.create({ content: Ember.A(pets) }); - // outputs: 'Our person has greeted' + ap.get('firstObject'); // 'dog' + ap.set('content', ['amoeba', 'paramecium']); + ap.get('firstObject'); // 'amoeba' ``` - You can also chain multiple event subscriptions: + 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`: ```javascript - person.on('greet', function() { - console.log('Our person has greeted'); - }).one('greet', function() { - console.log('Offer one-time special'); - }).off('event', this, forgetThis); + var pets = ['dog', 'cat', 'fish']; + var ap = Ember.ArrayProxy.create({ + content: Ember.A(pets), + objectAtContent: function(idx) { + return this.get('content').objectAt(idx).toUpperCase(); + } + }); + + ap.get('firstObject'); // . 'DOG' ``` - @class Evented + @class ArrayProxy @namespace Ember - */ - __exports__["default"] = Mixin.create({ + @extends Ember.Object + @uses Ember.MutableArray + */ + var ArrayProxy = EmberObject.extend(MutableArray, { /** - Subscribes to a named event with given function. - - ```javascript - person.on('didLoad', function() { - // fired once the person has loaded - }); - ``` - - 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. + The content array. Must be an object that implements `Ember.Array` and/or + `Ember.MutableArray.` - @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 + @property content + @type Ember.Array */ - on: function(name, target, method) { - addListener(this, name, target, method); - return this; - }, + content: null, /** - 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. + 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. - @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 + @property arrangedContent */ - one: function(name, target, method) { - if (!method) { - method = target; - target = null; - } - - addListener(this, name, target, method, true); - return this; - }, + arrangedContent: alias('content'), /** - Triggers a named event for the object. Any additional arguments - will be passed as parameters to the functions that are subscribed to the - event. - - ```javascript - person.on('didEat', function(food) { - console.log('person ate some ' + food); - }); + 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. - person.trigger('didEat', 'broccoli'); + This method will only be called if content is non-`null`. - // outputs: person ate some broccoli - ``` - @method trigger - @param {String} name The name of the event - @param {Object...} args Optional arguments to pass on + @method objectAtContent + @param {Number} idx The index to retrieve. + @return {Object} the value or undefined if none found */ - trigger: function(name) { - var length = arguments.length; - var args = new Array(length - 1); - - for (var i = 1; i < length; i++) { - args[i - 1] = arguments[i]; - } - - sendEvent(this, name, args); + objectAtContent: function(idx) { + return get(this, 'arrangedContent').objectAt(idx); }, /** - Cancels subscription for given name, target, and method. + 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 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 + This method will only be called if content is non-`null`. + + @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} */ - off: function(name, target, method) { - removeListener(this, name, target, method); - return this; + replaceContent: function(idx, amt, objects) { + get(this, 'content').replace(idx, amt, objects); }, /** - Checks to see if object has any subscriptions for named event. - - @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 - */ - - 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. - - ## 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. - - 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 Usage + Invoked when the content property is about to change. Notifies observers that the + entire array content will change. - The example below shows a simple object that implement the `Ember.Freezable` - protocol. + @private + @method _contentWillChange + */ + _contentWillChange: beforeObserver('content', function() { + this._teardownContent(); + }), - ```javascript - Contact = Ember.Object.extend(Ember.Freezable, { - firstName: null, - lastName: null, + _teardownContent: function() { + var content = get(this, 'content'); - // 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; + if (content) { + content.removeArrayObserver(this, { + willChange: 'contentArrayWillChange', + didChange: 'contentArrayDidChange' + }); } + }, - }); - - c = Contact.create({ firstName: "John", lastName: "Doe" }); - c.swapNames(); // returns c - c.freeze(); - c.swapNames(); // EXCEPTION - ``` - - ## Copying + /** + Override to implement content array `willChange` observer. - 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. + @method contentArrayWillChange - @class Freezable - @namespace Ember - @since Ember 0.9 - */ - var Freezable = Mixin.create({ + @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, /** - Set to `true` when the object is frozen. Use this property to detect - whether your object is frozen or not. + Override to implement content array `didChange` observer. - @property isFrozen - @type Boolean + @method contentArrayDidChange + + @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 */ - isFrozen: false, + contentArrayDidChange: K, /** - Freezes the object. Once this method has been called the object should - no longer allow any properties to be edited. + Invoked when the content property changes. Notifies observers that the + entire array content has changed. - @method freeze - @return {Object} receiver + @private + @method _contentDidChange */ - freeze: function() { - if (get(this, 'isFrozen')) return this; - set(this, 'isFrozen', true); - return this; - } + _contentDidChange: observer('content', function() { + var content = get(this, 'content'); - }); - __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 - */ + Ember.assert("Can't set ArrayProxy's content to itself", content !== this); + this._setupContent(); + }), - // require('ember-runtime/mixins/array'); - // require('ember-runtime/mixins/mutable_enumerable'); + _setupContent: function() { + var content = get(this, 'content'); - // .......................................................... - // CONSTANTS - // + if (content) { + Ember.assert(fmt('ArrayProxy expects an Array or ' + + 'Ember.ArrayProxy, but you passed %@', [typeof content]), + isArray(content) || content.isDestroyed); - var OUT_OF_RANGE_EXCEPTION = "Index out of range"; - var EMPTY = []; + content.addArrayObserver(this, { + willChange: 'contentArrayWillChange', + didChange: 'contentArrayDidChange' + }); + } + }, - // .......................................................... - // HELPERS - // + _arrangedContentWillChange: beforeObserver('arrangedContent', function() { + var arrangedContent = get(this, 'arrangedContent'); + var len = arrangedContent ? get(arrangedContent, 'length') : 0; - var get = __dependency1__.get; - var isArray = __dependency2__.isArray; - var EmberError = __dependency3__["default"]; - var Mixin = __dependency4__.Mixin; - var required = __dependency4__.required; - var EmberArray = __dependency5__["default"]; - var MutableEnumerable = __dependency6__["default"]; - var Enumerable = __dependency7__["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. + this.arrangedContentArrayWillChange(this, 0, len, undefined); + this.arrangedContentWillChange(this); - 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. + this._teardownArrangedContent(arrangedContent); + }), + _arrangedContentDidChange: observer('arrangedContent', function() { + var arrangedContent = get(this, 'arrangedContent'); + var len = arrangedContent ? get(arrangedContent, 'length') : 0; - 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. + Ember.assert("Can't set ArrayProxy's content to itself", arrangedContent !== this); - @class MutableArray - @namespace Ember - @uses Ember.Array - @uses Ember.MutableEnumerable - */ - __exports__["default"] = Mixin.create(EmberArray, MutableEnumerable, { + this._setupArrangedContent(); - /** - __Required.__ You must implement this method to apply this mixin. + this.arrangedContentDidChange(this); + this.arrangedContentArrayDidChange(this, 0, undefined, len); + }), - 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()` + _setupArrangedContent: function() { + var arrangedContent = get(this, 'arrangedContent'); - @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* - */ - replace: required(), + if (arrangedContent) { + Ember.assert(fmt('ArrayProxy expects an Array or ' + + 'Ember.ArrayProxy, but you passed %@', [typeof arrangedContent]), + isArray(arrangedContent) || arrangedContent.isDestroyed); - /** - Remove all elements from the array. This is useful if you - want to reuse an existing array without having to recreate it. + arrangedContent.addArrayObserver(this, { + willChange: 'arrangedContentArrayWillChange', + didChange: 'arrangedContentArrayDidChange' + }); + } + }, - ```javascript - var colors = ["red", "green", "blue"]; - color.length(); // 3 - colors.clear(); // [] - colors.length(); // 0 - ``` + _teardownArrangedContent: function() { + var arrangedContent = get(this, 'arrangedContent'); - @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; + if (arrangedContent) { + arrangedContent.removeArrayObserver(this, { + willChange: 'arrangedContentArrayWillChange', + didChange: 'arrangedContentArrayDidChange' + }); + } }, - /** - This will use the primitive `replace()` method to insert an object at the - specified index. + arrangedContentWillChange: K, + arrangedContentDidChange: K, - ```javascript - var colors = ["red", "green", "blue"]; - colors.insertAt(2, "yellow"); // ["red", "green", "yellow", "blue"] - colors.insertAt(5, "orange"); // Error: Index out of range - ``` + objectAt: function(idx) { + return get(this, 'content') && this.objectAtContent(idx); + }, - @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]); + length: computed(function() { + var arrangedContent = get(this, 'arrangedContent'); + return arrangedContent ? get(arrangedContent, 'length') : 0; + // No dependencies since Enumerable notifies length of change + }), + + _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; }, - /** - 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. + 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 you pass a start and length that is beyond the - length this method will throw an `OUT_OF_RANGE_EXCEPTION`. + _insertAt: function(idx, object) { + if (idx > get(this, 'content.length')) throw new EmberError(OUT_OF_RANGE_EXCEPTION); + this._replace(idx, 0, [object]); + return this; + }, - ```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 - ``` + 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."); + } + }, - @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) { + var content = get(this, 'content'); + var arrangedContent = get(this, 'arrangedContent'); + var indices = []; + var i; if ((start < 0) || (start >= get(this, 'length'))) { throw new EmberError(OUT_OF_RANGE_EXCEPTION); } - // fast case if (len === undefined) len = 1; - this.replace(start, len, EMPTY); - } - return this; - }, + // Get a list of indices in original content to remove + for (i=start; i= 0) { - var curObject = this.objectAt(loc); - if (curObject === obj) this.removeAt(loc); + var wasApplied = false; + var initMixins, initProperties; + + var Class = function() { + if (!wasApplied) { + Class.proto(); // prepare prototype... + } + o_defineProperty(this, GUID_KEY, nullDescriptor); + o_defineProperty(this, '__nextSuper', undefinedDescriptor); + var m = meta(this); + var 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; + + var concatenatedProperties = this.concatenatedProperties; + var mergedProperties = this.mergedProperties; + + for (var i = 0, l = props.length; i < l; i++) { + var properties = props[i]; + + Ember.assert("Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead.", !(properties instanceof Mixin)); + + if (typeof properties !== 'object' && properties !== undefined) { + throw new EmberError("Ember.Object.create only accepts objects."); + } + + if (!properties) { continue; } + + var keyNames = keys(properties); + + for (var j = 0, ll = keyNames.length; j < ll; j++) { + var keyName = keyNames[j]; + var value = properties[keyName]; + + 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; + } + + var desc = m.descs[keyName]; + + 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))); + + if (concatenatedProperties && + concatenatedProperties.length > 0 && + indexOf(concatenatedProperties, keyName) >= 0) { + var baseValue = this[keyName]; + + if (baseValue) { + if ('function' === typeof baseValue.concat) { + value = baseValue.concat(value); + } else { + value = makeArray(baseValue).concat(value); + } + } else { + value = makeArray(value); + } + } + + if (mergedProperties && + mergedProperties.length && + indexOf(mergedProperties, keyName) >= 0) { + var originalValue = this[keyName]; + + value = merge(originalValue, value); + } + + 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; + } + } + } + } + } + } + + finishPartial(this, m); + + var length = arguments.length; + + 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); + } + + m.proto = proto; + finishChains(this); + sendEvent(this, 'init'); + }; + + Class.toString = Mixin.prototype.toString; + Class.willReopen = function() { + if (wasApplied) { + Class.PrototypeMixin = Mixin.create(Class.PrototypeMixin); + } + + wasApplied = false; + }; + Class._initMixins = function(args) { initMixins = args; }; + Class._initProperties = function(args) { initProperties = args; }; + + Class.proto = function() { + var superclass = Class.superclass; + if (superclass) { superclass.proto(); } + + if (!wasApplied) { + wasApplied = true; + Class.PrototypeMixin.applyPartial(Class.prototype); + } + + return this.prototype; + }; + + 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; }, /** - Push the object onto the end of the array if it is not already - present in the array. + An overridable method called when objects are instantiated. By default, + does nothing unless it is overridden during class definition. + + Example: + + ```javascript + App.Person = Ember.Object.extend({ + init: function() { + alert('Name is ' + this.get('name')); + } + }); + + var steve = App.Person.create({ + name: "Steve" + }); - ```javascript - var cities = ["Chicago", "Berlin"]; - cities.addObject("Lima"); // ["Chicago", "Berlin", "Lima"] - cities.addObject("Berlin"); // ["Chicago", "Berlin", "Lima"] + // alerts 'Name is Steve'. ``` - @method addObject - @param {*} obj object to add, if not already present - @return {Ember.Array} receiver + 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 */ - addObject: function(obj) { - if (!this.contains(obj)) this.pushObject(obj); - return this; - } + init: function() {}, - }); - }); -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 forEach = __dependency1__.forEach; - var Enumerable = __dependency2__["default"]; - var Mixin = __dependency3__.Mixin; - var required = __dependency3__.required; - var beginPropertyChanges = __dependency4__.beginPropertyChanges; - var endPropertyChanges = __dependency4__.endPropertyChanges; + /** + Defines the properties that will be concatenated from the superclass + (instead of overridden). - /** - @module ember - @submodule ember-runtime - */ + 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`. - /** - 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. + Here is some sample code showing the difference between a concatenated + property and a normal one: - 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 + App.BarView = Ember.View.extend({ + someNonConcatenatedProperty: ['bar'], + classNames: ['bar'] + }); - ## Adding Objects + App.FooBarView = App.BarView.extend({ + someNonConcatenatedProperty: ['foo'], + classNames: ['foo'] + }); - 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. + var fooBarView = App.FooBarView.create(); + fooBarView.get('someNonConcatenatedProperty'); // ['foo'] + fooBarView.get('classNames'); // ['ember-view', 'bar', 'foo'] + ``` - ```javascript - set.addObject(contact); - ``` + This behavior extends to object creation as well. Continuing the + above example: - ## Removing Objects + ```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: - 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. + ```javascript + var view = App.FooBarView.create({ + classNames: 'baz' + }) + view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz'] + ``` - ```javascript - set.removeObject(contact); - ``` + Using the `concatenatedProperties` property, we can tell Ember to mix the + content of the properties. - ## Implementing In Your Own Code + In `Ember.View` the `classNameBindings` and `attributeBindings` properties + are also concatenated, in addition to `classNames`. - 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. + 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). - @class MutableEnumerable - @namespace Ember - @uses Ember.Enumerable - */ - __exports__["default"] = Mixin.create(Enumerable, { + @property concatenatedProperties + @type Array + @default null + */ + concatenatedProperties: null, /** - __Required.__ You must implement this method to apply this mixin. - - 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. + Destroyed object property flag. - If the passed object is of a type not supported by the receiver, - then this method should raise an exception. + if this property is `true` the observers and bindings were already + removed by the effect of calling the `destroy()` method. - @method addObject - @param {Object} object The object to add to the enumerable. - @return {Object} the passed object + @property isDestroyed + @default false */ - addObject: required(Function), + isDestroyed: false, /** - Adds each object in the passed enumerable to the receiver. + Destruction scheduled flag. The `destroy()` method has been called. - @method addObjects - @param {Ember.Enumerable} objects the objects to add. - @return {Object} receiver + The object stays intact until the end of the run loop at which point + the `isDestroyed` flag is set. + + @property isDestroying + @default false */ - addObjects: function(objects) { - beginPropertyChanges(this); - forEach(objects, function(obj) { this.addObject(obj); }, this); - endPropertyChanges(this); - return this; - }, + isDestroying: false, /** - __Required.__ You must implement this method to apply this mixin. + Destroys an object by setting the `isDestroyed` flag and removing its + metadata, which effectively destroys observers and bindings. - 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. + If you try to set a property on a destroyed object, an exception will be + raised. - If the passed object is of a type not supported by the receiver, - then this method should raise an exception. + Note that destruction is scheduled for the end of the run loop and does not + happen immediately. It will set an isDestroying flag immediately. - @method removeObject - @param {Object} object The object to remove from the enumerable. - @return {Object} the passed object + @method destroy + @return {Ember.Object} receiver */ - removeObject: required(Function), + destroy: function() { + if (this.isDestroying) { return; } + this.isDestroying = true; + schedule('actions', this, this.willDestroy); + schedule('destroy', this, this._scheduledDestroy); + return this; + }, /** - Removes each object in the passed enumerable from the receiver. + Override to implement teardown. - @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; - } - }); - }); -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"; - /** - @module ember - @submodule ember-runtime - */ - var Ember = __dependency1__["default"]; - // Ember.assert + @method willDestroy + */ + willDestroy: K, - 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"]; + /** + Invoked by the run loop to actually destroy the object. This is + scheduled for execution by the `destroy` method. + @private + @method _scheduledDestroy + */ + _scheduledDestroy: function() { + if (this.isDestroyed) { return; } + destroy(this); + this.isDestroyed = true; + }, - var slice = Array.prototype.slice; - /** - ## Overview + bind: function(to, from) { + if (!(from instanceof Binding)) { from = Binding.from(from); } + from.to(to).connect(this); + return from; + }, - This mixin provides properties and property observing functionality, core - features of the Ember object model. + /** + Returns a string representation which attempts to provide more information + than Javascript's `toString` typically does, in a generic way for all Ember + objects. - 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. + ```javascript + App.Person = Em.Object.extend() + person = App.Person.create() + person.toString() //=> "" + ``` - 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. + If the object's class is not defined on an Ember namespace, it will + indicate it is a subclass of the registered superclass: - 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. + ```javascript + Student = App.Person.extend() + student = Student.create() + student.toString() //=> "<(subclass of App.Person):ember1025>" + ``` - ## Using `get()` and `set()` + If the method `toStringExtension` is defined, its return value will be + included in the output. - 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. + ```javascript + App.Teacher = App.Person.extend({ + toStringExtension: function() { + return this.get('fullName'); + } + }); + teacher = App.Teacher.create() + teacher.toString(); //=> "" + ``` - More documentation about `get` and `set` are below. + @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+'>'; - ## Observing Property Changes + this.toString = makeToString(ret); + return ret; + } + }); - 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: + CoreObject.PrototypeMixin.ownerConstructor = CoreObject; - ```javascript - Ember.Object.extend({ - valueObserver: function() { - // Executes whenever the "value" property changes - }.observes('value') - }); - ``` + function makeToString(ret) { + return function() { return ret; }; + } - 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. + CoreObject.__super__ = null; - To add an observer for a property, call: + var ClassMixinProps = { - ```javascript - object.addObserver('propertyKey', targetObject, targetAction) - ``` + ClassMixin: required(), - This will call the `targetAction` method on the `targetObject` whenever - the value of the `propertyKey` changes. + PrototypeMixin: required(), - 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. + isClass: true, - @class Observable - @namespace Ember - */ - __exports__["default"] = Mixin.create({ + isMethod: false, /** - Retrieves the value of a property from the object. - - This method is usually similar to using `object[keyName]` or `object.keyName`, - however it supports both computed properties and the unknownProperty - handler. + Creates a new subclass. - 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. + ```javascript + App.Person = Ember.Object.extend({ + say: function(thing) { + alert(thing); + } + }); + ``` - ### Computed Properties + This defines a new subclass of Ember.Object: `App.Person`. It contains one method: `say()`. - Computed properties are methods defined with the `property` modifier - declared at the end, such as: + 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: ```javascript - fullName: function() { - return this.get('firstName') + ' ' + this.get('lastName'); - }.property('firstName', 'lastName') + App.PersonView = Ember.View.extend({ + tagName: 'li', + classNameBindings: ['isAdministrator'] + }); ``` - 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. - - ### Unknown Properties + When defining a subclass, you can override methods but still access the implementation of your parent class by calling the special `_super()` method: - 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. + ```javascript + App.Person = Ember.Object.extend({ + say: function(thing) { + var name = this.get('name'); + alert(name + ' says: ' + thing); + } + }); - @method get - @param {String} keyName The property to retrieve - @return {Object} The property value or undefined. - */ - get: function(keyName) { - return get(this, keyName); - }, + App.Soldier = App.Person.extend({ + say: function(thing) { + this._super(thing + ", sir!"); + }, + march: function(numberOfHours) { + alert(this.get('name') + ' marches for ' + numberOfHours + ' hours.') + } + }); - /** - To get the values of multiple properties at once, call `getProperties` - with a list of strings or an array: + var yehuda = App.Soldier.create({ + name: "Yehuda Katz" + }); - ```javascript - record.getProperties('firstName', 'lastName', 'zipCode'); - // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } + yehuda.say("Yes"); // alerts "Yehuda Katz says: Yes, sir!" ``` - is equivalent to: + 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. + + You can also pass `Mixin` classes to add additional properties to the subclass. ```javascript - record.getProperties(['firstName', 'lastName', 'zipCode']); - // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } + App.Person = Ember.Object.extend({ + say: function(thing) { + alert(this.get('name') + ' says: ' + thing); + } + }); + + App.SingingMixin = Mixin.create({ + sing: function(thing){ + alert(this.get('name') + ' sings: la la la ' + thing); + } + }); + + App.BroadwayStar = App.Person.extend(App.SingingMixin, { + dance: function() { + alert(this.get('name') + ' dances: tap tap tap tap '); + } + }); ``` - @method getProperties - @param {String...|Array} list of keys to get - @return {Hash} + The `App.BroadwayStar` class contains three methods: `say()`, `sing()`, and `dance()`. + + @method extend + @static + + @param {Mixin} [mixins]* One or more Mixin classes + @param {Object} [arguments]* Object containing values to use within the new class */ - getProperties: function() { - return apply(null, getProperties, [this].concat(slice.call(arguments))); - }, + extend: function extend() { + var Class = makeCtor(); + var proto; + Class.ClassMixin = Mixin.create(this.ClassMixin); + Class.PrototypeMixin = Mixin.create(this.PrototypeMixin); - /** - Sets the provided key or path to the value. + Class.ClassMixin.ownerConstructor = Class; + Class.PrototypeMixin.ownerConstructor = Class; - 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. + reopen.apply(Class.PrototypeMixin, arguments); - ### Computed Properties + Class.superclass = this; + Class.__super__ = this.prototype; - 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. + proto = Class.prototype = o_create(this.prototype); + proto.constructor = Class; + generateGuid(proto); + meta(proto).proto = proto; // this will disable observers on prototype - ### Unknown Properties + Class.ClassMixin.apply(Class); + return Class; + }, - 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. + /** + Equivalent to doing `extend(arguments).create()`. + If possible use the normal `create` method instead. + + @method createWithMixins + @static + @param [arguments]* + */ + createWithMixins: function() { + var C = this; + var l= arguments.length; + if (l > 0) { + var args = new Array(l); + for (var i = 0; i < l; i++) { + args[i] = arguments[i]; + } + this._initMixins(args); + } + return new C(); + }, + + /** + 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')); + } + }); - ### Property Observers + var tom = App.Person.create({ + name: 'Tom Dale' + }); - 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. + tom.helloWorld(); // alerts "Hi, my name is Tom Dale". + ``` - ### Chaining + `create` will call the `init` function if defined during + `Ember.AnyObject.extend` - In addition to property changes, `set()` returns the value of the object - itself so you can do chaining like this: + If no arguments are passed to `create`, it will not set values to the new + instance during initialization: ```javascript - record.set('firstName', 'Charles').set('lastName', 'Jolley'); + var noName = App.Person.create(); + noName.helloWorld(); // alerts undefined ``` - @method set - @param {String} keyName The property to set - @param {Object} value The value to set or `null`. - @return {Ember.Observable} + 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]* */ - set: function(keyName, value) { - set(this, keyName, value); - return this; + 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(); }, - /** - Sets a list of properties at once. These properties are set inside - a single `beginPropertyChanges` and `endPropertyChanges` batch, so - observers will be buffered. + Augments a constructor's prototype with additional + properties and functions: ```javascript - record.setProperties({ firstName: 'Charles', lastName: 'Jolley' }); - ``` + MyObject = Ember.Object.extend({ + name: 'an object' + }); - @method setProperties - @param {Hash} hash the hash of keys and values to set - @return {Ember.Observable} - */ - setProperties: function(hash) { - return setProperties(this, hash); - }, + o = MyObject.create(); + o.get('name'); // 'an object' - /** - Begins a grouping of property changes. + MyObject.reopen({ + say: function(msg){ + console.log(msg); + } + }) - 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. + o2 = MyObject.create(); + o2.say("hello"); // logs "hello" - @method beginPropertyChanges - @return {Ember.Observable} + o.say("goodbye"); // logs "goodbye" + ``` + + To add functions and properties to the constructor itself, + see `reopenClass` + + @method reopen */ - beginPropertyChanges: function() { - beginPropertyChanges(); + 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; }, /** - Ends a grouping of property changes. + Augments a constructor's own properties and functions: - 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. + ```javascript + MyObject = Ember.Object.extend({ + name: 'an object' + }); - @method endPropertyChanges - @return {Ember.Observable} - */ - endPropertyChanges: function() { - endPropertyChanges(); - return this; - }, + MyObject.reopenClass({ + canBuild: false + }); - /** - Notify the observer system that a property is about to change. + MyObject.canBuild; // false + o = MyObject.create(); + ``` - 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. + 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. - 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. + ```javascript + App.Person = Ember.Object.extend({ + name : "", + sayHello : function(){ + alert("Hello. My name is " + this.get('name')); + } + }); - @method propertyWillChange - @param {String} keyName The property key that is about to change. - @return {Ember.Observable} - */ - propertyWillChange: function(keyName) { - propertyWillChange(this, keyName); - return this; - }, + App.Person.reopenClass({ + species : "Homo sapiens", + createPerson: function(newPersonsName){ + return App.Person.create({ + name:newPersonsName + }); + } + }); - /** - Notify the observer system that a property has just changed. + var tom = App.Person.create({ + name : "Tom Dale" + }); + var yehuda = App.Person.createPerson("Yehuda Katz"); - 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. + tom.sayHello(); // "Hello. My name is Tom Dale" + yehuda.sayHello(); // "Hello. My name is Yehuda Katz" + alert(App.Person.species); // "Homo sapiens" + ``` - 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. + Note that `species` and `createPerson` are *not* valid on the `tom` and `yehuda` + variables. They are only valid on `App.Person`. - @method propertyDidChange - @param {String} keyName The property key that has just changed. - @return {Ember.Observable} + To add functions and properties to instances of + a constructor by extending the constructor's prototype + see `reopen` + + @method reopenClass */ - propertyDidChange: function(keyName) { - propertyDidChange(this, keyName); + 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]; + } + } + + apply(this.ClassMixin, reopen, args); + applyMixin(this, arguments, false); return this; }, - /** - Convenience method to call `propertyWillChange` and `propertyDidChange` in - succession. - - @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; + detect: function(obj) { + if ('function' !== typeof obj) { return false; } + while(obj) { + if (obj===this) { return true; } + obj = obj.superclass; + } + return false; }, - addBeforeObserver: function(key, target, method) { - addBeforeObserver(this, key, target, method); + detectInstance: function(obj) { + return obj instanceof this; }, /** - Adds an observer on a property. - - This is the core method used to register an observer for a property. - - 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. - - 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. - - ### Observer Methods + 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. - Observer methods you pass should generally have the following signature if - you do not pass a `context` parameter: + You can pass a hash of these values to a computed property like this: ```javascript - fooDidChange: function(sender, key, value, rev) { }; + person: function() { + var personId = this.get('personId'); + return App.Person.create({ id: personId }); + }.property().meta({ type: App.Person }) ``` - 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. - - If you pass a `context` parameter, the context will be passed before the - revision like so: + Once you've done this, you can retrieve the values saved to the computed + property from your class like this: ```javascript - fooDidChange: function(sender, key, value, context, rev) { }; + MyClass.metaForProperty('person'); ``` - 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. - - @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. + This will return the original hash that was passed to `meta()`. + + @static + @method metaForProperty + @param key {String} property name */ - addObserver: function(key, target, method) { - addObserver(this, key, target, method); + metaForProperty: function(key) { + var meta = this.proto()['__ember_meta__']; + var desc = meta && meta.descs[key]; + + Ember.assert("metaForProperty() could not find a computed property with key '"+key+"'.", !!desc && desc instanceof ComputedProperty); + return desc._meta || {}; }, - /** - 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. + _computedProperties: computed(function() { + hasCachedComputedProperties = true; + var proto = this.proto(); + var descs = meta(proto).descs; + var property; + var properties = []; - @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. + for (var name in descs) { + property = descs[name]; + + if (property instanceof ComputedProperty) { + properties.push({ + name: name, + meta: property._meta + }); + } + } + return properties; + }).readOnly(), + + /** + Iterate over each computed property for the class, passing its name + and any associated metadata (see `metaForProperty`) to the callback. + + @static + @method eachComputedProperty + @param {Function} callback + @param {Object} binding */ - removeObserver: function(key, target, method) { - removeObserver(this, key, target, method); - }, + eachComputedProperty: function(callback, binding) { + var property, name; + var empty = {}; + + var properties = get(this, '_computedProperties'); + + 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); + } + } + }; + + function injectedPropertyAssertion() { + Ember.assert("Injected properties are invalid", validatePropertyInjections(this)); + } + + function addOnLookupHandler() { + Ember.runInDebug(function() { + /** + Provides lookup-time type validation for injected properties. + + @private + @method _onLookup + */ + ClassMixinProps._onLookup = injectedPropertyAssertion; + }); + } + + + addOnLookupHandler(); /** - 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. + Returns a hash of property names and container names that injected + properties will lookup on the container lazily. - @method hasObserverFor - @param {String} key Key to check - @return {Boolean} + @method _lazyInjections + @return {Object} Hash of all lazy injected property keys to container names */ - hasObserverFor: function(key) { - return hasListeners(this, key+':change'); - }, + ClassMixinProps._lazyInjections = function() { + var injections = {}; + var proto = this.proto(); + var descs = meta(proto).descs; + var key, desc; - /** - Retrieves the value of a property, or a default value in the case that the - property returns `undefined`. + for (key in descs) { + desc = descs[key]; + if (desc instanceof InjectedProperty) { + injections[key] = desc.type + ':' + (desc.name || key); + } + } - ```javascript - person.getWithDefault('lastName', 'Doe'); - ``` + return injections; + }; + - @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); - }, + var ClassMixin = Mixin.create(ClassMixinProps); - /** - Set the value of a property to the current value plus some amount. + ClassMixin.ownerConstructor = CoreObject; - ```javascript - person.incrementProperty('age'); - team.incrementProperty('score', 2); - ``` + CoreObject.ClassMixin = ClassMixin; - @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); - }, + ClassMixin.apply(CoreObject); - /** - Set the value of a property to the current value minus some amount. + CoreObject.reopen({ + didDefineProperty: function(proto, key, value) { + if (hasCachedComputedProperties === false) { return; } + if (value instanceof Ember.ComputedProperty) { + var cache = Ember.meta(this.constructor).cache; - ```javascript - player.decrementProperty('lives'); - orc.decrementProperty('health', 5); - ``` + if (cache._computedProperties !== undefined) { + cache._computedProperties = undefined; + } + } + } + }); - @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); - }, + __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"]; - /** - Set the value of a boolean property to the opposite of it's - current value. + var Deferred = EmberObject.extend(DeferredMixin, { + init: function() { + Ember.deprecate('Usage of Ember.Deferred is deprecated.', false, { url: 'http://emberjs.com/guides/deprecations/#toc_deprecate-ember-deferredmixin-and-ember-deferred' }); + this._super(); + } + }); - ```javascript - starship.toggleProperty('warpDriveEngaged'); - ``` + Deferred.reopenClass({ + promise: function(callback, binding) { + var deferred = Deferred.create(); + callback.call(binding, deferred); + return deferred; + } + }); - @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); + __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 + */ + + var Ember = __dependency1__["default"]; + // Ember.assert + + 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; + + var EachArray = EmberObject.extend(EmberArray, { + + init: function(content, keyName, owner) { + this._super(); + this._keyName = keyName; + this._owner = owner; + this._content = content; }, - /** - 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. - - @method cacheFor - @param {String} keyName - @return {Object} The cached value of the computed property, if any - */ - cacheFor: function(keyName) { - return cacheFor(this, keyName); + objectAt: function(idx) { + var item = this._content.objectAt(idx); + return item && get(item, this._keyName); }, - // intended for debugging purposes - observersForKey: function(keyName) { - return observersFor(this, keyName); - } + length: computed(function() { + var content = this._content; + return content ? get(content, 'length') : 0; + }) + }); - }); -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"]; - var not = computed.not; - var or = computed.or; + var IS_OBSERVER = /^.+:(before|change)$/; - /** - @module ember - @submodule ember-runtime - */ + function addObserverForContentKey(content, keyName, proxy, idx, loc) { + var objects = proxy._objects; + var guid; + if (!objects) objects = proxy._objects = {}; - function tap(proxy, promise) { - setProperties(proxy, { - isFulfilled: false, - isRejected: false - }); + 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'); - 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"); + // 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); + } + } } - /** - A low level mixin making ObjectProxy, ObjectController or ArrayController's promise aware. + function removeObserverForContentKey(content, keyName, proxy, idx, loc) { + var objects = proxy._objects; + if (!objects) objects = proxy._objects = {}; + var indicies, guid; - ```javascript - var ObjectPromiseController = Ember.ObjectController.extend(Ember.PromiseProxyMixin); + while(--loc>=idx) { + var item = content.objectAt(loc); + if (item) { + removeBeforeObserver(item, keyName, proxy, 'contentKeyWillChange'); + removeObserver(item, keyName, proxy, 'contentKeyDidChange'); - var controller = ObjectPromiseController.create({ - promise: $.getJSON('/some/remote/data.json') - }); + guid = guidFor(item); + indicies = objects[guid]; + indicies[indexOf.call(indicies, loc)] = null; + } + } + } - controller.then(function(json){ - // the json - }, function(reason) { - // the reason why you have no json - }); - ``` + /** + 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. - the controller has bindable attributes which - track the promises life cycle + @private + @class EachProxy + @namespace Ember + @extends Ember.Object + */ + var EachProxy = EmberObject.extend({ - ```javascript - controller.get('isPending') //=> true - controller.get('isSettled') //=> false - controller.get('isRejected') //=> false - controller.get('isFulfilled') //=> false - ``` + init: function(content) { + this._super(); + this._content = content; + content.addArrayObserver(this); - When the the $.getJSON completes, and the promise is fulfilled - with json, the life cycle attributes will update accordingly. + // in case someone is already observing some keys make sure they are + // added + forEach(watchedEvents(this), function(eventName) { + this.didAddListener(eventName); + }, this); + }, - ```javascript - controller.get('isPending') //=> false - controller.get('isSettled') //=> true - controller.get('isRejected') //=> false - controller.get('isFulfilled') //=> true - ``` + /** + You can directly access mapped properties by simply requesting them. + The `unknownProperty` handler will generate an EachArray of each item. - As the controller is an ObjectController, and the json now its content, - all the json properties will be available directly from the controller. + @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; + }, - ```javascript - // Assuming the following json: - { - firstName: 'Stefan', - lastName: 'Penner' - } + // .......................................................... + // ARRAY CHANGES + // Invokes whenever the content array itself changes. - // both properties will accessible on the controller - controller.get('firstName') //=> 'Stefan' - controller.get('lastName') //=> 'Penner' - ``` + arrayWillChange: function(content, idx, removedCnt, addedCnt) { + var keys = this._keys; + var key, lim; - If the controller is backing a template, the attributes are - bindable from within that template + lim = removedCnt>0 ? idx+removedCnt : -1; + beginPropertyChanges(this); - ```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. + for(key in keys) { + if (!keys.hasOwnProperty(key)) { continue; } - @property reason - @default null - */ - reason: null, + if (lim>0) { removeObserverForContentKey(content, key, this, idx, lim); } - /** - Once the proxied promise has settled this will become `false`. + propertyWillChange(this, key); + } - @property isPending - @default true - */ - isPending: not('isSettled').readOnly(), + propertyWillChange(this._content, '@each'); + endPropertyChanges(this); + }, - /** - Once the proxied promise has settled this will become `true`. + arrayDidChange: function(content, idx, removedCnt, addedCnt) { + var keys = this._keys; + var lim; - @property isSettled - @default false - */ - isSettled: or('isRejected', 'isFulfilled').readOnly(), + lim = addedCnt>0 ? idx+addedCnt : -1; + changeProperties(function() { + for(var key in keys) { + if (!keys.hasOwnProperty(key)) { continue; } - /** - Will become `true` if the proxied promise is rejected. + if (lim>0) { addObserverForContentKey(content, key, this, idx, lim); } - @property isRejected - @default false - */ - isRejected: false, + propertyDidChange(this, key); + } - /** - Will become `true` if the proxied promise is fulfilled. + propertyDidChange(this._content, '@each'); + }, this); + }, - @property isFulfilled - @default false - */ - isFulfilled: false, + // .......................................................... + // LISTEN FOR NEW OBSERVERS AND OTHER EVENT LISTENERS + // Start monitoring keys based on who is listening... - /** - The promise whose fulfillment value is being proxied by this object. + didAddListener: function(eventName) { + if (IS_OBSERVER.test(eventName)) { + this.beginObservingContentKey(eventName.slice(0, -7)); + } + }, - This property must be specified upon creation, and should not be - changed once created. + didRemoveListener: function(eventName) { + if (IS_OBSERVER.test(eventName)) { + this.stopObservingContentKey(eventName.slice(0, -7)); + } + }, - Example: + // .......................................................... + // CONTENT KEY OBSERVING + // Actual watch keys on the source content. - ```javascript - Ember.ObjectController.extend(Ember.PromiseProxyMixin).create({ - promise: - }); - ``` + 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'); - @property promise - */ - promise: computed(function(key, promise) { - if (arguments.length === 2) { - return tap(this, promise); + addObserverForContentKey(content, keyName, this, 0, len); } else { - throw new EmberError("PromiseProxy's promise must be set"); + keys[keyName]++; } - }), + }, - /** - An alias to the proxied promise's `then`. + stopObservingContentKey: function(keyName) { + var keys = this._keys; + if (keys && (keys[keyName]>0) && (--keys[keyName]<=0)) { + var content = this._content; + var len = get(content, 'length'); - See RSVP.Promise.then. + removeObserverForContentKey(content, keyName, this, 0, len); + } + }, - @method then - @param {Function} callback - @return {RSVP.Promise} - */ - then: promiseAlias('then'), + contentKeyWillChange: function(obj, keyName) { + propertyWillChange(this, keyName); + }, - /** - An alias to the proxied promise's `catch`. + contentKeyDidChange: function(obj, keyName) { + propertyDidChange(this, keyName); + } + }); - See RSVP.Promise.catch. + __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 */ - @method catch - @param {Function} callback - @return {RSVP.Promise} - @since 1.3.0 - */ - 'catch': promiseAlias('catch'), + var Ember = __dependency1__["default"]; + // Ember.ENV.EMBER_LOAD_HOOKS + var forEach = __dependency2__.forEach; + // make sure Ember.A is setup. - /** - An alias to the proxied promise's `finally`. + /** + @module ember + @submodule ember-runtime + */ - See RSVP.Promise.finally. + var loadHooks = Ember.ENV.EMBER_LOAD_HOOKS || {}; + var loaded = {}; - @method finally - @param {Function} callback - @return {RSVP.Promise} - @since 1.3.0 - */ - 'finally': promiseAlias('finally') + /** + Detects when a specific package of Ember (e.g. 'Ember.Handlebars') + has fully loaded and is available for extension. - }); + The provided `callback` will be called with the `name` passed + resolved from a string into the object: - function promiseAlias(name) { - return function () { - var promise = get(this, 'promise'); - return promise[name].apply(promise, arguments); - }; + ``` javascript + Ember.onLoad('Ember.Handlebars' function(hbars) { + hbars.registerHelper(...); + }); + ``` + + @method onLoad + @for Ember + @param name {String} name of hook + @param callback {Function} callback to be called + */ + function onLoad(name, callback) { + var object; + + loadHooks[name] = loadHooks[name] || Ember.A(); + loadHooks[name].pushObject(callback); + + if (object = loaded[name]) { + callback(object); + } + } + + __exports__.onLoad = onLoad;/** + Called when an Ember.js package (e.g Ember.Handlebars) has finished + loading. Triggers any callbacks registered for this event. + + @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 (typeof window === 'object' && typeof window.dispatchEvent === 'function' && typeof CustomEvent === "function") { + var event = new CustomEvent(name, {detail: object, name: name}); + window.dispatchEvent(event); + } + + if (loadHooks[name]) { + forEach.call(loadHooks[name], function(callback) { + callback(object); + }); + } } + + __exports__.runLoadHooks = runLoadHooks; }); -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__) { +enifed("ember-runtime/system/namespace", + ["ember-metal/core","ember-metal/property_get","ember-metal/array","ember-metal/utils","ember-metal/mixin","ember-runtime/system/object","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { "use strict"; /** @module ember @submodule ember-runtime */ + // Ember.lookup, Ember.BOOTED, Ember.deprecate, Ember.NAME_KEY, Ember.anyUnprocessedMixins var Ember = __dependency1__["default"]; - // Ember.assert, Ember.A - var get = __dependency2__.get; - var forEach = __dependency3__.forEach; - var Mixin = __dependency4__.Mixin; - var MutableEnumerable = __dependency5__["default"]; - var compare = __dependency6__["default"]; - var addObserver = __dependency7__.addObserver; - var removeObserver = __dependency7__.removeObserver; - var computed = __dependency8__.computed; - var beforeObserver = __dependency4__.beforeObserver; - var observer = __dependency4__.observer; - //ES6TODO: should we access these directly from their package or from how their exposed in ember-metal? + var indexOf = __dependency3__.indexOf; + var GUID_KEY = __dependency4__.GUID_KEY; + var guidFor = __dependency4__.guidFor; + var Mixin = __dependency5__.Mixin; + + var EmberObject = __dependency6__["default"]; /** - `Ember.SortableMixin` provides a standard interface for array proxies - to specify a sort order and maintain this sorting when objects are added, - removed, or updated without changing the implicit order of their underlying - model array: + A 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. - ```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'}, - ]; + # Example Usage - songsController = Ember.ArrayController.create({ - model: songs, - sortProperties: ['trackNumber'], - sortAscending: true + ```javascript + MyFramework = Ember.Namespace.create({ + VERSION: '1.0.0' }); + ``` - songsController.get('firstObject'); // {trackNumber: 2, title: 'Back in the U.S.S.R.'} + @class Namespace + @namespace Ember + @extends Ember.Object + */ + var Namespace = EmberObject.extend({ + isNamespace: true, - songsController.addObject({trackNumber: 1, title: 'Dear Prudence'}); - songsController.get('firstObject'); // {trackNumber: 1, title: 'Dear Prudence'} - ``` + init: function() { + Namespace.NAMESPACES.push(this); + Namespace.PROCESSED = false; + }, - If you add or remove the properties to sort by or change the sort direction the model - sort order will be automatically updated. + toString: function() { + var name = get(this, 'name') || get(this, 'modulePrefix'); + if (name) { return name; } - ```javascript - songsController.set('sortProperties', ['title']); - songsController.get('firstObject'); // {trackNumber: 2, title: 'Back in the U.S.S.R.'} + findNamespaces(); + return this[NAME_KEY]; + }, - songsController.toggleProperty('sortAscending'); - songsController.get('firstObject'); // {trackNumber: 4, title: 'Ob-La-Di, Ob-La-Da'} - ``` + nameClasses: function() { + processNamespace([this.toString()], this, {}); + }, - `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: + destroy: function() { + var namespaces = Namespace.NAMESPACES; + var toString = this.toString(); - ```javascript - songsController.get('content').get('firstObject'); // Returns the unsorted original content - songsController.get('firstObject'); // Returns the sorted content. - ``` + if (toString) { + Ember.lookup[toString] = undefined; + delete Namespace.NAMESPACES_BY_ID[toString]; + } + namespaces.splice(indexOf.call(namespaces, this), 1); + this._super(); + } + }); - 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. + Namespace.reopenClass({ + NAMESPACES: [Ember], + NAMESPACES_BY_ID: {}, + PROCESSED: false, + processAll: processAllNamespaces, + byName: function(name) { + if (!Ember.BOOTED) { + processAllNamespaces(); + } - @class SortableMixin - @namespace Ember - @uses Ember.MutableEnumerable - */ - __exports__["default"] = Mixin.create(MutableEnumerable, { + return NAMESPACES_BY_ID[name]; + } + }); - /** - Specifies which properties dictate the `arrangedContent`'s sort order. + var NAMESPACES_BY_ID = Namespace.NAMESPACES_BY_ID; - When specifying multiple properties the sorting will use properties - from the `sortProperties` array prioritized from first to last. + var hasOwnProp = ({}).hasOwnProperty; - @property {Array} sortProperties - */ - sortProperties: null, + function processNamespace(paths, root, seen) { + var idx = paths.length; - /** - Specifies the `arrangedContent`'s sort direction. - Sorts the content in ascending order by default. Set to `false` to - use descending order. + NAMESPACES_BY_ID[paths.join('.')] = root; - @property {Boolean} sortAscending - @default true - */ - sortAscending: true, + // 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]; - /** - 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., + // 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; - * 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: + // 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('.'); - ```javascript - function(x, y) { // These are assumed to be integers - if (x === y) - return 0; - return x < y ? -1 : 1; + // Support nested namespaces + } else if (obj && obj.isNamespace) { + // Skip aliased namespaces + if (seen[guidFor(obj)]) { continue; } + seen[guidFor(obj)] = true; + + // Process the child namespace + processNamespace(paths, obj, seen); } - ``` + } - @property sortFunction - @type {Function} - @default Ember.compare - */ - sortFunction: compare, + paths.length = idx; // cut out last item + } - orderBy: function(item1, item2) { - var result = 0; - var sortProperties = get(this, 'sortProperties'); - var sortAscending = get(this, 'sortAscending'); - var sortFunction = get(this, 'sortFunction'); + var STARTS_WITH_UPPERCASE = /^[A-Z]/; - Ember.assert("you need to define `sortProperties`", !!sortProperties); + function tryIsNamespace(lookup, prop) { + try { + var obj = lookup[prop]; + return obj && obj.isNamespace && obj; + } catch (e) { + // continue + } + } - 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); + function findNamespaces() { + var lookup = Ember.lookup; + var obj; - return result; - }, + if (Namespace.PROCESSED) { return; } - destroy: function() { - var content = get(this, 'content'); - var sortProperties = get(this, 'sortProperties'); + for (var prop in lookup) { + // Only process entities that start with uppercase A-Z + if (!STARTS_WITH_UPPERCASE.test(prop)) { continue; } - if (content && sortProperties) { - forEach(content, function(item) { - forEach(sortProperties, function(sortProperty) { - removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); - }, this); - }, this); + // Unfortunately, some versions of IE don't support window.hasOwnProperty + if (lookup.hasOwnProperty && !lookup.hasOwnProperty(prop)) { continue; } + + // At times we are not allowed to access certain properties for security reasons. + // There are also times where even if we can access them, we are not allowed to access their properties. + obj = tryIsNamespace(lookup, prop); + if (obj) { + obj[NAME_KEY] = prop; } + } + } - return this._super(); - }, + var NAME_KEY = Ember.NAME_KEY = GUID_KEY + '_name'; - isSorted: computed.notEmpty('sortProperties'), + function superClassString(mixin) { + var superclass = mixin.superclass; + if (superclass) { + if (superclass[NAME_KEY]) { return superclass[NAME_KEY]; } + else { return superClassString(superclass); } + } else { + return; + } + } - /** - 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. + function classToString() { + if (!Ember.BOOTED && !this[NAME_KEY]) { + processAllNamespaces(); + } - @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; + var ret; - 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); + 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); + } - return content; - }), + return ret; + } - _contentWillChange: beforeObserver('content', function() { - var content = get(this, 'content'); - var sortProperties = get(this, 'sortProperties'); + function processAllNamespaces() { + var unprocessedNamespaces = !Namespace.PROCESSED; + var unprocessedMixins = Ember.anyUnprocessedMixins; - if (content && sortProperties) { - forEach(content, function(item) { - forEach(sortProperties, function(sortProperty) { - removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); - }, this); - }, this); + if (unprocessedNamespaces) { + findNamespaces(); + Namespace.PROCESSED = true; + } + + if (unprocessedNamespaces || unprocessedMixins) { + var namespaces = Namespace.NAMESPACES; + var namespace; + + for (var i=0, l=namespaces.length; i 0) { - arrangedContent.removeObject(item); - this.insertItemSorted(item); + lastIndexOf: lastIndexOf, + + copy: function(deep) { + if (deep) { + return this.map(function(item) { return copy(item, true); }); } - }, - _binarySearch: function(item, low, high) { - var mid, midItem, res, arrangedContent; + return this.slice(); + } + }); - if (low === high) { - return low; - } + // 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); + }); - arrangedContent = get(this, 'arrangedContent'); + if (ignore.length > 0) { + NativeArray = NativeArray.without.apply(NativeArray, ignore); + } - mid = low + Math.floor((high - low) / 2); - midItem = arrangedContent.objectAt(mid); + /** + 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`. - res = this.orderBy(midItem, item); + Example - if (res < 0) { - return this._binarySearch(item, mid+1, high); - } else if (res > 0) { - return this._binarySearch(item, low, mid); + ```js + var Pagination = Ember.CollectionView.extend({ + tagName: 'ul', + classNames: ['pagination'], + + init: function() { + this._super(); + if (!this.get('content')) { + this.set('content', Ember.A()); + } } + }); + ``` - return mid; + @method A + @for Ember + @return {Ember.NativeArray} + */ + var A = function(arr) { + if (arr === undefined) { arr = []; } + return EmberArray.detect(arr) ? arr : NativeArray.apply(arr); + }; + + /** + 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`. + + Example + + ```js + if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Array) { + Ember.NativeArray.activate(); } - }); + ``` + + @method activate + @for Ember.NativeArray + @static + @return {void} + */ + NativeArray.activate = function() { + NativeArray.apply(Array.prototype); + + A = function(arr) { return arr || []; }; + }; + + if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Array) { + NativeArray.activate(); + } + + 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/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__) { +enifed("ember-runtime/system/object", + ["ember-runtime/system/core_object","ember-runtime/mixins/observable","exports"], + function(__dependency1__, __dependency2__, __exports__) { "use strict"; /** @module ember @submodule ember-runtime */ - var Ember = __dependency1__["default"]; - // Ember.lookup, Ember.assert - var get = __dependency2__.get; - var typeOf = __dependency3__.typeOf; - var Mixin = __dependency4__.Mixin; - var computed = __dependency5__.computed; + var CoreObject = __dependency1__["default"]; + var Observable = __dependency2__["default"]; /** - `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. + `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. - @class TargetActionSupport - @namespace Ember - @extends Ember.Mixin + @class Object + @namespace Ember + @extends Ember.CoreObject + @uses Ember.Observable */ - var TargetActionSupport = Mixin.create({ - target: null, - action: null, - actionContext: null, - - targetObject: computed(function() { - var target = get(this, 'target'); - - if (typeOf(target) === "string") { - var value = get(this, target); - if (value === undefined) { value = get(Ember.lookup, target); } - return value; - } else { - return target; - } - }).property('target'), - - actionContextObject: computed(function() { - var actionContext = get(this, 'actionContext'); + var EmberObject = CoreObject.extend(Observable); + EmberObject.toString = function() { + return "Ember.Object"; + }; - if (typeOf(actionContext) === "string") { - var value = get(this, actionContext); - if (value === undefined) { value = get(Ember.lookup, actionContext); } - return value; - } else { - return actionContext; - } - }).property('actionContext'), + __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"]; - /** - 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.ObjectProxy` forwards all properties not defined by the proxy itself + to a proxied `content` object. - ```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 - } + ```javascript + object = Ember.Object.create({ + name: 'Foo' + }); + + proxy = Ember.ObjectProxy.create({ + content: object }); + + // Access and change existing properties + proxy.get('name') // 'Foo' + proxy.set('name', 'Bar'); + object.get('name') // 'Bar' + + // Create new 'description' property on `object` + proxy.set('description', 'Foo is a whizboo baz'); + object.get('description') // 'Foo is a whizboo baz' ``` - The `target`, `action`, and `actionContext` can be provided as properties of - an optional object argument to `triggerAction` as well. + While `content` is unset, setting a property to be delegated will throw an + Error. ```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 - } + 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 `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: + Delegated properties can be bound to and will change when content is updated. + + Computed properties on the proxy itself can depend on delegated properties. ```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 - } + 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') }); + + 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' ``` - @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'); - var target = opts.target || get(this, 'targetObject'); - var actionContext = opts.actionContext; + @class ObjectProxy + @namespace Ember + @extends Ember.Object + @extends Ember._ProxyMixin + */ - function args(options, actionName) { - var ret = []; - if (actionName) { ret.push(actionName); } + __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; - return ret.concat(options); - } + var Service; - if (typeof actionContext === 'undefined') { - actionContext = get(this, 'actionContextObject') || this; - } + + /** + @class Service + @namespace Ember + @extends Ember.Object + */ + Service = Object.extend(); - if (target && action) { - var ret; + /** + 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. - 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)); - } + Example: - if (ret !== false) ret = true; + ```javascript + App.ApplicationRoute = Ember.Route.extend({ + authManager: Ember.inject.service('auth'), - return ret; - } else { - return false; - } - } - }); + model: function() { + return this.get('authManager').findCurrentUser(); + } + }); + ``` - __exports__["default"] = TargetActionSupport; - }); -enifed("ember-runtime/system/application", - ["ember-runtime/system/namespace","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Namespace = __dependency1__["default"]; + 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. - __exports__["default"] = Namespace.extend(); + @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/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__) { +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"; - var Ember = __dependency1__["default"]; - // Ember.K, 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"]; - /** @module ember @submodule ember-runtime */ + var Ember = __dependency1__["default"]; + // Ember.isNone, Ember.A - var OUT_OF_RANGE_EXCEPTION = "Index out of range"; - var EMPTY = []; - var K = Ember.K; + 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; /** - 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. + An unordered collection of objects. - A simple example of usage: + 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. - ```javascript - var pets = ['dog', 'cat', 'fish']; - var ap = Ember.ArrayProxy.create({ content: Ember.A(pets) }); + All Sets are observable via the Enumerable Observer API - which works + on any enumerable object including both Sets and Arrays. - ap.get('firstObject'); // 'dog' - ap.set('content', ['amoeba', 'paramecium']); - ap.get('firstObject'); // 'amoeba' - ``` + ## Creating a Set - 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`: + 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. + + 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 - var pets = ['dog', 'cat', 'fish']; - var ap = Ember.ArrayProxy.create({ - content: Ember.A(pets), - objectAtContent: function(idx) { - return this.get('content').objectAt(idx).toUpperCase(); - } - }); + // creates a new empty set + var foundNames = new Ember.Set(); - ap.get('firstObject'); // . 'DOG' + // creates a set with four names in it. + var names = new Ember.Set(["Charles", "Tom", "Juan", "Alex"]); // :P + + // creates a copy of the names set. + var namesCopy = new Ember.Set(names); + + // same as above. + var anotherNamesCopy = names.copy(); ``` - @class ArrayProxy + ## Adding/Removing Objects + + 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. + + 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. + + NOTE: You cannot add/remove `null` or `undefined` to a set. Any attempt to do + so will be ignored. + + 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. + + ## Testing for an Object + + To test for an object's presence in a set you simply call + `Ember.Set#contains()`. + + ## Observing 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. + + 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. + + ## Other Methods + + `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. + + 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. + + @class Set @namespace Ember - @extends Ember.Object - @uses Ember.MutableArray + @extends Ember.CoreObject + @uses Ember.MutableEnumerable + @uses Ember.Copyable + @uses Ember.Freezable + @since Ember 0.9 + @deprecated */ - var ArrayProxy = EmberObject.extend(MutableArray, { + __exports__["default"] = CoreObject.extend(MutableEnumerable, Copyable, Freezable, { + + // .......................................................... + // IMPLEMENT ENUMERABLE APIS + // /** - The content array. Must be an object that implements `Ember.Array` and/or - `Ember.MutableArray.` + This property will change as the number of objects in the set changes. - @property content - @type Ember.Array + @property length + @type number + @default 0 */ - content: null, + length: 0, /** - 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. + Clears the set. This is useful if you want to reuse an existing set + without having to recreate it. - @property arrangedContent + ```javascript + var colors = new Ember.Set(["red", "green", "blue"]); + colors.length; // 3 + colors.clear(); + colors.length; // 0 + ``` + + @method clear + @return {Ember.Set} An empty Set */ - arrangedContent: alias('content'), + clear: function() { + if (this.isFrozen) { throw new EmberError(FROZEN_ERROR); } - /** - 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. + var len = get(this, 'length'); + if (len === 0) { return this; } - This method will only be called if content is non-`null`. + var guid; - @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); - }, + this.enumerableContentWillChange(len, 0); + propertyWillChange(this, 'firstObject'); + propertyWillChange(this, 'lastObject'); - /** - 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. + for (var i=0; i < len; i++) { + guid = guidFor(this[i]); + delete this[guid]; + delete this[i]; + } - This method will only be called if content is non-`null`. + set(this, 'length', 0); - @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); + propertyDidChange(this, 'firstObject'); + propertyDidChange(this, 'lastObject'); + this.enumerableContentDidChange(len, 0); + + return this; }, /** - Invoked when the content property is about to change. Notifies observers that the - entire array content will change. + Returns true if the passed object is also an enumerable that contains the + same objects as the receiver. - @private - @method _contentWillChange + ```javascript + var colors = ["red", "green", "blue"], + same_colors = new Ember.Set(colors); + + same_colors.isEqual(colors); // true + same_colors.isEqual(["purple", "brown"]); // false + ``` + + @method isEqual + @param {Ember.Set} obj the other object. + @return {Boolean} */ - _contentWillChange: beforeObserver('content', function() { - this._teardownContent(); - }), + isEqual: function(obj) { + // fail fast + if (!Enumerable.detect(obj)) return false; - _teardownContent: function() { - var content = get(this, 'content'); + var loc = get(this, 'length'); + if (get(obj, 'length') !== loc) return false; - if (content) { - content.removeArrayObserver(this, { - willChange: 'contentArrayWillChange', - didChange: 'contentArrayDidChange' - }); + while(--loc >= 0) { + if (!obj.contains(this[loc])) return false; } + + return true; }, /** - Override to implement content array `willChange` observer. + 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. - @method contentArrayWillChange + This is an alias for `Ember.MutableEnumerable.addObject()`. - @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 + ```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. */ - contentArrayWillChange: K, + add: aliasMethod('addObject'), + /** - Override to implement content array `didChange` observer. + 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()`. - @method contentArrayDidChange + ```javascript + var colors = new Ember.Set(["red", "green", "blue"]); + colors.remove("red"); // ["blue", "green"] + colors.remove("purple"); // ["blue", "green"] + colors.remove(null); // ["blue", "green"] + ``` - @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 + @method remove + @param {Object} obj The object to remove + @return {Ember.Set} The set itself. */ - contentArrayDidChange: K, + remove: aliasMethod('removeObject'), /** - Invoked when the content property changes. Notifies observers that the - entire array content has changed. + Removes the last element from the set and returns it, or `null` if it's empty. - @private - @method _contentDidChange + ```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. */ - _contentDidChange: observer('content', function() { - var content = get(this, 'content'); + 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; + }, - Ember.assert("Can't set ArrayProxy's content to itself", content !== this); + /** + Inserts the given object on to the end of the set. It returns + the set itself. - this._setupContent(); - }), + This is an alias for `Ember.MutableEnumerable.addObject()`. - _setupContent: function() { - var content = get(this, 'content'); + ```javascript + var colors = new Ember.Set(); + colors.push("red"); // ["red"] + colors.push("green"); // ["red", "green"] + colors.push("blue"); // ["red", "green", "blue"] + ``` - if (content) { - Ember.assert(fmt('ArrayProxy expects an Array or ' + - 'Ember.ArrayProxy, but you passed %@', [typeof content]), - isArray(content) || content.isDestroyed); + @method push + @return {Ember.Set} The set itself. + */ + push: aliasMethod('addObject'), - content.addArrayObserver(this, { - willChange: 'contentArrayWillChange', - didChange: 'contentArrayDidChange' - }); - } - }, + /** + Removes the last element from the set and returns it, or `null` if it's empty. - _arrangedContentWillChange: beforeObserver('arrangedContent', function() { - var arrangedContent = get(this, 'arrangedContent'); - var len = arrangedContent ? get(arrangedContent, 'length') : 0; + This is an alias for `Ember.Set.pop()`. - this.arrangedContentArrayWillChange(this, 0, len, undefined); - this.arrangedContentWillChange(this); + ```javascript + var colors = new Ember.Set(["green", "blue"]); + colors.shift(); // "blue" + colors.shift(); // "green" + colors.shift(); // null + ``` - this._teardownArrangedContent(arrangedContent); - }), + @method shift + @return {Object} The removed object from the set or null. + */ + shift: aliasMethod('pop'), - _arrangedContentDidChange: observer('arrangedContent', function() { - var arrangedContent = get(this, 'arrangedContent'); - var len = arrangedContent ? get(arrangedContent, 'length') : 0; + /** + Inserts the given object on to the end of the set. It returns + the set itself. - Ember.assert("Can't set ArrayProxy's content to itself", arrangedContent !== this); + This is an alias of `Ember.Set.push()` - this._setupArrangedContent(); + ```javascript + var colors = new Ember.Set(); + colors.unshift("red"); // ["red"] + colors.unshift("green"); // ["red", "green"] + colors.unshift("blue"); // ["red", "green", "blue"] + ``` - this.arrangedContentDidChange(this); - this.arrangedContentArrayDidChange(this, 0, undefined, len); - }), + @method unshift + @return {Ember.Set} The set itself. + */ + unshift: aliasMethod('push'), - _setupArrangedContent: function() { - var arrangedContent = get(this, 'arrangedContent'); + /** + Adds each object in the passed enumerable to the set. - if (arrangedContent) { - Ember.assert(fmt('ArrayProxy expects an Array or ' + - 'Ember.ArrayProxy, but you passed %@', [typeof arrangedContent]), - isArray(arrangedContent) || arrangedContent.isDestroyed); + This is an alias of `Ember.MutableEnumerable.addObjects()` - arrangedContent.addArrayObserver(this, { - willChange: 'arrangedContentArrayWillChange', - didChange: 'arrangedContentArrayDidChange' - }); - } - }, + ```javascript + var colors = new Ember.Set(); + colors.addEach(["red", "green", "blue"]); // ["red", "green", "blue"] + ``` - _teardownArrangedContent: function() { - var arrangedContent = get(this, 'arrangedContent'); + @method addEach + @param {Ember.Enumerable} objects the objects to add. + @return {Ember.Set} The set itself. + */ + addEach: aliasMethod('addObjects'), - if (arrangedContent) { - arrangedContent.removeArrayObserver(this, { - willChange: 'arrangedContentArrayWillChange', - didChange: 'arrangedContentArrayDidChange' - }); - } - }, + /** + Removes each object in the passed enumerable to the set. - arrangedContentWillChange: K, - arrangedContentDidChange: K, + This is an alias of `Ember.MutableEnumerable.removeObjects()` - objectAt: function(idx) { - return get(this, 'content') && this.objectAtContent(idx); - }, + ```javascript + var colors = new Ember.Set(["red", "green", "blue"]); + colors.removeEach(["red", "blue"]); // ["green"] + ``` - length: computed(function() { - var arrangedContent = get(this, 'arrangedContent'); - return arrangedContent ? get(arrangedContent, 'length') : 0; - // No dependencies since Enumerable notifies length of change - }), + @method removeEach + @param {Ember.Enumerable} objects the objects to remove. + @return {Ember.Set} The set itself. + */ + removeEach: aliasMethod('removeObjects'), - _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; - }, + // .......................................................... + // PRIVATE ENUMERABLE SUPPORT + // - 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."); - } + init: function(items) { + Ember.deprecate('Ember.Set is deprecated and will be removed in a future release.'); + this._super(); + if (items) this.addObjects(items); }, - _insertAt: function(idx, object) { - if (idx > get(this, 'content.length')) throw new EmberError(OUT_OF_RANGE_EXCEPTION); - this._replace(idx, 0, [object]); - return this; + // implement Ember.Enumerable + nextObject: function(idx) { + return this[idx]; }, - 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."); - } - }, + // more optimized version + firstObject: computed(function() { + return this.length > 0 ? this[0] : undefined; + }), - removeAt: function(start, len) { - if ('number' === typeof start) { - var content = get(this, 'content'); - var arrangedContent = get(this, 'arrangedContent'); - var indices = []; - var i; + // more optimized version + lastObject: computed(function() { + return this.length > 0 ? this[this.length-1] : undefined; + }), - if ((start < 0) || (start >= get(this, 'length'))) { - throw new EmberError(OUT_OF_RANGE_EXCEPTION); - } + // implements Ember.MutableEnumerable + addObject: function(obj) { + if (get(this, 'isFrozen')) throw new EmberError(FROZEN_ERROR); + if (isNone(obj)) return this; // nothing to do - if (len === undefined) len = 1; + var guid = guidFor(obj); + var idx = this[guid]; + var len = get(this, 'length'); + var added; - // Get a list of indices in original content to remove - for (i=start; i=0 && idx=0 && idx=0; }, - init: function() { - this._super(); - this._setupContent(); - this._setupArrangedContent(); + 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; }, - willDestroy: function() { - this._teardownArrangedContent(); - this._teardownContent(); + toString: function() { + var len = this.length, idx, array = []; + for(idx = 0; idx < len; idx++) { + array[idx] = this[idx]; + } + return fmt("Ember.Set<%@>", [array.join(',')]); } }); - - __exports__["default"] = ArrayProxy; }); -enifed("ember-runtime/system/container", - ["ember-metal/property_set","container","exports"], - function(__dependency1__, __dependency2__, __exports__) { +enifed("ember-runtime/system/string", + ["ember-metal/core","ember-metal/utils","ember-metal/cache","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; - var set = __dependency1__.set; - var Container = __dependency2__["default"]; - - Container.set = set; - - __exports__["default"] = Container; - }); -enifed("ember-runtime/system/core_object", - ["ember-metal/core","ember-metal/property_get","ember-metal/utils","ember-metal/platform","ember-metal/chains","ember-metal/events","ember-metal/mixin","ember-metal/enumerable_utils","ember-metal/error","ember-metal/keys","ember-runtime/mixins/action_handler","ember-metal/properties","ember-metal/binding","ember-metal/computed","ember-metal/injected_property","ember-metal/run_loop","ember-metal/watching","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __exports__) { - // Remove "use strict"; from transpiled module until - // https://bugs.webkit.org/show_bug.cgi?id=138038 is fixed - // - // REMOVE_USE_STRICT: true - /** - @module ember - @submodule ember-runtime + @module ember + @submodule ember-runtime */ - var Ember = __dependency1__["default"]; - // Ember.assert, Ember.K, Ember.config - - // 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 guidFor = __dependency3__.guidFor; - var apply = __dependency3__.apply; - var o_create = __dependency4__.create; - var generateGuid = __dependency3__.generateGuid; - var GUID_KEY = __dependency3__.GUID_KEY; - var meta = __dependency3__.meta; - var makeArray = __dependency3__.makeArray; - var finishChains = __dependency5__.finishChains; - var sendEvent = __dependency6__.sendEvent; - var IS_BINDING = __dependency7__.IS_BINDING; - var Mixin = __dependency7__.Mixin; - var required = __dependency7__.required; - var indexOf = __dependency8__.indexOf; - var EmberError = __dependency9__["default"]; - var o_defineProperty = __dependency4__.defineProperty; - var keys = __dependency10__["default"]; - var ActionHandler = __dependency11__["default"]; - var defineProperty = __dependency12__.defineProperty; - var Binding = __dependency13__.Binding; - var ComputedProperty = __dependency14__.ComputedProperty; - var computed = __dependency14__.computed; - var InjectedProperty = __dependency15__["default"]; - var run = __dependency16__["default"]; - var destroy = __dependency17__.destroy; - var K = __dependency1__.K; - var hasPropertyAccessors = __dependency4__.hasPropertyAccessors; - - var schedule = run.schedule; - var applyMixin = Mixin._apply; - var finishPartial = Mixin.finishPartial; - var reopen = Mixin.prototype.reopen; - var hasCachedComputedProperties = false; + // Ember.STRINGS, Ember.FEATURES + var isArray = __dependency2__.isArray; + var emberInspect = __dependency2__.inspect; - var undefinedDescriptor = { - configurable: true, - writable: true, - enumerable: false, - value: undefined - }; + var Cache = __dependency3__["default"]; - var nullDescriptor = { - configurable: true, - writable: true, - enumerable: false, - value: null - }; + var STRING_DASHERIZE_REGEXP = (/[ _]/g); - function makeCtor() { + var STRING_DASHERIZE_CACHE = new Cache(1000, function(key) { + return decamelize(key).replace(STRING_DASHERIZE_REGEXP, '-'); + }); - // 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. + 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(); + }); + }); - var wasApplied = false; - var initMixins, initProperties; + var CLASSIFY_CACHE = new Cache(1000, function(str) { + var parts = str.split("."); + var out = []; - var Class = function() { - if (!wasApplied) { - Class.proto(); // prepare prototype... - } - o_defineProperty(this, GUID_KEY, nullDescriptor); - o_defineProperty(this, '__nextSuper', undefinedDescriptor); - var m = meta(this); - var 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; + for (var i=0, l=parts.length; i 2) { + cachedFormats = new Array(arguments.length - 1); - 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; - } + for (var i = 1, l = arguments.length; i < l; i++) { + cachedFormats[i - 1] = arguments[i]; + } + } - var desc = m.descs[keyName]; + // 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); + }); + } - 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))); + function loc(str, formats) { + if (!isArray(formats) || arguments.length > 2) { + formats = Array.prototype.slice.call(arguments, 1); + } - if (concatenatedProperties && - concatenatedProperties.length > 0 && - indexOf(concatenatedProperties, keyName) >= 0) { - var baseValue = this[keyName]; + str = Ember.STRINGS[str] || str; + return fmt(str, formats); + } - if (baseValue) { - if ('function' === typeof baseValue.concat) { - value = baseValue.concat(value); - } else { - value = makeArray(baseValue).concat(value); - } - } else { - value = makeArray(value); - } - } + function w(str) { + return str.split(/\s+/); + } - 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; - } - } - } - } - } - } - finishPartial(this, m); - var length = arguments.length; - var args = new Array(length); - for (var x = 0; x < length; x++) { - args[x] = arguments[x]; - } - apply(this, this.init, args); - m.proto = proto; - finishChains(this); - sendEvent(this, "init"); - }; + function decamelize(str) { + return DECAMELIZE_CACHE.get(str); + } - Class.toString = Mixin.prototype.toString; - Class.willReopen = function() { - if (wasApplied) { - Class.PrototypeMixin = Mixin.create(Class.PrototypeMixin); - } + function dasherize(str) { + return STRING_DASHERIZE_CACHE.get(str); + } - wasApplied = false; - }; - Class._initMixins = function(args) { initMixins = args; }; - Class._initProperties = function(args) { initProperties = args; }; + function camelize(str) { + return CAMELIZE_CACHE.get(str); + } - Class.proto = function() { - var superclass = Class.superclass; - if (superclass) { superclass.proto(); } + function classify(str) { + return CLASSIFY_CACHE.get(str); + } - if (!wasApplied) { - wasApplied = true; - Class.PrototypeMixin.applyPartial(Class.prototype); - } + function underscore(str) { + return UNDERSCORE_CACHE.get(str); + } - return this.prototype; - }; + function capitalize(str) { + return CAPITALIZE_CACHE.get(str); + } - return Class; + /** + 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 = {}; /** - @class CoreObject + 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 String @namespace Ember + @static */ - 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; - }, - + __exports__["default"] = { /** - An overridable method called when objects are instantiated. By default, - does nothing unless it is overridden during class definition. + 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. - Example: + Ordered insertions are most useful when building loc strings where values + you need to insert may appear in different orders. ```javascript - App.Person = Ember.Object.extend({ - init: function() { - alert('Name is ' + this.get('name')); - } - }); - - var steve = App.Person.create({ - name: "Steve" - }); - - // alerts 'Name is Steve'. + "Hello %@ %@".fmt('John', 'Doe'); // "Hello John Doe" + "Hello %@2, %@1".fmt('John', 'Doe'); // "Hello Doe, John" ``` - 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 + @method fmt + @param {String} str The string to format + @param {Array} formats An array of parameters to interpolate into string. + @return {String} formatted string */ - init: function() {}, + fmt: fmt, /** - Defines the properties that will be concatenated from the superclass - (instead of overridden). - - 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`. + 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. - Here is some sample code showing the difference between a concatenated - property and a normal one: + 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. ```javascript - App.BarView = Ember.View.extend({ - someNonConcatenatedProperty: ['bar'], - classNames: ['bar'] - }); - - App.FooBarView = App.BarView.extend({ - someNonConcatenatedProperty: ['foo'], - classNames: ['foo'] - }); + Ember.STRINGS = { + '_Hello World': 'Bonjour le monde', + '_Hello %@ %@': 'Bonjour %@ %@' + }; - var fooBarView = App.FooBarView.create(); - fooBarView.get('someNonConcatenatedProperty'); // ['foo'] - fooBarView.get('classNames'); // ['ember-view', 'bar', 'foo'] + Ember.String.loc("_Hello World"); // 'Bonjour le monde'; + Ember.String.loc("_Hello %@ %@", ["John", "Smith"]); // "Bonjour John Smith"; ``` - This behavior extends to object creation as well. Continuing the - above example: + @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, - ```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: + /** + 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`. ```javascript - var view = App.FooBarView.create({ - classNames: 'baz' - }) - view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz'] - ``` - - Using the `concatenatedProperties` property, we can tell to Ember that mix - the content of the properties. - - In `Ember.View` the `classNameBindings` and `attributeBindings` properties - are also concatenated, in addition to `classNames`. + Ember.String.w("alpha beta gamma").forEach(function(key) { + console.log(key); + }); - 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). + // > alpha + // > beta + // > gamma + ``` - @property concatenatedProperties - @type Array - @default null + @method w + @param {String} str The string to split + @return {Array} array containing the split strings */ - concatenatedProperties: null, + w: w, /** - Destroyed object property flag. + Converts a camelized string into all lower case separated by underscores. - if this property is `true` the observers and bindings were already - removed by the effect of calling the `destroy()` method. + ```javascript + 'innerHTML'.decamelize(); // 'inner_html' + 'action_name'.decamelize(); // 'action_name' + 'css-class-name'.decamelize(); // 'css-class-name' + 'my favorite items'.decamelize(); // 'my favorite items' + ``` - @property isDestroyed - @default false + @method decamelize + @param {String} str The string to decamelize. + @return {String} the decamelized string. */ - isDestroyed: false, + decamelize: decamelize, /** - Destruction scheduled flag. The `destroy()` method has been called. + Replaces underscores, spaces, or camelCase with dashes. - The object stays intact until the end of the run loop at which point - the `isDestroyed` flag is set. + ```javascript + 'innerHTML'.dasherize(); // 'inner-html' + 'action_name'.dasherize(); // 'action-name' + 'css-class-name'.dasherize(); // 'css-class-name' + 'my favorite items'.dasherize(); // 'my-favorite-items' + ``` - @property isDestroying - @default false + @method dasherize + @param {String} str The string to dasherize. + @return {String} the dasherized string. */ - isDestroying: false, + dasherize: dasherize, /** - Destroys an object by setting the `isDestroyed` flag and removing its - metadata, which effectively destroys observers and bindings. - - If you try to set a property on a destroyed object, an exception will be - raised. + Returns the lowerCamelCase form of a string. - Note that destruction is scheduled for the end of the run loop and does not - happen immediately. It will set an isDestroying flag immediately. + ```javascript + 'innerHTML'.camelize(); // 'innerHTML' + 'action_name'.camelize(); // 'actionName' + 'css-class-name'.camelize(); // 'cssClassName' + 'my favorite items'.camelize(); // 'myFavoriteItems' + 'My Favorite Items'.camelize(); // 'myFavoriteItems' + ``` - @method destroy - @return {Ember.Object} receiver + @method camelize + @param {String} str The string to camelize. + @return {String} the camelized string. */ - destroy: function() { - if (this.isDestroying) { return; } - this.isDestroying = true; - - schedule('actions', this, this.willDestroy); - schedule('destroy', this, this._scheduledDestroy); - return this; - }, + camelize: camelize, /** - Override to implement teardown. - - @method willDestroy - */ - willDestroy: K, + Returns the UpperCamelCase form of a string. - /** - Invoked by the run loop to actually destroy the object. This is - scheduled for execution by the `destroy` method. + ```javascript + 'innerHTML'.classify(); // 'InnerHTML' + 'action_name'.classify(); // 'ActionName' + 'css-class-name'.classify(); // 'CssClassName' + 'my favorite items'.classify(); // 'MyFavoriteItems' + ``` - @private - @method _scheduledDestroy + @method classify + @param {String} str the string to classify + @return {String} the classified string */ - _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; - }, + classify: classify, /** - Returns a string representation which attempts to provide more information - than Javascript's `toString` typically does, in a generic way for all Ember - objects. + More general than decamelize. Returns the lower\_case\_and\_underscored + form of a string. ```javascript - App.Person = Em.Object.extend() - person = App.Person.create() - person.toString() //=> "" + 'innerHTML'.underscore(); // 'inner_html' + 'action_name'.underscore(); // 'action_name' + 'css-class-name'.underscore(); // 'css_class_name' + 'my favorite items'.underscore(); // 'my_favorite_items' ``` - If the object's class is not defined on an Ember namespace, it will - indicate it is a subclass of the registered superclass: - - ```javascript - Student = App.Person.extend() - student = Student.create() - student.toString() //=> "<(subclass of App.Person):ember1025>" - ``` + @method underscore + @param {String} str The string to underscore. + @return {String} the underscored string. + */ + underscore: underscore, - If the method `toStringExtension` is defined, its return value will be - included in the output. + /** + Returns the Capitalized form of a string ```javascript - App.Teacher = App.Person.extend({ - toStringExtension: function() { - return this.get('fullName'); - } - }); - teacher = App.Teacher.create() - teacher.toString(); //=> "" + 'innerHTML'.capitalize() // 'InnerHTML' + 'action_name'.capitalize() // 'Action_name' + 'css-class-name'.capitalize() // 'Css-class-name' + 'my favorite items'.capitalize() // 'My favorite items' ``` - @method toString - @return {String} string representation + @method capitalize + @param {String} str The string to capitalize. + @return {String} The capitalized string. */ - toString: function toString() { - var hasToStringExtension = typeof this.toStringExtension === 'function'; - var extension = hasToStringExtension ? ":" + this.toStringExtension() : ''; - var ret = '<'+this.constructor.toString()+':'+guidFor(this)+extension+'>'; - - this.toString = makeToString(ret); - return ret; - } - }); + capitalize: capitalize + }; - CoreObject.PrototypeMixin.ownerConstructor = CoreObject; + __exports__.fmt = fmt; + __exports__.loc = loc; + __exports__.w = w; + __exports__.decamelize = decamelize; + __exports__.dasherize = dasherize; + __exports__.camelize = camelize; + __exports__.classify = classify; + __exports__.underscore = underscore; + __exports__.capitalize = capitalize; + }); +enifed("ember-runtime/system/subarray", + ["ember-metal/error","ember-metal/enumerable_utils","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var EmberError = __dependency1__["default"]; + var EnumerableUtils = __dependency2__["default"]; - function makeToString(ret) { - return function() { return ret; }; - } + var RETAIN = 'r'; + var FILTER = 'f'; - if (Ember.config.overridePrototypeMixin) { - Ember.config.overridePrototypeMixin(CoreObject.PrototypeMixin); + function Operation(type, count) { + this.type = type; + this.count = count; } - CoreObject.__super__ = null; - - var ClassMixinProps = { + __exports__["default"] = SubArray; - ClassMixin: required(), + /** + 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. - PrototypeMixin: required(), + @class SubArray + @namespace Ember + */ + function SubArray (length) { + if (arguments.length < 1) { length = 0; } - isClass: true, + if (length > 0) { + this._operations = [new Operation(RETAIN, length)]; + } else { + this._operations = []; + } + } - isMethod: false, + SubArray.prototype = { /** - Creates a new subclass. + Track that an item was added to the tracked array. - ```javascript - App.Person = Ember.Object.extend({ - say: function(thing) { - alert(thing); - } - }); - ``` + @method addItem - This defines a new subclass of Ember.Object: `App.Person`. It contains one method: `say()`. + @param {Number} index The index of the item in the tracked array. + @param {Boolean} match `true` iff the item is included in the subarray. - 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: + @return {number} The index of the item in the subarray. + */ + addItem: function(index, match) { + var returnValue = -1; + var itemType = match ? RETAIN : FILTER; + var self = this; - ```javascript - App.PersonView = Ember.View.extend({ - tagName: 'li', - classNameBindings: ['isAdministrator'] - }); - ``` + this._findOperation(index, function(operation, operationIndex, rangeStart, rangeEnd, seenInSubArray) { + var newOperation, splitOperation; - When defining a subclass, you can override methods but still access the implementation of your parent class by calling the special `_super()` method: + 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; - ```javascript - App.Person = Ember.Object.extend({ - say: function(thing) { - var name = this.get('name'); - alert(name + ' says: ' + thing); + self._operations.splice(operationIndex + 1, 0, newOperation, splitOperation); } - }); - App.Soldier = App.Person.extend({ - say: function(thing) { - this._super(thing + ", sir!"); - }, - march: function(numberOfHours) { - alert(this.get('name') + ' marches for ' + numberOfHours + ' hours.') + if (match) { + if (operation.type === RETAIN) { + returnValue = seenInSubArray + (index - rangeStart); + } else { + returnValue = seenInSubArray; + } } - }); - var yehuda = App.Soldier.create({ - name: "Yehuda Katz" + self._composeAt(operationIndex); + }, function(seenInSubArray) { + self._operations.push(new Operation(itemType, 1)); + + if (match) { + returnValue = seenInSubArray; + } + + self._composeAt(self._operations.length-1); }); - yehuda.say("Yes"); // alerts "Yehuda Katz says: Yes, sir!" - ``` + return returnValue; + }, - 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. + /** + Track that an item was removed from the tracked array. - You can also pass `Mixin` classes to add additional properties to the subclass. + @method removeItem - ```javascript - App.Person = Ember.Object.extend({ - say: function(thing) { - alert(this.get('name') + ' says: ' + thing); - } - }); + @param {Number} index The index of the item in the tracked array. - App.SingingMixin = Mixin.create({ - sing: function(thing){ - alert(this.get('name') + ' sings: la la la ' + thing); + @return {number} The index of the item in the subarray, or `-1` if the item + was not in the subarray. + */ + removeItem: function(index) { + var returnValue = -1; + var self = this; + + this._findOperation(index, function (operation, operationIndex, rangeStart, rangeEnd, seenInSubArray) { + if (operation.type === RETAIN) { + returnValue = seenInSubArray + (index - rangeStart); } - }); - App.BroadwayStar = App.Person.extend(App.SingingMixin, { - dance: function() { - alert(this.get('name') + ' dances: tap tap tap tap '); + 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."); }); - ``` - The `App.BroadwayStar` class contains three methods: `say()`, `sing()`, and `dance()`. + return returnValue; + }, - @method extend - @static - @param {Mixin} [mixins]* One or more Mixin classes - @param {Object} [arguments]* Object containing values to use within the new class - */ - extend: function extend() { - var Class = makeCtor(); - var proto; - Class.ClassMixin = Mixin.create(this.ClassMixin); - Class.PrototypeMixin = Mixin.create(this.PrototypeMixin); + _findOperation: function (index, foundCallback, notFoundCallback) { + var seenInSubArray = 0; + var operationIndex, len, operation, rangeStart, rangeEnd; - Class.ClassMixin.ownerConstructor = Class; - Class.PrototypeMixin.ownerConstructor = Class; + // 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; - reopen.apply(Class.PrototypeMixin, arguments); + if (index >= rangeStart && index <= rangeEnd) { + foundCallback(operation, operationIndex, rangeStart, rangeEnd, seenInSubArray); + return; + } else if (operation.type === RETAIN) { + seenInSubArray += operation.count; + } + } - Class.superclass = this; - Class.__super__ = this.prototype; + notFoundCallback(seenInSubArray); + }, - proto = Class.prototype = o_create(this.prototype); - proto.constructor = Class; - generateGuid(proto); - meta(proto).proto = proto; // this will disable observers on prototype + _composeAt: function(index) { + var op = this._operations[index]; + var otherOp; - Class.ClassMixin.apply(Class); - return Class; - }, + if (!op) { + // Composing out of bounds is a no-op, as when removing the last operation + // in the list. + return; + } - /** - Equivalent to doing `extend(arguments).create()`. - If possible use the normal `create` method instead. + if (index > 0) { + otherOp = this._operations[index-1]; + if (otherOp.type === op.type) { + op.count += otherOp.count; + this._operations.splice(index-1, 1); + --index; + } + } - @method createWithMixins - @static - @param [arguments]* - */ - createWithMixins: function() { - var C = this; - var l= arguments.length; - if (l > 0) { - var args = new Array(l); - for (var i = 0; i < l; i++) { - args[i] = arguments[i]; + 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); } - this._initMixins(args); } - return new C(); }, - /** - 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')); - } + toString: function () { + var str = ""; + EnumerableUtils.forEach(this._operations, function (operation) { + str += " " + operation.type + ":" + operation.count; }); + return str.substring(1); + } + }; + }); +enifed("ember-runtime/system/tracked_array", + ["ember-metal/property_get","ember-metal/enumerable_utils","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var get = __dependency1__.get; + var forEach = __dependency2__.forEach; - var tom = App.Person.create({ - name: 'Tom Dale' - }); + var RETAIN = 'r'; + var INSERT = 'i'; + var DELETE = 'd'; - tom.helloWorld(); // alerts "Hi, my name is Tom Dale". - ``` + __exports__["default"] = TrackedArray; - `create` will call the `init` function if defined during - `Ember.AnyObject.extend` + /** + 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. - If no arguments are passed to `create`, it will not set values to the new - instance during initialization: + @class TrackedArray + @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 = []; } - ```javascript - var noName = App.Person.create(); - noName.helloWorld(); // alerts undefined - ``` + var length = get(items, 'length'); - 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. + if (length) { + this._operations = [new ArrayOperation(RETAIN, length, items)]; + } else { + this._operations = []; + } + } - @method create - @static - @param [arguments]* - */ - create: function() { - var C = this; - var l = arguments.length; - if (l > 0) { - var args = new Array(l); - for (var i = 0; i < l; i++) { - args[i] = arguments[i]; - } - this._initProperties(args); - } - return new C(); - }, + TrackedArray.RETAIN = RETAIN; + TrackedArray.INSERT = INSERT; + TrackedArray.DELETE = DELETE; + + TrackedArray.prototype = { /** - Augments a constructor's prototype with additional - properties and functions: + Track that `newItems` were added to the tracked array at `index`. - ```javascript - MyObject = Ember.Object.extend({ - name: 'an object' - }); + @method addItems + @param index + @param newItems + */ + addItems: function (index, newItems) { + var count = get(newItems, 'length'); + if (count < 1) { return; } - o = MyObject.create(); - o.get('name'); // 'an object' + var match = this._findArrayOperation(index); + var arrayOperation = match.operation; + var arrayOperationIndex = match.index; + var arrayOperationRangeStart = match.rangeStart; + var composeIndex, newArrayOperation; - MyObject.reopen({ - say: function(msg){ - console.log(msg); - } - }) + newArrayOperation = new ArrayOperation(INSERT, count, newItems); - o2 = MyObject.create(); - o2.say("hello"); // logs "hello" + 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; + } - o.say("goodbye"); // logs "goodbye" - ``` + this._composeInsert(composeIndex); + }, - To add functions and properties to the constructor itself, - see `reopenClass` + /** + Track that `count` items were removed at `index`. - @method reopen + @method removeItems + @param index + @param count */ - reopen: function() { - this.willReopen(); + removeItems: function (index, count) { + if (count < 1) { return; } - var l = arguments.length; - var args = new Array(l); - if (l > 0) { - for (var i = 0; i < l; i++) { - args[i] = arguments[i]; - } + var match = this._findArrayOperation(index); + var arrayOperationIndex = match.index; + var arrayOperationRangeStart = match.rangeStart; + var newArrayOperation, composeIndex; + + 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; } - apply(this.PrototypeMixin, reopen, args); - return this; + return this._composeDelete(composeIndex); }, /** - Augments a constructor's own properties and functions: + Apply all operations, reducing them to retain:n, for `n`, the number of + items in the array. - ```javascript - MyObject = Ember.Object.extend({ - name: 'an object' - }); + `callback` will be called for each operation and will be passed the following arguments: - MyObject.reopenClass({ - canBuild: false - }); + * {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}` - MyObject.canBuild; // false - o = MyObject.create(); - ``` + @method apply + @param {Function} callback + */ + apply: function (callback) { + var items = []; + var offset = 0; - 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. + forEach(this._operations, function (arrayOperation, operationIndex) { + callback(arrayOperation.items, offset, arrayOperation.type, operationIndex); - ```javascript - App.Person = Ember.Object.extend({ - name : "", - sayHello : function(){ - alert("Hello. My name is " + this.get('name')); + if (arrayOperation.type !== DELETE) { + offset += arrayOperation.count; + items = items.concat(arrayOperation.items); } }); - App.Person.reopenClass({ - species : "Homo sapiens", - createPerson: function(newPersonsName){ - return App.Person.create({ - name:newPersonsName - }); - } - }); + this._operations = [new ArrayOperation(RETAIN, items.length, items)]; + }, - var tom = App.Person.create({ - name : "Tom Dale" - }); - var yehuda = App.Person.createPerson("Yehuda Katz"); + /** + Return an `ArrayOperationMatch` for the operation that contains the item at `index`. - tom.sayHello(); // "Hello. My name is Tom Dale" - yehuda.sayHello(); // "Hello. My name is Yehuda Katz" - alert(App.Person.species); // "Homo sapiens" - ``` + @method _findArrayOperation - Note that `species` and `createPerson` are *not* valid on the `tom` and `yehuda` - variables. They are only valid on `App.Person`. + @param {Number} index the index of the item whose operation information + should be returned. + @private + */ + _findArrayOperation: function (index) { + var split = false; + var arrayOperationIndex, arrayOperation, + arrayOperationRangeStart, arrayOperationRangeEnd, + len; - To add functions and properties to instances of - a constructor by extending the constructor's prototype - see `reopen` + // 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]; - @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]; + if (arrayOperation.type === DELETE) { continue; } + + arrayOperationRangeEnd = arrayOperationRangeStart + arrayOperation.count - 1; + + if (index === arrayOperationRangeStart) { + break; + } else if (index > arrayOperationRangeStart && index <= arrayOperationRangeEnd) { + split = true; + break; + } else { + arrayOperationRangeStart = arrayOperationRangeEnd + 1; } } - apply(this.ClassMixin, reopen, args); - applyMixin(this, arguments, false); - return this; + return new ArrayOperationMatch(arrayOperation, arrayOperationIndex, split, arrayOperationRangeStart); }, - detect: function(obj) { - if ('function' !== typeof obj) { return false; } - while(obj) { - if (obj===this) { return true; } - obj = obj.superclass; - } - return false; - }, + _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); - detectInstance: function(obj) { - return obj instanceof this; + // truncate LHS + arrayOperation.count = splitIndex; + arrayOperation.items = arrayOperation.items.slice(0, splitIndex); + + this._operations.splice(arrayOperationIndex + 1, 0, newArrayOperation, splitArrayOperation); }, - /** - 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. + // 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; - You can pass a hash of these values to a computed property like this: + if (leftOp === INSERT) { + // merge left + leftArrayOperation.count += newArrayOperation.count; + leftArrayOperation.items = leftArrayOperation.items.concat(newArrayOperation.items); - ```javascript - person: function() { - var personId = this.get('personId'); - return App.Person.create({ id: personId }); - }.property().meta({ type: App.Person }) - ``` + 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); + } + }, - Once you've done this, you can retrieve the values saved to the computed - property from your class like this: + _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 = []; - ```javascript - MyClass.metaForProperty('person'); - ``` + if (leftOp === DELETE) { + arrayOperation = leftArrayOperation; + index -= 1; + } - This will return the original hash that was passed to `meta()`. + for (var i = index + 1; deletesToGo > 0; ++i) { + nextArrayOperation = this._operations[i]; + nextOp = nextArrayOperation.type; + nextCount = nextArrayOperation.count; - @method metaForProperty - @param key {String} property name - */ - metaForProperty: function(key) { - var meta = this.proto()['__ember_meta__']; - var desc = meta && meta.descs[key]; + if (nextOp === DELETE) { + arrayOperation.count += nextCount; + continue; + } - Ember.assert("metaForProperty() could not find a computed property with key '"+key+"'.", !!desc && desc instanceof ComputedProperty); - return desc._meta || {}; - }, + 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; - _computedProperties: computed(function() { - hasCachedComputedProperties = true; - var proto = this.proto(); - var descs = meta(proto).descs; - var property; - var properties = []; + // 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; - for (var name in descs) { - property = descs[name]; + 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; + } - if (property instanceof ComputedProperty) { - properties.push({ - name: name, - meta: property._meta - }); + if (nextOp === INSERT) { + // d:2 i:3 will result in delete going away + arrayOperation.count -= nextCount; } } - return properties; - }).readOnly(), - - /** - Iterate over each computed property for the class, passing its name - and any associated metadata (see `metaForProperty`) to the callback. - @method eachComputedProperty - @param {Function} callback - @param {Object} binding - */ - eachComputedProperty: function(callback, binding) { - var property, name; - var empty = {}; + if (arrayOperation.count > 0) { + // compose our new delete with possibly several operations to the right of + // disparate types + this._operations.splice(index+1, i-1-index); + } else { + // The delete operation can go away; it has merely reduced some other + // operation, as in d:3 i:4; it may also have eliminated that operation, + // as in d:3 i:3. + this._operations.splice(index, removeNewAndNextOp ? 2 : 1); + } - var properties = get(this, '_computedProperties'); + return removedItems; + }, - 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); - } + toString: function () { + var str = ""; + forEach(this._operations, function (operation) { + str += " " + operation.type + ":" + operation.count; + }); + return str.substring(1); } }; - - var ClassMixin = Mixin.create(ClassMixinProps); + /** + Internal data structure to represent an array operation. - ClassMixin.ownerConstructor = CoreObject; + @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; + } + + /** + Internal data structure used to include information when looking up operations + by item index. - if (Ember.config.overrideClassMixin) { - Ember.config.overrideClassMixin(ClassMixin); + @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","ember-template-compiler/compat","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { + "use strict"; + var _Ember = __dependency1__["default"]; + var precompile = __dependency2__["default"]; + var compile = __dependency3__["default"]; + var template = __dependency4__["default"]; + var registerPlugin = __dependency5__.registerPlugin; - CoreObject.ClassMixin = ClassMixin; + var TransformEachInToHash = __dependency6__["default"]; + var TransformWithAsToHash = __dependency7__["default"]; - ClassMixin.apply(CoreObject); + // used for adding Ember.Handlebars.compile for backwards compat - CoreObject.reopen({ - didDefineProperty: function(proto, key, value) { - if (hasCachedComputedProperties === false) { return; } - if (value instanceof Ember.ComputedProperty) { - var cache = Ember.meta(this.constructor).cache; + registerPlugin('ast', TransformWithAsToHash); + registerPlugin('ast', TransformEachInToHash); - if (cache._computedProperties !== undefined) { - cache._computedProperties = undefined; - } - } - } - }); + __exports__._Ember = _Ember; + __exports__.precompile = precompile; + __exports__.compile = compile; + __exports__.template = template; + __exports__.registerPlugin = registerPlugin; + }); +enifed("ember-template-compiler/compat", + ["ember-metal/core","ember-template-compiler/compat/precompile","ember-template-compiler/system/compile","ember-template-compiler/system/template"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) { + "use strict"; + var Ember = __dependency1__["default"]; + var precompile = __dependency2__["default"]; + var compile = __dependency3__["default"]; + var template = __dependency4__["default"]; - __exports__["default"] = CoreObject; + var EmberHandlebars = Ember.Handlebars = Ember.Handlebars || {}; + + EmberHandlebars.precompile = precompile; + EmberHandlebars.compile = compile; + EmberHandlebars.template = template; }); -enifed("ember-runtime/system/deferred", - ["ember-metal/core","ember-runtime/mixins/deferred","ember-runtime/system/object","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { +enifed("ember-template-compiler/compat/precompile", + ["exports"], + function(__exports__) { "use strict"; - var Ember = __dependency1__["default"]; - var DeferredMixin = __dependency2__["default"]; - var EmberObject = __dependency3__["default"]; + /** + @module ember + @submodule ember-template-compiler + */ - var Deferred = EmberObject.extend(DeferredMixin, { - init: function() { - Ember.deprecate('Usage of Ember.Deferred is deprecated.'); - this._super(); + var compile, compileSpec; + + __exports__["default"] = function(string) { + if ((!compile || !compileSpec) && Ember.__loader.registry['htmlbars-compiler/compiler']) { + var Compiler = requireModule('htmlbars-compiler/compiler'); + + compile = Compiler.compile; + compileSpec = Compiler.compileSpec; } - }); - Deferred.reopenClass({ - promise: function(callback, binding) { - var deferred = Deferred.create(); - callback.call(binding, deferred); - return deferred; + if (!compile || !compileSpec) { + throw new Error('Cannot call `precompile` without the template compiler loaded. Please load `ember-template-compiler.js` prior to calling `precompile`.'); } - }); - __exports__["default"] = Deferred; + var asObject = arguments[1] === undefined ? true : arguments[1]; + var compileFunc = asObject ? compile : compileSpec; + + return compileFunc(string); + } }); -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__) { +enifed("ember-template-compiler/plugins", + ["exports"], + function(__exports__) { "use strict"; /** @module ember - @submodule ember-runtime + @submodule ember-template-compiler */ - var Ember = __dependency1__["default"]; - // Ember.assert - - 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; - - var EachArray = EmberObject.extend(EmberArray, { - - init: function(content, keyName, owner) { - this._super(); - this._keyName = keyName; - this._owner = owner; - this._content = content; - }, + /** + @private + @property helpers + */ + var plugins = { + ast: [ ] + }; - objectAt: function(idx) { - var item = this._content.objectAt(idx); - return item && get(item, this._keyName); - }, + /** + Adds an AST plugin to be used by Ember.HTMLBars.compile. - length: computed(function() { - var content = this._content; - return content ? get(content, 'length') : 0; - }) + @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.'); + } - }); + plugins[type].push(Plugin); + } - var IS_OBSERVER = /^.+:(before|change)$/; + __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 + */ - function addObserverForContentKey(content, keyName, proxy, idx, loc) { - var objects = proxy._objects; - var guid; - if (!objects) objects = proxy._objects = {}; - 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'); + /** + An HTMLBars AST transformation that replaces all instances of - // 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); - } - } - } + ```handlebars + {{#each item in items}} + {{/each}} + ``` - function removeObserverForContentKey(content, keyName, proxy, idx, loc) { - var objects = proxy._objects; - if (!objects) objects = proxy._objects = {}; - var indicies, guid; + with - while(--loc>=idx) { - var item = content.objectAt(loc); - if (item) { - removeBeforeObserver(item, keyName, proxy, 'contentKeyWillChange'); - removeObserver(item, keyName, proxy, 'contentKeyDidChange'); + ```handlebars + {{#each items keyword="item"}} + {{/each}} + ``` - guid = guidFor(item); - indicies = objects[guid]; - indicies[indexOf.call(indicies, loc)] = null; - } - } + @class TransformEachInToHash + @private + */ + function TransformEachInToHash() { + // set later within HTMLBars to the syntax package + this.syntax = null; } /** - 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. - @private - @class EachProxy - @namespace Ember - @extends Ember.Object + @method transform + @param {AST} The AST to be transformed. */ - var EachProxy = EmberObject.extend({ - - init: function(content) { - this._super(); - this._content = content; - content.addArrayObserver(this); + TransformEachInToHash.prototype.transform = function TransformEachInToHash_transform(ast) { + var pluginContext = this; + var walker = new pluginContext.syntax.Walker(); + var b = pluginContext.syntax.builders; - // in case someone is already observing some keys make sure they are - // added - forEach(watchedEvents(this), function(eventName) { - this.didAddListener(eventName); - }, this); - }, + walker.visit(ast, function(node) { + if (pluginContext.validate(node)) { - /** - You can directly access mapped properties by simply requesting them. - The `unknownProperty` handler will generate an EachArray of each item. + if (node.program && node.program.blockParams.length) { + throw new Error('You cannot use keyword (`{{each foo in bar}}`) and block params (`{{each bar as |foo|}}`) at the same time.'); + } - @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; - }, + var removedParams = node.sexpr.params.splice(0, 2); + var keyword = removedParams[0].original; - // .......................................................... - // ARRAY CHANGES - // Invokes whenever the content array itself changes. + // TODO: This may not be necessary. + if (!node.sexpr.hash) { + node.sexpr.hash = b.hash(); + } - arrayWillChange: function(content, idx, removedCnt, addedCnt) { - var keys = this._keys; - var key, lim; + node.sexpr.hash.pairs.push(b.pair( + 'keyword', + b.string(keyword) + )); + } + }); - lim = removedCnt>0 ? idx+removedCnt : -1; - beginPropertyChanges(this); + return ast; + }; - for(key in keys) { - if (!keys.hasOwnProperty(key)) { continue; } + 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 (lim>0) { removeObserverForContentKey(content, key, this, idx, lim); } + __exports__["default"] = TransformEachInToHash; + }); +enifed("ember-template-compiler/plugins/transform-with-as-to-hash", + ["exports"], + function(__exports__) { + "use strict"; + /** + @module ember + @submodule ember-htmlbars + */ - propertyWillChange(this, key); - } + /** + An HTMLBars AST transformation that replaces all instances of - propertyWillChange(this._content, '@each'); - endPropertyChanges(this); - }, + ```handlebars + {{#with foo.bar as bar}} + {{/with}} + ``` - arrayDidChange: function(content, idx, removedCnt, addedCnt) { - var keys = this._keys; - var lim; + with - lim = addedCnt>0 ? idx+addedCnt : -1; - changeProperties(function() { - for(var key in keys) { - if (!keys.hasOwnProperty(key)) { continue; } + ```handlebars + {{#with foo.bar as |bar|}} + {{/with}} + ``` - if (lim>0) { addObserverForContentKey(content, key, this, idx, lim); } + @private + @class TransformWithAsToHash + */ + function TransformWithAsToHash() { + // set later within HTMLBars to the syntax package + this.syntax = null; + } - propertyDidChange(this, key); - } + /** + @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(); - propertyDidChange(this._content, '@each'); - }, this); - }, + walker.visit(ast, function(node) { + if (pluginContext.validate(node)) { - // .......................................................... - // LISTEN FOR NEW OBSERVERS AND OTHER EVENT LISTENERS - // Start monitoring keys based on who is listening... + if (node.program && node.program.blockParams.length) { + throw new Error('You cannot use keyword (`{{with foo as bar}}`) and block params (`{{with foo as |bar|}}`) at the same time.'); + } - didAddListener: function(eventName) { - if (IS_OBSERVER.test(eventName)) { - this.beginObservingContentKey(eventName.slice(0, -7)); + var removedParams = node.sexpr.params.splice(1, 2); + var keyword = removedParams[1].original; + node.program.blockParams = [ keyword ]; } - }, + }); - didRemoveListener: function(eventName) { - if (IS_OBSERVER.test(eventName)) { - this.stopObservingContentKey(eventName.slice(0, -7)); - } - }, + return ast; + }; - // .......................................................... - // CONTENT KEY OBSERVING - // Actual watch keys on the source content. + 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'; + }; - 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'); + __exports__["default"] = TransformWithAsToHash; + }); +enifed("ember-template-compiler/system/compile", + ["ember-template-compiler/system/compile_options","ember-template-compiler/system/template","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-template-compiler + */ - addObserverForContentKey(content, keyName, this, 0, len); - } else { - keys[keyName]++; - } - }, + var compile; + var compileOptions = __dependency1__["default"]; + var template = __dependency2__["default"]; - stopObservingContentKey: function(keyName) { - var keys = this._keys; - if (keys && (keys[keyName]>0) && (--keys[keyName]<=0)) { - var content = this._content; - var len = get(content, 'length'); + /** + Uses HTMLBars `compile` function to process a string into a compiled template. - removeObserverForContentKey(content, keyName, this, 0, len); - } - }, + This is not present in production builds. - contentKeyWillChange: function(obj, keyName) { - propertyWillChange(this, keyName); - }, + @private + @method compile + @param {String} templateString This is the string to be compiled by HTMLBars. + */ + __exports__["default"] = function(templateString) { + if (!compile && Ember.__loader.registry['htmlbars-compiler/compiler']) { + compile = requireModule('htmlbars-compiler/compiler').compile; + } - contentKeyDidChange: function(obj, keyName) { - propertyDidChange(this, keyName); + if (!compile) { + throw new Error('Cannot call `compile` without the template compiler loaded. Please load `ember-template-compiler.js` prior to calling `compile`.'); } - }); - __exports__.EachArray = EachArray; - __exports__.EachProxy = EachProxy; + var templateSpec = compile(templateString, compileOptions()); + + return template(templateSpec); + } }); -enifed("ember-runtime/system/lazy_load", - ["ember-metal/core","ember-metal/array","ember-runtime/system/native_array","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { +enifed("ember-template-compiler/system/compile_options", + ["ember-metal/core","ember-template-compiler/plugins","exports"], + function(__dependency1__, __dependency2__, __exports__) { "use strict"; - /*globals CustomEvent */ + /** + @module ember + @submodule ember-template-compiler + */ var Ember = __dependency1__["default"]; - // Ember.ENV.EMBER_LOAD_HOOKS - var forEach = __dependency2__.forEach; - // make sure Ember.A is setup. + var plugins = __dependency2__["default"]; /** - @module ember - @submodule ember-runtime + @private + @property compileOptions */ + __exports__["default"] = function() { + var disableComponentGeneration = true; + + return { + disableComponentGeneration: disableComponentGeneration, - var loadHooks = Ember.ENV.EMBER_LOAD_HOOKS || {}; - var loaded = {}; - + plugins: plugins + }; + } + }); +enifed("ember-template-compiler/system/precompile", + ["ember-template-compiler/system/compile_options","exports"], + function(__dependency1__, __exports__) { + "use strict"; /** - Detects when a specific package of Ember (e.g. 'Ember.Handlebars') - has fully loaded and is available for extension. - - The provided `callback` will be called with the `name` passed - resolved from a string into the object: - - ``` javascript - Ember.onLoad('Ember.Handlebars' function(hbars) { - hbars.registerHelper(...); - }); - ``` - - @method onLoad - @for Ember - @param name {String} name of hook - @param callback {Function} callback to be called + @module ember + @submodule ember-template-compiler */ - function onLoad(name, callback) { - var object; - loadHooks[name] = loadHooks[name] || Ember.A(); - loadHooks[name].pushObject(callback); + var compileOptions = __dependency1__["default"]; + var compileSpec; - if (object = loaded[name]) { - callback(object); - } - } + /** + Uses HTMLBars `compile` function to process a string into a compiled template string. + The returned string must be passed through `Ember.HTMLBars.template`. - __exports__.onLoad = onLoad;/** - Called when an Ember.js package (e.g Ember.Handlebars) has finished - loading. Triggers any callbacks registered for this event. + This is not present in production builds. - @method runLoadHooks - @for Ember - @param name {String} name of hook - @param object {Object} object to pass to callbacks + @private + @method precompile + @param {String} templateString This is the string to be compiled by HTMLBars. */ - function runLoadHooks(name, object) { - loaded[name] = object; - - if (typeof window === 'object' && typeof window.dispatchEvent === 'function' && typeof CustomEvent === "function") { - var event = new CustomEvent(name, {detail: object, name: name}); - window.dispatchEvent(event); + __exports__["default"] = function(templateString) { + if (!compileSpec && Ember.__loader.registry['htmlbars-compiler/compiler']) { + compileSpec = requireModule('htmlbars-compiler/compiler').compileSpec; } - if (loadHooks[name]) { - forEach.call(loadHooks[name], function(callback) { - callback(object); - }); + if (!compileSpec) { + throw new Error('Cannot call `compileSpec` without the template compiler loaded. Please load `ember-template-compiler.js` prior to calling `compileSpec`.'); } - } - __exports__.runLoadHooks = runLoadHooks; + return compileSpec(templateString, compileOptions()); + } }); -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__) { +enifed("ember-template-compiler/system/template", + ["exports"], + function(__exports__) { "use strict"; /** @module ember - @submodule ember-runtime + @submodule ember-template-compiler */ - // 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; - - var EmberObject = __dependency6__["default"]; - /** - 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. + Augments the detault precompiled output of an HTMLBars template with + additional information needed by Ember. - # Example Usage + @private + @method template + @param {Function} templateSpec This is the compiled HTMLBars template spec. + */ - ```javascript - MyFramework = Ember.Namespace.create({ - VERSION: '1.0.0' - }); - ``` + __exports__["default"] = function(templateSpec) { + templateSpec.isTop = true; + templateSpec.isMethod = false; - @class Namespace - @namespace Ember - @extends Ember.Object - */ - var Namespace = EmberObject.extend({ - isNamespace: true, + 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"]; - init: function() { - Namespace.NAMESPACES.push(this); - Namespace.PROCESSED = false; - }, + // to setup initializer + // to handle various edge cases - toString: function() { - var name = get(this, 'name') || get(this, 'modulePrefix'); - if (name) { return name; } + var setupForTesting = __dependency4__["default"]; + var Test = __dependency5__["default"]; + var Adapter = __dependency6__["default"]; + var QUnitAdapter = __dependency7__["default"]; + // adds helpers to helpers object in Test - findNamespaces(); - return this[NAME_KEY]; - }, + /** + Ember Testing - nameClasses: function() { - processNamespace([this.toString()], this, {}); - }, + @module ember + @submodule ember-testing + @requires ember-application + */ - destroy: function() { - var namespaces = Namespace.NAMESPACES; - var toString = this.toString(); + 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"]; - if (toString) { - Ember.lookup[toString] = undefined; - delete Namespace.NAMESPACES_BY_ID[toString]; - } - namespaces.splice(indexOf.call(namespaces, this), 1); - this._super(); - } - }); + function K() { return this; } - Namespace.reopenClass({ - NAMESPACES: [Ember], - NAMESPACES_BY_ID: {}, - PROCESSED: false, - processAll: processAllNamespaces, - byName: function(name) { - if (!Ember.BOOTED) { - processAllNamespaces(); - } + /** + @module ember + @submodule ember-testing + */ - return NAMESPACES_BY_ID[name]; - } - }); + /** + The primary purpose of this class is to create hooks that can be implemented + by an adapter for various test frameworks. - var NAMESPACES_BY_ID = Namespace.NAMESPACES_BY_ID; + @class Adapter + @namespace Ember.Test + */ + var Adapter = EmberObject.extend({ + /** + This callback will be called whenever an async operation is about to start. - var hasOwnProp = ({}).hasOwnProperty; + Override this to call your framework's methods that handle async + operations. - function processNamespace(paths, root, seen) { - var idx = paths.length; + @public + @method asyncStart + */ + asyncStart: K, - NAMESPACES_BY_ID[paths.join('.')] = root; + /** + This callback will be called whenever an async operation has completed. - // 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]; + @public + @method asyncEnd + */ + asyncEnd: K, - // 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; + /** + Override this method with your testing framework's false assertion. + This function is called whenever an exception occurs causing the testing + promise to fail. - // 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('.'); + QUnit example: - // Support nested namespaces - } else if (obj && obj.isNamespace) { - // Skip aliased namespaces - if (seen[guidFor(obj)]) { continue; } - seen[guidFor(obj)] = true; + ```javascript + exception: function(error) { + ok(false, error); + }; + ``` - // Process the child namespace - processNamespace(paths, obj, seen); - } + @public + @method exception + @param {String} error The exception to be raised. + */ + exception: function(error) { + throw error; } + }); - paths.length = idx; // cut out last item - } + __exports__["default"] = Adapter; + }); +enifed("ember-testing/adapters/qunit", + ["ember-testing/adapters/adapter","ember-metal/utils","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var Adapter = __dependency1__["default"]; + var inspect = __dependency2__.inspect; - var STARTS_WITH_UPPERCASE = /^[A-Z]/; + /** + This class implements the methods defined by Ember.Test.Adapter for the + QUnit testing framework. - function tryIsNamespace(lookup, prop) { - try { - var obj = lookup[prop]; - return obj && obj.isNamespace && obj; - } catch (e) { - // continue + @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 EmberError = __dependency2__["default"]; + var run = __dependency3__["default"]; + var jQuery = __dependency4__["default"]; + var Test = __dependency5__["default"]; - function findNamespaces() { - var lookup = Ember.lookup; - var obj; + /** + * @module ember + * @submodule ember-testing + */ - if (Namespace.PROCESSED) { return; } + var helper = Test.registerHelper; + var asyncHelper = Test.registerAsyncHelper; + var countAsync = 0; - for (var prop in lookup) { - // Only process entities that start with uppercase A-Z - if (!STARTS_WITH_UPPERCASE.test(prop)) { continue; } + function currentRouteName(app){ + var appController = app.__container__.lookup('controller:application'); - // Unfortunately, some versions of IE don't support window.hasOwnProperty - if (lookup.hasOwnProperty && !lookup.hasOwnProperty(prop)) { continue; } + return get(appController, 'currentRouteName'); + } - // At times we are not allowed to access certain properties for security reasons. - // There are also times where even if we can access them, we are not allowed to access their properties. - obj = tryIsNamespace(lookup, prop); - if (obj) { - obj[NAME_KEY] = prop; - } - } + function currentPath(app){ + var appController = app.__container__.lookup('controller:application'); + + return get(appController, 'currentPath'); } - var NAME_KEY = Ember.NAME_KEY = GUID_KEY + '_name'; + function currentURL(app){ + var router = app.__container__.lookup('router:main'); - function superClassString(mixin) { - var superclass = mixin.superclass; - if (superclass) { - if (superclass[NAME_KEY]) { return superclass[NAME_KEY]; } - else { return superClassString(superclass); } - } else { - return; - } + return get(router, 'location').getURL(); } - function classToString() { - if (!Ember.BOOTED && !this[NAME_KEY]) { - processAllNamespaces(); - } + function pauseTest(){ + Test.adapter.asyncStart(); + return new Ember.RSVP.Promise(function(){ }, 'TestAdapter paused promise'); + } - var ret; + function visit(app, url) { + var router = app.__container__.lookup('router:main'); + router.location.setURL(url); - if (this[NAME_KEY]) { - ret = this[NAME_KEY]; - } else if (this._toString) { - ret = this._toString; + if (app._readinessDeferrals > 0) { + router['initialURL'] = url; + run(app, 'advanceReadiness'); + delete router['initialURL']; } else { - var str = superClassString(this); - if (str) { - ret = "(subclass of " + str + ")"; - } else { - ret = "(unknown mixin)"; - } - this.toString = makeToString(ret); + run(app, app.handleURL, url); } - return ret; + return app.testHelpers.wait(); } - function processAllNamespaces() { - var unprocessedNamespaces = !Namespace.PROCESSED; - var unprocessedMixins = Ember.anyUnprocessedMixins; - - if (unprocessedNamespaces) { - findNamespaces(); - Namespace.PROCESSED = true; - } - - if (unprocessedNamespaces || unprocessedMixins) { - var namespaces = Namespace.NAMESPACES; - var namespace; + function click(app, selector, context) { + var $el = app.testHelpers.findWithAssert(selector, context); + run($el, 'mousedown'); - for (var i=0, l=namespaces.length; i 0) { - NativeArray = NativeArray.without.apply(NativeArray, ignore); - } + // 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); - /** - 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`. + // If this is the last async promise, end the async test + if (--countAsync === 0) { + Test.adapter.asyncEnd(); + } - Example + // Synchronously resolve the promise + run(null, resolve, value); + }, 10); + }); - ```js - var Pagination = Ember.CollectionView.extend({ - tagName: 'ul', - classNames: ['pagination'], + } - init: function() { - this._super(); - if (!this.get('content')) { - this.set('content', Ember.A()); - } - } - }); - ``` - @method A - @for Ember - @return {Ember.NativeArray} + /** + * 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} */ - var A = function(arr) { - if (arr === undefined) { arr = []; } - return EmberArray.detect(arr) ? arr : NativeArray.apply(arr); - }; + asyncHelper('visit', visit); /** - 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`. - - Example - - ```js - if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Array) { - Ember.NativeArray.activate(); - } - ``` + * 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); - @method activate - @for Ember.NativeArray - @static - @return {void} + /** + * 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 */ - NativeArray.activate = function() { - NativeArray.apply(Array.prototype); + asyncHelper('keyEvent', keyEvent); - A = function(arr) { return arr || []; }; - }; + /** + * 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); - if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Array) { - NativeArray.activate(); - } + /** + * 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); - 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-metal/core","ember-runtime/system/core_object","ember-runtime/mixins/observable","ember-runtime/inject","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; /** - @module ember - @submodule ember-runtime + * 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 */ - - var Ember = __dependency1__["default"]; - // Ember.assert - var CoreObject = __dependency2__["default"]; - var Observable = __dependency3__["default"]; - var validatePropertyInjections = __dependency4__.validatePropertyInjections; + helper('findWithAssert', findWithAssert); /** - `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. - - @class Object - @namespace Ember - @extends Ember.CoreObject - @uses Ember.Observable - */ - var EmberObject = CoreObject.extend(Observable); - EmberObject.toString = function() { - return "Ember.Object"; - }; - - function injectedPropertyAssertion(props) { - // Injection validations are a debugging aid only, so ensure that they are - // not performed in production builds by invoking from an assertion - Ember.assert("Injected properties are invalid", validatePropertyInjections(this.constructor, props)); - } + 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. - - __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"]; + This is most often used as the return value for the helper functions (see 'click', + 'fillIn','visit',etc). - /** - `Ember.ObjectProxy` forwards all properties not defined by the proxy itself - to a proxied `content` object. + Example: ```javascript - object = Ember.Object.create({ - name: 'Foo' - }); + Ember.Test.registerAsyncHelper('loginUser', function(app, username, password) { + visit('secured/path/here') + .fillIn('#username', username) + .fillIn('#password', password) + .click('.submit') - proxy = Ember.ObjectProxy.create({ - content: object + return app.testHelpers.wait(); }); - // Access and change existing properties - proxy.get('name') // 'Foo' - proxy.set('name', 'Bar'); - object.get('name') // 'Bar' - - // Create new 'description' property on `object` - proxy.set('description', 'Foo is a whizboo baz'); - object.get('description') // 'Foo is a whizboo baz' - ``` - - While `content` is unset, setting a property to be delegated will throw an - Error. + @method wait + @param {Object} value The value to be returned. + @return {RSVP.Promise} + */ + asyncHelper('wait', wait); + asyncHelper('andThen', andThen); - ```javascript - proxy = Ember.ObjectProxy.create({ - content: null, - flag: null - }); - proxy.set('flag', true); - proxy.get('flag'); // true - proxy.get('foo'); // undefined - proxy.set('foo', 'data'); // throws Error - ``` - Delegated properties can be bound to and will change when content is updated. + /** + Returns the currently active route name. - Computed properties on the proxy itself can depend on delegated properties. + Example: - ```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') - }); + ```javascript + function validateRouteName(){ + equal(currentRouteName(), 'some.path', "correct route was transitioned into."); + } - proxy = ProxyWithComputedProperty.create(); + visit('/some/path').then(validateRouteName) + ``` - proxy.get('fullName'); // undefined - proxy.set('content', { - firstName: 'Tom', lastName: 'Dale' - }); // triggers property change for fullName on proxy + @method currentRouteName + @return {Object} The name of the currently active route. + @since 1.5.0 + */ + helper('currentRouteName', currentRouteName); - proxy.get('fullName'); // 'Tom Dale' - ``` + /** + Returns the current path. - @class ObjectProxy - @namespace Ember - @extends Ember.Object - @extends Ember._ProxyMixin - */ + Example: - __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; + ```javascript + function validateURL(){ + equal(currentPath(), 'some.path.index', "correct path was transitioned into."); + } - var Service; + click('#some-link-id').then(validateURL); + ``` - - __exports__["default"] = Service; - }); -enifed("ember-runtime/system/set", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/is_none","ember-runtime/system/string","ember-runtime/system/core_object","ember-runtime/mixins/mutable_enumerable","ember-runtime/mixins/enumerable","ember-runtime/mixins/copyable","ember-runtime/mixins/freezable","ember-metal/error","ember-metal/property_events","ember-metal/mixin","ember-metal/computed","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-runtime + @method currentPath + @return {Object} The currently active path. + @since 1.5.0 */ - 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; + helper('currentPath', currentPath); /** - An unordered collection of objects. + Returns the current URL. - 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. + Example: - All Sets are observable via the Enumerable Observer API - which works - on any enumerable object including both Sets and Arrays. + ```javascript + function validateURL(){ + equal(currentURL(), '/some/path', "correct URL was transitioned into."); + } - ## Creating a Set + click('#some-link-id').then(validateURL); + ``` - 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. + @method currentURL + @return {Object} The currently active URL. + @since 1.5.0 + */ + helper('currentURL', currentURL); - 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()`. + + /** + Pauses the current test - this is useful for debugging while testing or for test-driving. + It allows you to inspect the state of your application at any point. - ```javascript - // creates a new empty set - var foundNames = new Ember.Set(); + Example (The test will pause before clicking the button): - // creates a set with four names in it. - var names = new Ember.Set(["Charles", "Tom", "Juan", "Alex"]); // :P + ```javascript + visit('/') + return pauseTest(); - // creates a copy of the names set. - var namesCopy = new Ember.Set(names); + click('.btn'); + ``` - // same as above. - var anotherNamesCopy = names.copy(); - ``` + @since 1.9.0 + @method pauseTest + @return {Object} A promise that will never resolve + */ + helper('pauseTest', pauseTest); + - ## Adding/Removing Objects + /** + Triggers the given DOM event on the element identified by the provided selector. - 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. + Example: - 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. + ```javascript + triggerEvent('#some-elem-id', 'blur'); + ``` - NOTE: You cannot add/remove `null` or `undefined` to a set. Any attempt to do - so will be ignored. + This is actually used internally by the `keyEvent` helper like so: - 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. + ```javascript + triggerEvent('#some-elem-id', 'keypress', { keyCode: 13 }); + ``` - ## Testing for an Object + @method triggerEvent + @param {String} selector jQuery selector for finding element on the DOM + @param {String} [context] jQuery selector that will limit the selector + argument to find only within the context's children + @param {String} type The event type to be triggered. + @param {Object} [options] The options to be passed to jQuery.Event. + @return {RSVP.Promise} + @since 1.5.0 + */ + asyncHelper('triggerEvent', triggerEvent); + }); +enifed("ember-testing/initializers", + ["ember-runtime/system/lazy_load"], + function(__dependency1__) { + "use strict"; + var onLoad = __dependency1__.onLoad; - To test for an object's presence in a set you simply call - `Ember.Set#contains()`. + var name = 'deferReadiness in `testing` mode'; - ## Observing changes + onLoad('Ember.Application', function(Application) { + if (!Application.initializers[name]) { + Application.initializer({ + name: name, - 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. + initialize: function(container, application){ + if (application.testing) { + application.deferReadiness(); + } + } + }); + } + }); + }); +enifed("ember-testing/setup_for_testing", + ["ember-metal/core","ember-testing/adapters/qunit","ember-views/system/jquery","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // import Test from "ember-testing/test"; // ES6TODO: fix when cycles are supported + var QUnitAdapter = __dependency2__["default"]; + var jQuery = __dependency3__["default"]; - 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. + var Test, requests; - ## Other Methods + function incrementAjaxPendingRequests(_, xhr){ + requests.push(xhr); + Test.pendingAjaxRequests = requests.length; + } - `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. + function decrementAjaxPendingRequests(_, xhr){ + for (var i=0;i') + .css({ position: 'absolute', left: '-1000px', top: '-1000px' }) + .appendTo('body') + .on('click', handler) + .trigger('click') + .remove(); + } - this.enumerableContentWillChange(len, 0); - propertyWillChange(this, 'firstObject'); - propertyWillChange(this, 'lastObject'); + $(function() { + /* + Determine whether a checkbox checked using jQuery's "click" method will have + the correct value for its checked property. - for (var i=0; i < len; i++) { - guid = guidFor(this[i]); - delete this[guid]; - delete this[i]; + 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; + } + } + }; } + }); - set(this, 'length', 0); - - propertyDidChange(this, 'firstObject'); - propertyDidChange(this, 'lastObject'); - this.enumerableContentDidChange(len, 0); - - return this; - }, - - /** - Returns true if the passed object is also an enumerable that contains the - same objects as the receiver. - - ```javascript - var colors = ["red", "green", "blue"], - same_colors = new Ember.Set(colors); - - same_colors.isEqual(colors); // true - same_colors.isEqual(["purple", "brown"]); // false - ``` - - @method isEqual - @param {Ember.Set} obj the other object. - @return {Boolean} - */ - isEqual: function(obj) { - // fail fast - if (!Enumerable.detect(obj)) return false; + // 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/ext/rsvp","ember-testing/setup_for_testing","ember-application/system/application","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + var emberRun = __dependency2__["default"]; + var create = __dependency3__.create; + var RSVP = __dependency4__["default"]; + var setupForTesting = __dependency5__["default"]; + var EmberApplication = __dependency6__["default"]; - var loc = get(this, 'length'); - if (get(obj, 'length') !== loc) return false; + /** + @module ember + @submodule ember-testing + */ + var slice = [].slice; + var helpers = {}; + var injectHelpersCallbacks = []; - while(--loc >= 0) { - if (!obj.contains(this[loc])) return false; - } + /** + This is a container for an assortment of testing related functionality: - return true; - }, + * 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. + @class Test + @namespace Ember + */ + var Test = { /** - 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. - - This is an alias for `Ember.MutableEnumerable.addObject()`. - - ```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"] - ``` + Hash containing all known test helpers. - @method add - @param {Object} obj The object to add. - @return {Ember.Set} The set itself. + @property _helpers + @private + @since 1.7.0 */ - add: aliasMethod('addObject'), + _helpers: helpers, /** - 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()`. + `registerHelper` is used to register a test helper that will be injected + when `App.injectTestHelpers` is called. + + The helper method will always be called with the current Application as + the first parameter. + + For example: ```javascript - var colors = new Ember.Set(["red", "green", "blue"]); - colors.remove("red"); // ["blue", "green"] - colors.remove("purple"); // ["blue", "green"] - colors.remove(null); // ["blue", "green"] + Ember.Test.registerHelper('boot', function(app) { + Ember.run(app, app.advanceReadiness); + }); ``` - @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. + This helper can later be called without arguments because it will be + called with `app` as the first parameter. ```javascript - var colors = new Ember.Set(["green", "blue"]); - colors.pop(); // "blue" - colors.pop(); // "green" - colors.pop(); // null + App = Ember.Application.create(); + App.injectTestHelpers(); + boot(); ``` - @method pop - @return {Object} The removed object from the set or null. + @public + @method registerHelper + @param {String} name The name of the helper method to add. + @param {Function} helperMethod + @param options {Object} */ - 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; + registerHelper: function(name, helperMethod) { + helpers[name] = { + method: helperMethod, + meta: { wait: false } + }; }, /** - Inserts the given object on to the end of the set. It returns - the set itself. + `registerAsyncHelper` is used to register an async test helper that will be injected + when `App.injectTestHelpers` is called. - This is an alias for `Ember.MutableEnumerable.addObject()`. + The helper method will always be called with the current Application as + the first parameter. + + For example: ```javascript - var colors = new Ember.Set(); - colors.push("red"); // ["red"] - colors.push("green"); // ["red", "green"] - colors.push("blue"); // ["red", "green", "blue"] + Ember.Test.registerAsyncHelper('boot', function(app) { + Ember.run(app, app.advanceReadiness); + }); ``` - @method push - @return {Ember.Set} The set itself. - */ - push: aliasMethod('addObject'), + 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. - /** - Removes the last element from the set and returns it, or `null` if it's empty. - This is an alias for `Ember.Set.pop()`. + For example: ```javascript - var colors = new Ember.Set(["green", "blue"]); - colors.shift(); // "blue" - colors.shift(); // "green" - colors.shift(); // null + Ember.Test.registerAsyncHelper('deletePost', function(app, postId) { + click('.delete-' + postId); + }); + + // ... in your test + visit('/post/2'); + deletePost(2); + visit('/post/3'); + deletePost(3); ``` - @method shift - @return {Object} The removed object from the set or null. + @public + @method registerAsyncHelper + @param {String} name The name of the helper method to add. + @param {Function} helperMethod + @since 1.2.0 */ - shift: aliasMethod('pop'), + registerAsyncHelper: function(name, helperMethod) { + helpers[name] = { + method: helperMethod, + meta: { wait: true } + }; + }, /** - Inserts the given object on to the end of the set. It returns - the set itself. + Remove a previously added helper method. - This is an alias of `Ember.Set.push()` + Example: ```javascript - var colors = new Ember.Set(); - colors.unshift("red"); // ["red"] - colors.unshift("green"); // ["red", "green"] - colors.unshift("blue"); // ["red", "green", "blue"] + Ember.Test.unregisterHelper('wait'); ``` - @method unshift - @return {Ember.Set} The set itself. + @public + @method unregisterHelper + @param {String} name The helper to remove. */ - unshift: aliasMethod('push'), + unregisterHelper: function(name) { + delete helpers[name]; + delete Test.Promise.prototype[name]; + }, /** - Adds each object in the passed enumerable to the set. + Used to register callbacks to be fired whenever `App.injectTestHelpers` + is called. - This is an alias of `Ember.MutableEnumerable.addObjects()` + The callback will receive the current application as an argument. + + Example: ```javascript - var colors = new Ember.Set(); - colors.addEach(["red", "green", "blue"]); // ["red", "green", "blue"] + Ember.Test.onInjectHelpers(function() { + Ember.$(document).ajaxSend(function() { + Test.pendingAjaxRequests++; + }); + + Ember.$(document).ajaxComplete(function() { + Test.pendingAjaxRequests--; + }); + }); ``` - @method addEach - @param {Ember.Enumerable} objects the objects to add. - @return {Ember.Set} The set itself. + @public + @method onInjectHelpers + @param {Function} callback The function to be called. */ - addEach: aliasMethod('addObjects'), + onInjectHelpers: function(callback) { + injectHelpersCallbacks.push(callback); + }, /** - Removes each object in the passed enumerable to the set. - - This is an alias of `Ember.MutableEnumerable.removeObjects()` + 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. - ```javascript - var colors = new Ember.Set(["red", "green", "blue"]); - colors.removeEach(["red", "blue"]); // ["green"] - ``` + This method should be returned by async helpers such as `wait`. - @method removeEach - @param {Ember.Enumerable} objects the objects to remove. - @return {Ember.Set} The set itself. + @public + @method promise + @param {Function} resolver The function used to resolve the promise. */ - removeEach: aliasMethod('removeObjects'), - - // .......................................................... - // PRIVATE ENUMERABLE SUPPORT - // - - 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]; + promise: function(resolver) { + return new Test.Promise(resolver); }, - // more optimized version - firstObject: computed(function() { - return this.length > 0 ? this[0] : undefined; - }), - - // more optimized version - lastObject: computed(function() { - return this.length > 0 ? this[this.length-1] : undefined; - }), - - // implements Ember.MutableEnumerable - addObject: function(obj) { - if (get(this, 'isFrozen')) throw new EmberError(FROZEN_ERROR); - if (isNone(obj)) return this; // nothing to do + /** + Used to allow ember-testing to communicate with a specific testing + framework. - var guid = guidFor(obj); - var idx = this[guid]; - var len = get(this, 'length'); - var added; + You can manually set it before calling `App.setupForTesting()`. - if (idx>=0 && idx=0 && idx=0; - }, - - 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; + if (!this.waiters) { + this.waiters = Ember.A(); } - return ret; + this.waiters.push([context, callback]); }, + /** + `unregisterWaiter` is used to unregister a callback that was + registered with `registerWaiter`. - toString: function() { - var len = this.length, idx, array = []; - for(idx = 0; idx < len; idx++) { - array[idx] = this[idx]; + @public + @method unregisterWaiter + @param {Object} context (optional) + @param {Function} callback + @since 1.2.0 + */ + unregisterWaiter: function(context, callback) { + if (!this.waiters) { return; } + if (arguments.length === 1) { + callback = context; + context = null; } - return fmt("Ember.Set<%@>", [array.join(',')]); - } - }); - }); -enifed("ember-runtime/system/string", - ["ember-metal/core","ember-metal/utils","ember-metal/cache","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-runtime - */ - var Ember = __dependency1__["default"]; - // Ember.STRINGS, Ember.FEATURES - var isArray = __dependency2__.isArray; - var emberInspect = __dependency2__.inspect; - - var Cache = __dependency3__["default"]; - - var STRING_DASHERIZE_REGEXP = (/[ _]/g); - - var STRING_DASHERIZE_CACHE = new Cache(1000, function(key) { - return decamelize(key).replace(STRING_DASHERIZE_REGEXP, '-'); - }); - - 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(); - }); - }); - - var CLASSIFY_CACHE = new Cache(1000, function(str) { - var parts = str.split("."); - var out = []; - - for (var i=0, l=parts.length; i 2) { - cachedFormats = new Array(arguments.length - 1); + args.unshift(app); - for (var i = 1, l = arguments.length; i < l; i++) { - cachedFormats[i - 1] = arguments[i]; + // some helpers are not async and + // need to return a value immediately. + // example: `find` + if (!meta.wait) { + return fn.apply(app, args); } - } - - // 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); - }); - } - - function loc(str, formats) { - if (!isArray(formats) || arguments.length > 2) { - formats = Array.prototype.slice.call(arguments, 1); - } - - str = Ember.STRINGS[str] || str; - return fmt(str, formats); - } - - function w(str) { - return str.split(/\s+/); - } - - function decamelize(str) { - return DECAMELIZE_CACHE.get(str); - } - - function dasherize(str) { - return STRING_DASHERIZE_CACHE.get(str); - } - - function camelize(str) { - return CAMELIZE_CACHE.get(str); - } - - function classify(str) { - return CLASSIFY_CACHE.get(str); - } - function underscore(str) { - return UNDERSCORE_CACHE.get(str); - } + if (!lastPromise) { + // It's the first async helper in current context + lastPromise = fn.apply(app, args); + } else { + // wait for last helper's promise to resolve + // and then execute + run(function() { + lastPromise = Test.resolve(lastPromise).then(function() { + return fn.apply(app, args); + }); + }); + } - function capitalize(str) { - return CAPITALIZE_CACHE.get(str); + return lastPromise; + }; } - /** - 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 = {}; - - /** - 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 String - @namespace Ember - @static - */ - __exports__["default"] = { - /** - Apply formatting options to the string. This will look for occurrences - of "%@" in your string and substitute them with the arguments you pass into - this method. If you want to control the specific order of replacement, - you can add a number after the key as well to indicate which argument - you want to insert. - - Ordered insertions are most useful when building loc strings where values - you need to insert may appear in different orders. + function run(fn) { + if (!emberRun.currentRunLoop) { + emberRun(fn); + } else { + fn(); + } + } - ```javascript - "Hello %@ %@".fmt('John', 'Doe'); // "Hello John Doe" - "Hello %@2, %@1".fmt('John', 'Doe'); // "Hello Doe, John" - ``` + 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. - @method fmt - @param {String} str The string to format - @param {Array} formats An array of parameters to interpolate into string. - @return {String} formatted string + @property testHelpers + @type {Object} + @default {} */ - fmt: fmt, + testHelpers: {}, /** - 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. + This property will contain the original methods that were registered + on the `helperContainer` before `injectTestHelpers` is called. - 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. + When `removeTestHelpers` is called, these methods are restored to the + `helperContainer`. - ```javascript - Ember.STRINGS = { - '_Hello World': 'Bonjour le monde', - '_Hello %@ %@': 'Bonjour %@ %@' - }; + @property originalMethods + @type {Object} + @default {} + @private + @since 1.3.0 + */ + originalMethods: {}, - Ember.String.loc("_Hello World"); // 'Bonjour le monde'; - Ember.String.loc("_Hello %@ %@", ["John", "Smith"]); // "Bonjour John Smith"; - ``` - @method loc - @param {String} str The string to format - @param {Array} formats Optional array of parameters to interpolate into string. - @return {String} formatted string + /** + This property indicates whether or not this application is currently in + testing mode. This is set when `setupForTesting` is called on the current + application. + + @property testing + @type {Boolean} + @default false + @since 1.3.0 */ - loc: loc, + testing: false, /** - 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`. + 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). - ```javascript - Ember.String.w("alpha beta gamma").forEach(function(key) { - console.log(key); - }); + Example: - // > alpha - // > beta - // > gamma - ``` + ``` + App.setupForTesting(); + ``` - @method w - @param {String} str The string to split - @return {Array} array containing the split strings + @method setupForTesting */ - w: w, - - /** - Converts a camelized string into all lower case separated by underscores. + setupForTesting: function() { + setupForTesting(); - ```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.testing = true; - @method decamelize - @param {String} str The string to decamelize. - @return {String} the decamelized string. - */ - decamelize: decamelize, + this.Router.reopen({ + location: 'none' + }); + }, /** - 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' - ``` + This will be used as the container to inject the test helpers into. By + default the helpers are injected into `window`. - @method dasherize - @param {String} str The string to dasherize. - @return {String} the dasherized string. + @property helperContainer + @type {Object} The object to be used for test helpers. + @default window + @since 1.2.0 */ - dasherize: dasherize, + helperContainer: window, /** - Returns the lowerCamelCase form of a string. + 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`). - ```javascript - 'innerHTML'.camelize(); // 'innerHTML' - 'action_name'.camelize(); // 'actionName' - 'css-class-name'.camelize(); // 'cssClassName' - 'my favorite items'.camelize(); // 'myFavoriteItems' - 'My Favorite Items'.camelize(); // 'myFavoriteItems' - ``` + Any callbacks registered with `onInjectHelpers` will be called once the + helpers have been injected. - @method camelize - @param {String} str The string to camelize. - @return {String} the camelized string. - */ - camelize: camelize, + Example: + ``` + App.injectTestHelpers(); + ``` - /** - Returns the UpperCamelCase form of a string. + @method injectTestHelpers + */ + injectTestHelpers: function(helperContainer) { + if (helperContainer) { this.helperContainer = helperContainer; } - ```javascript - 'innerHTML'.classify(); // 'InnerHTML' - 'action_name'.classify(); // 'ActionName' - 'css-class-name'.classify(); // 'CssClassName' - 'my favorite items'.classify(); // 'MyFavoriteItems' - ``` + 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); + } - @method classify - @param {String} str the string to classify - @return {String} the classified string - */ - classify: classify, + for(var i = 0, l = injectHelpersCallbacks.length; i < l; i++) { + injectHelpersCallbacks[i](this); + } + }, /** - More general than decamelize. Returns the lower\_case\_and\_underscored - form of a string. + This removes all helpers that have been registered, and resets and functions + that were overridden by the helpers. + + Example: ```javascript - 'innerHTML'.underscore(); // 'inner_html' - 'action_name'.underscore(); // 'action_name' - 'css-class-name'.underscore(); // 'css_class_name' - 'my favorite items'.underscore(); // 'my_favorite_items' + App.removeTestHelpers(); ``` - @method underscore - @param {String} str The string to underscore. - @return {String} the underscored string. + @public + @method removeTestHelpers */ - underscore: underscore, - - /** - Returns the Capitalized form of a string + removeTestHelpers: function() { + for (var name in helpers) { + this.helperContainer[name] = this.originalMethods[name]; + delete this.testHelpers[name]; + delete this.originalMethods[name]; + } + } + }); - ```javascript - 'innerHTML'.capitalize() // 'InnerHTML' - 'action_name'.capitalize() // 'Action_name' - 'css-class-name'.capitalize() // 'Css-class-name' - 'my favorite items'.capitalize() // 'My favorite items' - ``` + // 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); + }); + } + }; + } - @method capitalize - @param {String} str The string to capitalize. - @return {String} The capitalized string. - */ - capitalize: capitalize + Test.Promise = function() { + RSVP.Promise.apply(this, arguments); + Test.lastPromise = this; }; - __exports__.fmt = fmt; - __exports__.loc = loc; - __exports__.w = w; - __exports__.decamelize = decamelize; - __exports__.dasherize = dasherize; - __exports__.camelize = camelize; - __exports__.classify = classify; - __exports__.underscore = underscore; - __exports__.capitalize = capitalize; - }); -enifed("ember-runtime/system/subarray", - ["ember-metal/error","ember-metal/enumerable_utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var EmberError = __dependency1__["default"]; - var EnumerableUtils = __dependency2__["default"]; + Test.Promise.prototype = create(RSVP.Promise.prototype); + Test.Promise.prototype.constructor = Test.Promise; - var RETAIN = 'r'; - var FILTER = 'f'; + // 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); + }; - function Operation(type, count) { - this.type = type; - this.count = count; - } + // 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; - __exports__["default"] = SubArray; + // Reset lastPromise for nested helpers + Test.lastPromise = null; - /** - 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. + value = fn(val); - @class SubArray - @namespace Ember - */ - function SubArray (length) { - if (arguments.length < 1) { length = 0; } + lastPromise = Test.lastPromise; - if (length > 0) { - this._operations = [new Operation(RETAIN, length)]; + // 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 { - this._operations = []; + run(function() { + lastPromise = Test.resolve(lastPromise).then(function() { + return value; + }); + }); + return lastPromise; } } + __exports__["default"] = Test; + }); +enifed("ember-views", + ["ember-runtime","ember-views/system/jquery","ember-views/system/utils","ember-views/system/render_buffer","ember-views/system/ext","ember-views/views/states","ember-views/views/core_view","ember-views/views/view","ember-views/views/container_view","ember-views/views/collection_view","ember-views/views/component","ember-views/system/event_dispatcher","ember-views/mixins/view_target_action_support","ember-views/component_lookup","ember-views/views/checkbox","ember-views/mixins/text_support","ember-views/views/text_field","ember-views/views/text_area","ember-views/views/bound_view","ember-views/views/simple_bound_view","ember-views/views/metamorph_view","ember-views/views/select","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__, __dependency22__, __exports__) { + "use strict"; + /** + Ember Views - SubArray.prototype = { - /** - Track that an item was added to the tracked array. - - @method addItem - - @param {Number} index The index of the item in the tracked array. - @param {Boolean} match `true` iff the item is included in the subarray. - - @return {number} The index of the item in the subarray. - */ - addItem: function(index, match) { - var returnValue = -1; - var itemType = match ? RETAIN : FILTER; - var self = this; - - this._findOperation(index, function(operation, operationIndex, rangeStart, rangeEnd, seenInSubArray) { - var newOperation, splitOperation; - - 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; - - self._operations.splice(operationIndex + 1, 0, newOperation, splitOperation); - } + @module ember + @submodule ember-views + @requires ember-runtime + @main ember-views + */ - if (match) { - if (operation.type === RETAIN) { - returnValue = seenInSubArray + (index - rangeStart); - } else { - returnValue = seenInSubArray; - } - } + // BEGIN IMPORTS + var Ember = __dependency1__["default"]; + var jQuery = __dependency2__["default"]; + var isSimpleClick = __dependency3__.isSimpleClick; + var getViewClientRects = __dependency3__.getViewClientRects; + var getViewBoundingClientRect = __dependency3__.getViewBoundingClientRect; + var RenderBuffer = __dependency4__["default"]; + // for the side effect of extending Ember.run.queues + var cloneStates = __dependency6__.cloneStates; + var states = __dependency6__.states; - self._composeAt(operationIndex); - }, function(seenInSubArray) { - self._operations.push(new Operation(itemType, 1)); + var CoreView = __dependency7__["default"]; + var View = __dependency8__["default"]; + var ContainerView = __dependency9__["default"]; + var CollectionView = __dependency10__["default"]; + var Component = __dependency11__["default"]; - if (match) { - returnValue = seenInSubArray; - } + var EventDispatcher = __dependency12__["default"]; + var ViewTargetActionSupport = __dependency13__["default"]; + var ComponentLookup = __dependency14__["default"]; + var Checkbox = __dependency15__["default"]; + var TextSupport = __dependency16__["default"]; + var TextField = __dependency17__["default"]; + var TextArea = __dependency18__["default"]; + + var BoundView = __dependency19__["default"]; + var SimpleBoundView = __dependency20__["default"]; + var _MetamorphView = __dependency21__["default"]; + var _SimpleMetamorphView = __dependency21__._SimpleMetamorphView; + var _Metamorph = __dependency21__._Metamorph; + var Select = __dependency22__.Select; + var SelectOption = __dependency22__.SelectOption; + var SelectOptgroup = __dependency22__.SelectOptgroup; + // END IMPORTS - self._composeAt(self._operations.length-1); - }); + /** + Alias for jQuery - return returnValue; - }, + @method $ + @for Ember + */ - /** - Track that an item was removed from the tracked array. + // BEGIN EXPORTS + Ember.$ = jQuery; - @method removeItem + Ember.ViewTargetActionSupport = ViewTargetActionSupport; + Ember.RenderBuffer = RenderBuffer; - @param {Number} index The index of the item in the tracked array. + var ViewUtils = Ember.ViewUtils = {}; + ViewUtils.isSimpleClick = isSimpleClick; + ViewUtils.getViewClientRects = getViewClientRects; + ViewUtils.getViewBoundingClientRect = getViewBoundingClientRect; - @return {number} The index of the item in the subarray, or `-1` if the item - was not in the subarray. - */ - removeItem: function(index) { - var returnValue = -1; - var self = this; + Ember.CoreView = CoreView; + Ember.View = View; + Ember.View.states = states; + Ember.View.cloneStates = cloneStates; + Ember.Checkbox = Checkbox; + Ember.TextField = TextField; + Ember.TextArea = TextArea; - this._findOperation(index, function (operation, operationIndex, rangeStart, rangeEnd, seenInSubArray) { - if (operation.type === RETAIN) { - returnValue = seenInSubArray + (index - rangeStart); - } + Ember._SimpleBoundView = SimpleBoundView; + Ember._BoundView = BoundView; + Ember._SimpleMetamorphView = _SimpleMetamorphView; + Ember._MetamorphView = _MetamorphView; + Ember._Metamorph = _Metamorph; + Ember.Select = Select; + Ember.SelectOption = SelectOption; + Ember.SelectOptgroup = SelectOptgroup; - 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."); - }); + Ember.TextSupport = TextSupport; + Ember.ComponentLookup = ComponentLookup; + Ember.ContainerView = ContainerView; + Ember.CollectionView = CollectionView; + Ember.Component = Component; + Ember.EventDispatcher = EventDispatcher; + // END EXPORTS - return returnValue; - }, + __exports__["default"] = Ember; + }); +enifed("ember-views/attr_nodes/attr_node", + ["ember-metal/streams/utils","ember-metal/run_loop","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-htmlbars + */ + var read = __dependency1__.read; + var subscribe = __dependency1__.subscribe; + var unsubscribe = __dependency1__.unsubscribe; + var run = __dependency2__["default"]; - _findOperation: function (index, foundCallback, notFoundCallback) { - var seenInSubArray = 0; - var operationIndex, len, operation, rangeStart, rangeEnd; + function AttrNode(attrName, attrValue) { + this.init(attrName, attrValue); + } - // 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; + AttrNode.prototype.init = function init(attrName, simpleAttrValue){ + this.isView = true; - if (index >= rangeStart && index <= rangeEnd) { - foundCallback(operation, operationIndex, rangeStart, rangeEnd, seenInSubArray); - return; - } else if (operation.type === RETAIN) { - seenInSubArray += operation.count; - } - } + // That these semantics are used is very unfortunate. + this.tagName = ''; + this.classNameBindings = []; - notFoundCallback(seenInSubArray); - }, + this.attrName = attrName; + this.attrValue = simpleAttrValue; + this.isDirty = true; + this.lastValue = null; - _composeAt: function(index) { - var op = this._operations[index]; - var otherOp; + subscribe(this.attrValue, this.rerender, this); + }; - if (!op) { - // Composing out of bounds is a no-op, as when removing the last operation - // in the list. - return; + 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; } + } + }; - if (index > 0) { - otherOp = this._operations[index-1]; - if (otherOp.type === op.type) { - op.count += otherOp.count; - this._operations.splice(index-1, 1); - --index; - } - } + AttrNode.prototype.render = function render(buffer) { + this.isDirty = false; + var value = read(this.attrValue); - 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); - } - } - }, + this._morph.setContent(value); - toString: function () { - var str = ""; - EnumerableUtils.forEach(this._operations, function (operation) { - str += " " + operation.type + ":" + operation.count; - }); - return str.substring(1); - } + this.lastValue = value; }; - }); -enifed("ember-runtime/system/tracked_array", - ["ember-metal/property_get","ember-metal/enumerable_utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var get = __dependency1__.get; - var forEach = __dependency2__.forEach; - var RETAIN = 'r'; - var INSERT = 'i'; - var DELETE = 'd'; + AttrNode.prototype.rerender = function render() { + this.isDirty = true; + run.schedule('render', this, this.renderIfDirty); + }; - __exports__["default"] = TrackedArray; + AttrNode.prototype.destroy = function render() { + this.isDirty = false; + unsubscribe(this.attrValue, this.rerender, this); - /** - 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. + var parent = this._parentView; + if (parent) { parent.removeChild(this); } + }; - @class TrackedArray - @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. + __exports__["default"] = AttrNode; + }); +enifed("ember-views/attr_nodes/legacy_bind", + ["./attr_node","ember-runtime/system/string","ember-metal/utils","ember-metal/streams/utils","ember-metal/platform/create","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-htmlbars */ - function TrackedArray(items) { - if (arguments.length < 1) { items = []; } - var length = get(items, 'length'); + var AttrNode = __dependency1__["default"]; + var fmt = __dependency2__.fmt; + var typeOf = __dependency3__.typeOf; + var read = __dependency4__.read; + var create = __dependency5__["default"]; - if (length) { - this._operations = [new ArrayOperation(RETAIN, length, items)]; - } else { - this._operations = []; - } + function LegacyBindAttrNode(attrName, attrValue) { + this.init(attrName, attrValue); } - TrackedArray.RETAIN = RETAIN; - TrackedArray.INSERT = INSERT; - TrackedArray.DELETE = DELETE; + LegacyBindAttrNode.prototype = create(AttrNode.prototype); - TrackedArray.prototype = { + LegacyBindAttrNode.prototype.render = function render(buffer) { + this.isDirty = false; + var value = read(this.attrValue); + var type = typeOf(value); - /** - Track that `newItems` were added to the tracked array at `index`. + Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [value]), + value === null || value === undefined || type === 'number' || type === 'string' || type === 'boolean'); - @method addItems - @param index - @param newItems - */ - addItems: function (index, newItems) { - var count = get(newItems, 'length'); - if (count < 1) { return; } + this._morph.setContent(value); - var match = this._findArrayOperation(index); - var arrayOperation = match.operation; - var arrayOperationIndex = match.index; - var arrayOperationRangeStart = match.rangeStart; - var composeIndex, newArrayOperation; + this.lastValue = value; + }; - newArrayOperation = new ArrayOperation(INSERT, count, newItems); + __exports__["default"] = LegacyBindAttrNode; + }); +enifed("ember-views/component_lookup", + ["ember-runtime/system/object","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var EmberObject = __dependency1__["default"]; - 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; - } + __exports__["default"] = EmberObject.extend({ + lookupFactory: function(name, container) { - this._composeInsert(composeIndex); - }, + container = container || this.container; - /** - Track that `count` items were removed at `index`. + var fullName = 'component:' + name; + var templateFullName = 'template:components/' + name; + var templateRegistered = container && container.has(templateFullName); - @method removeItems - @param index - @param count - */ - removeItems: function (index, count) { - if (count < 1) { return; } + if (templateRegistered) { + container.injection(fullName, 'layout', templateFullName); + } - var match = this._findArrayOperation(index); - var arrayOperationIndex = match.index; - var arrayOperationRangeStart = match.rangeStart; - var newArrayOperation, composeIndex; + var Component = container.lookupFactory(fullName); - 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; + // 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; - return this._composeDelete(composeIndex); - }, + /** + 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. + `Ember.ComponentTemplateDeprecation` is used internally by Ember in + `Ember.Component`. + + @class ComponentTemplateDeprecation + @namespace Ember + */ + __exports__["default"] = Mixin.create({ /** - Apply all operations, reducing them to retain:n, for `n`, the number of - items in the array. + @private - `callback` will be called for each operation and will be passed the following arguments: + Moves `templateName` to `layoutName` and `template` to `layout` at extend + time if a layout is not also specified. - * {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}` + 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 apply - @param {Function} callback + @method willMergeMixin + @since 1.4.0 */ - apply: function (callback) { - var items = []; - var offset = 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); - forEach(this._operations, function (arrayOperation, operationIndex) { - callback(arrayOperation.items, offset, arrayOperation.type, operationIndex); + var deprecatedProperty, replacementProperty; + var layoutSpecified = (props.layoutName || props.layout || get(this, 'layoutName')); - if (arrayOperation.type !== DELETE) { - offset += arrayOperation.count; - items = items.concat(arrayOperation.items); - } - }); + if (props.templateName && !layoutSpecified) { + deprecatedProperty = 'templateName'; + replacementProperty = 'layoutName'; - this._operations = [new ArrayOperation(RETAIN, items.length, items)]; - }, + props.layoutName = props.templateName; + delete props['templateName']; + } - /** - Return an `ArrayOperationMatch` for the operation that contains the item at `index`. + if (props.template && !layoutSpecified) { + deprecatedProperty = 'template'; + replacementProperty = 'layout'; - @method _findArrayOperation + props.layout = props.template; + delete props['template']; + } - @param {Number} index the index of the item whose operation information - should be returned. - @private - */ - _findArrayOperation: function (index) { - var split = false; - var arrayOperationIndex, arrayOperation, - arrayOperationRangeStart, arrayOperationRangeEnd, - len; + Ember.deprecate('Do not specify ' + deprecatedProperty + ' on a Component, use ' + replacementProperty + ' instead.', !deprecatedProperty); + } + }); + }); +enifed("ember-views/mixins/text_support", + ["ember-metal/property_get","ember-metal/property_set","ember-metal/mixin","ember-runtime/mixins/target_action_support","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-views + */ - // 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]; + var get = __dependency1__.get; + var set = __dependency2__.set; + var Mixin = __dependency3__.Mixin; + var TargetActionSupport = __dependency4__["default"]; - if (arrayOperation.type === DELETE) { continue; } + /** + `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. - arrayOperationRangeEnd = arrayOperationRangeStart + arrayOperation.count - 1; + 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: - if (index === arrayOperationRangeStart) { - break; - } else if (index > arrayOperationRangeStart && index <= arrayOperationRangeEnd) { - split = true; - break; - } else { - arrayOperationRangeStart = arrayOperationRangeEnd + 1; - } - } + ```handlebars + {{! application.hbs}} - return new ArrayOperationMatch(arrayOperation, arrayOperationIndex, split, arrayOperationRangeStart); - }, + {{input escape-press='alertUser'}} + ``` - _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); + ```javascript + App = Ember.Application.create(); - // truncate LHS - arrayOperation.count = splitIndex; - arrayOperation.items = arrayOperation.items.slice(0, splitIndex); + App.ApplicationController = Ember.Controller.extend({ + actions: { + alertUser: function ( currentValue ) { + alert( 'escape pressed, current value: ' + currentValue ); + } + } + }); + ``` - this._operations.splice(arrayOperationIndex + 1, 0, newArrayOperation, splitArrayOperation); - }, + 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: - // 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; + ```handlebars + {{input attribute-name='controllerAction'}} + ``` + + +--------------------+----------------+ + | | | + | event | attribute name | + +--------------------+----------------+ + | new line inserted | insert-newline | + | | | + | enter key pressed | insert-newline | + | | | + | cancel key pressed | escape-press | + | | | + | focusin | focus-in | + | | | + | focusout | focus-out | + | | | + | keypress | key-press | + | | | + | keyup | key-up | + | | | + | keydown | key-down | + +--------------------+----------------+ - if (leftOp === INSERT) { - // merge left - leftArrayOperation.count += newArrayOperation.count; - leftArrayOperation.items = leftArrayOperation.items.concat(newArrayOperation.items); + @class TextSupport + @namespace Ember + @uses Ember.TargetActionSupport + @extends Ember.Mixin + @private + */ + var TextSupport = Mixin.create(TargetActionSupport, { + value: "", - 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); - } + attributeBindings: [ + 'autocapitalize', + 'autocorrect', + 'autofocus', + 'disabled', + 'form', + 'maxlength', + 'placeholder', + 'readonly', + 'required', + 'selectionDirection', + 'spellcheck', + 'tabindex', + 'title' + ], + placeholder: null, + disabled: false, + maxlength: null, + + init: function() { + this._super(); + this.on("paste", this, this._elementValueDidChange); + this.on("cut", this, this._elementValueDidChange); + this.on("input", this, this._elementValueDidChange); }, - _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 = []; + /** + The action to be sent when the user presses the return key. - if (leftOp === DELETE) { - arrayOperation = leftArrayOperation; - index -= 1; - } + 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. - for (var i = index + 1; deletesToGo > 0; ++i) { - nextArrayOperation = this._operations[i]; - nextOp = nextArrayOperation.type; - nextCount = nextArrayOperation.count; + @property action + @type String + @default null + */ + action: null, - if (nextOp === DELETE) { - arrayOperation.count += nextCount; - continue; - } + /** + The event that should send the action. - 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; + Options are: - // 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; + * `enter`: the user pressed enter + * `keyPress`: the user pressed a key - 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 onEvent + @type String + @default enter + */ + onEvent: 'enter', - if (nextOp === INSERT) { - // d:2 i:3 will result in delete going away - arrayOperation.count -= nextCount; - } - } + /** + Whether the `keyUp` event that triggers an `action` to be sent continues + propagating to other views. - 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); - } + 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 removedItems; + If you would like parent views to receive the `keyUp` event even after an + action has been dispatched, set `bubbles` to true. + + @property bubbles + @type Boolean + @default false + */ + bubbles: false, + + interpretKeyEvents: function(event) { + var map = TextSupport.KEY_EVENTS; + var method = map[event.keyCode]; + + this._elementValueDidChange(); + if (method) { return this[method](event); } }, - toString: function () { - var str = ""; - forEach(this._operations, function (operation) { - str += " " + operation.type + ":" + operation.count; - }); - return str.substring(1); - } - }; + _elementValueDidChange: function() { + set(this, 'value', this.$().val()); + }, - /** - Internal data structure to represent an array operation. + change: function(event) { + this._elementValueDidChange(event); + }, - @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; - } + /** + 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. - /** - Internal data structure used to include information when looking up operations - by item index. + For an example on how to use the `insert-newline` attribute, please + reference the example near the top of this file. - @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-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"]; + @method insertNewline + @param {Event} event + */ + insertNewline: function(event) { + sendAction('enter', this, event); + sendAction('insert-newline', this, event); + }, - // to setup initializer - // to handle various edge cases + /** + 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. - var setupForTesting = __dependency4__["default"]; - var Test = __dependency5__["default"]; - var Adapter = __dependency6__["default"]; - var QUnitAdapter = __dependency7__["default"]; - // adds helpers to helpers object in Test + For an example on how to use the `escape-press` attribute, please reference + the example near the top of this file. - /** - Ember Testing + @method cancel + @param {Event} event + */ + cancel: function(event) { + sendAction('escape-press', this, event); + }, - @module ember - @submodule ember-testing - @requires ember-application - */ + /** + 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. - Ember.Test = Test; - Ember.Test.Adapter = Adapter; - Ember.Test.QUnitAdapter = QUnitAdapter; - Ember.setupForTesting = setupForTesting; - }); -enifed("ember-testing/adapters/adapter", - ["ember-metal/core","ember-runtime/system/object","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // Ember.K - var EmberObject = __dependency2__["default"]; + For an example on how to use the `focus-in` attribute, please reference the + example near the top of this file. - /** - @module ember - @submodule ember-testing - */ + @method focusIn + @param {Event} event + */ + focusIn: function(event) { + sendAction('focus-in', this, event); + }, - /** - The primary purpose of this class is to create hooks that can be implemented - by an adapter for various test frameworks. + /** + 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. + + For an example on how to use the `focus-out` attribute, please reference the + example near the top of this file. + + @method focusOut + @param {Event} event + */ + focusOut: function(event) { + this._elementValueDidChange(event); + sendAction('focus-out', this, event); + }, - @class Adapter - @namespace Ember.Test - */ - var Adapter = EmberObject.extend({ /** - This callback will be called whenever an async operation is about to start. + 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. - Override this to call your framework's methods that handle async - operations. + For an example on how to use the `key-press` attribute, please reference the + example near the top of this file. - @public - @method asyncStart + @method keyPress + @param {Event} event */ - asyncStart: Ember.K, + keyPress: function(event) { + sendAction('key-press', this, event); + }, /** - This callback will be called whenever an async operation has completed. + 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. - @public - @method asyncEnd + For an example on how to use the `key-up` attribute, please reference the + example near the top of this file. + + @method keyUp + @param {Event} event */ - asyncEnd: Ember.K, + keyUp: function(event) { + this.interpretKeyEvents(event); - /** - Override this method with your testing framework's false assertion. - This function is called whenever an exception occurs causing the testing - promise to fail. + this.sendAction('key-up', get(this, 'value'), event); + }, - QUnit example: + /** + 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. - ```javascript - exception: function(error) { - ok(false, error); - }; - ``` + For an example on how to use the `key-down` attribute, please reference the + example near the top of this file. - @public - @method exception - @param {String} error The exception to be raised. + @method keyDown + @param {Event} event */ - exception: function(error) { - throw error; + keyDown: function(event) { + this.sendAction('key-down', get(this, 'value'), event); } }); - __exports__["default"] = Adapter; - }); -enifed("ember-testing/adapters/qunit", - ["ember-testing/adapters/adapter","ember-metal/utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var Adapter = __dependency1__["default"]; - var inspect = __dependency2__.inspect; + TextSupport.KEY_EVENTS = { + 13: 'insertNewline', + 27: 'cancel' + }; - /** - This class implements the methods defined by Ember.Test.Adapter for the - QUnit testing framework. + // 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'); - @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)); + // 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); + + if (action || on === eventName) { + if(!get(view, 'bubbles')) { + event.stopPropagation(); + } + } + } + + __exports__["default"] = TextSupport; }); -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__) { +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 get = __dependency1__.get; - var EmberError = __dependency2__["default"]; - var run = __dependency3__["default"]; - var jQuery = __dependency4__["default"]; - var Test = __dependency5__["default"]; + var Mixin = __dependency1__.Mixin; + var TargetActionSupport = __dependency2__["default"]; + var alias = __dependency3__["default"]; /** - * @module ember - * @submodule ember-testing - */ - - var helper = Test.registerHelper; - var asyncHelper = Test.registerAsyncHelper; - var countAsync = 0; + `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. - function currentRouteName(app){ - var appController = app.__container__.lookup('controller:application'); + 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. - return get(appController, 'currentRouteName'); - } + For example: - function currentPath(app){ - var appController = app.__container__.lookup('controller:application'); + ```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 + } + }); + ``` - return get(appController, 'currentPath'); - } + The `action` can be provided as properties of an optional object argument + to `triggerAction` as well. - function currentURL(app){ - var router = app.__container__.lookup('router:main'); + ```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 + } + }); + ``` - return get(router, 'location').getURL(); - } + @class ViewTargetActionSupport + @namespace Ember + @extends Ember.TargetActionSupport + */ + __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 chain = __dependency1__.chain; + var read = __dependency1__.read; + var get = __dependency2__.get; + var dasherize = __dependency3__.dasherize; + var isArray = __dependency4__.isArray; - function pauseTest(){ - Test.adapter.asyncStart(); - return new Ember.RSVP.Promise(function(){ }, 'TestAdapter paused promise'); - } + /** + Parse a path and return an object which holds the parsed properties. - function visit(app, url) { - var router = app.__container__.lookup('router:main'); - router.location.setURL(url); + For example a path like "content.isEnabled:enabled:disabled" will return the + following object: - if (app._readinessDeferrals > 0) { - router['initialURL'] = url; - run(app, 'advanceReadiness'); - delete router['initialURL']; - } else { - run(app, app.handleURL, url); + ```javascript + { + path: "content.isEnabled", + className: "enabled", + falsyClassName: "disabled", + classNames: ":enabled:disabled" } + ``` - return app.testHelpers.wait(); - } + @method parsePropertyPath + @static + @private + */ + function parsePropertyPath(path) { + var split = path.split(':'); + var propertyPath = split[0]; + var classNames = ""; + var className, falsyClassName; - function click(app, selector, context) { - var $el = app.testHelpers.findWithAssert(selector, context); - run($el, 'mousedown'); + // 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]; + } - 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'); - } - }); + classNames = ':' + className; + if (falsyClassName) { + classNames += ":" + falsyClassName; } } - run($el, 'mouseup'); - run($el, 'click'); - - return app.testHelpers.wait(); + return { + path: propertyPath, + classNames: classNames, + className: (className === '') ? undefined : className, + falsyClassName: falsyClassName + }; } - function triggerEvent(app, selector, contextOrType, typeOrOptions, possibleOptions){ - var arity = arguments.length; - var context, type, options; - - 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; + __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; } - var $el = app.testHelpers.findWithAssert(selector, context); + // 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 event = jQuery.Event(type, options); + } else if (falsyClassName && !val) { + return falsyClassName; - run($el, 'trigger', event); + } else { + return null; + } - return app.testHelpers.wait(); - } + // 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]); - function keyEvent(app, selector, contextOrType, typeOrKeyCode, keyCode) { - var context, type; + // If the value is not false, undefined, or null, return the current + // value of the property. + } else if (val !== false && val != null) { + return val; - if (typeof keyCode === 'undefined') { - context = null; - keyCode = typeOrKeyCode; - type = contextOrType; + // Nothing to display. Return null so that the old class is removed + // but no new class is added. } else { - context = contextOrType; - type = typeOrKeyCode; + return null; } - - return app.testHelpers.triggerEvent(selector, context, type, { keyCode: keyCode, which: keyCode }); } - function fillIn(app, selector, contextOrText, text) { - var $el, context; - if (typeof text === 'undefined') { - text = contextOrText; + __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 { - context = contextOrText; - } - $el = app.testHelpers.findWithAssert(selector, context); - run(function() { - $el.val(text).change(); - }); - return app.testHelpers.wait(); - } - - function findWithAssert(app, selector, context) { - var $el = app.testHelpers.find(selector, context); - if ($el.length === 0) { - throw new EmberError("Element " + selector + " not found."); + var pathValue = view.getStream(prefix+parsedPath.path); + return chain(pathValue, function() { + return classStringForValue( + parsedPath.path, + read(pathValue), + parsedPath.className, + parsedPath.falsyClassName + ); + }); } - return $el; - } - - function find(app, selector, context) { - var $el; - context = context || get(app, 'rootElement'); - $el = app.$(selector, context); - - return $el; } - 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(); - } - - // 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; } - - // 2. If there are pending Ajax requests, keep polling - if (Test.pendingAjaxRequests) { return; } - - // 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); - - // If this is the last async promise, end the async test - if (--countAsync === 0) { - Test.adapter.asyncEnd(); - } + __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; - // Synchronously resolve the promise - run(null, resolve, value); - }, 10); - }); + function ConditionalStream(test, consequent, alternate) { + this.init(); + this.oldTestResult = undefined; + this.test = test; + this.consequent = consequent; + this.alternate = alternate; } + ConditionalStream.prototype = create(Stream.prototype); - /** - * 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); + ConditionalStream.prototype.valueFn = function() { + var oldTestResult = this.oldTestResult; + var newTestResult = !!read(this.test); - /** - 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. + 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); + } - This is most often used as the return value for the helper functions (see 'click', - 'fillIn','visit',etc). + switch (newTestResult) { + case true: subscribe(this.consequent, this.notify, this); break; + case false: subscribe(this.alternate, this.notify, this); + } - Example: + this.oldTestResult = newTestResult; + } - ```javascript - Ember.Test.registerAsyncHelper('loginUser', function(app, username, password) { - visit('secured/path/here') - .fillIn('#username', username) - .fillIn('#password', password) - .click('.submit') + return newTestResult ? read(this.consequent) : read(this.alternate); + }; - return app.testHelpers.wait(); - }); + __exports__["default"] = ConditionalStream; + }); +enifed("ember-views/streams/context_stream", + ["ember-metal/core","ember-metal/merge","ember-metal/platform","ember-metal/path_cache","ember-metal/streams/stream","ember-metal/streams/simple","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; - @method wait - @param {Object} value The value to be returned. - @return {RSVP.Promise} - */ - asyncHelper('wait', wait); - asyncHelper('andThen', andThen); + var merge = __dependency2__["default"]; + var create = __dependency3__.create; + var isGlobal = __dependency4__.isGlobal; + var Stream = __dependency5__["default"]; + var SimpleStream = __dependency6__["default"]; + function ContextStream(view) { + Ember.assert("ContextStream error: the argument is not a view", view && view.isView); - /** - Returns the currently active route name. + this.init(); + this.view = view; + } - Example: + ContextStream.prototype = create(Stream.prototype); - ```javascript - function validateRouteName(){ - equal(currentRouteName(), 'some.path', "correct route was transitioned into."); - } + merge(ContextStream.prototype, { + value: function() {}, - visit('/some/path').then(validateRouteName) - ``` + _makeChildStream: function(key, _fullPath) { + var stream; - @method currentRouteName - @return {Object} The name of the currently active route. - @since 1.5.0 - */ - helper('currentRouteName', currentRouteName); + 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)); + } - /** - Returns the current path. + stream._isRoot = true; - Example: + if (key === 'controller') { + stream._isController = true; + } - ```javascript - function validateURL(){ - equal(currentPath(), 'some.path.index', "correct path was transitioned into."); - } + return stream; + } + }); - click('#some-link-id').then(validateURL); - ``` + __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"]; - @method currentPath - @return {Object} The currently active path. - @since 1.5.0 - */ - helper('currentPath', currentPath); + 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; - /** - Returns the current URL. + 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); - Example: + this.init(); + this.source = source; + this.obj = undefined; + this.key = key; - ```javascript - function validateURL(){ - equal(currentURL(), '/some/path', "correct URL was transitioned into."); + if (isStream(source)) { + source.subscribe(this._didChange, this); + } } - click('#some-link-id').then(validateURL); - ``` + KeyStream.prototype = create(Stream.prototype); - @method currentURL - @return {Object} The currently active URL. - @since 1.5.0 - */ - helper('currentURL', currentURL); + merge(KeyStream.prototype, { + valueFn: function() { + var prevObj = this.obj; + var nextObj = read(this.source); - - /** - Pauses the current test - this is useful for debugging while testing or for test-driving. - It allows you to inspect the state of your application at any point. + if (nextObj !== prevObj) { + if (prevObj && typeof prevObj === 'object') { + removeObserver(prevObj, this.key, this, this._didChange); + } - Example (The test will pause before clicking the button): + if (nextObj && typeof nextObj === 'object') { + addObserver(nextObj, this.key, this, this._didChange); + } - ```javascript - visit('/') - return pauseTest(); + this.obj = nextObj; + } - click('.btn'); - ``` + if (nextObj) { + return get(nextObj, this.key); + } + }, - @method pauseTest - @return {Object} A promise that will never resolve - */ - helper('pauseTest', pauseTest); - + setValue: function(value) { + if (this.obj) { + set(this.obj, this.key, value); + } + }, - /** - Triggers the given DOM event on the element identified by the provided selector. + setSource: function(nextSource) { + Ember.assert("KeyStream error: source must be an object", typeof nextSource === 'object'); - Example: + var prevSource = this.source; - ```javascript - triggerEvent('#some-elem-id', 'blur'); - ``` + if (nextSource !== prevSource) { + if (isStream(prevSource)) { + prevSource.unsubscribe(this._didChange, this); + } - This is actually used internally by the `keyEvent` helper like so: + if (isStream(nextSource)) { + nextSource.subscribe(this._didChange, this); + } - ```javascript - triggerEvent('#some-elem-id', 'keypress', { keyCode: 13 }); - ``` + this.source = nextSource; + this.notify(); + } + }, - @method triggerEvent - @param {String} selector jQuery selector for finding element on the DOM - @param {String} [context] jQuery selector that will limit the selector - argument to find only within the context's children - @param {String} type The event type to be triggered. - @param {Object} [options] The options to be passed to jQuery.Event. - @return {RSVP.Promise} - @since 1.5.0 - */ - asyncHelper('triggerEvent', triggerEvent); - }); -enifed("ember-testing/initializers", - ["ember-runtime/system/lazy_load"], - function(__dependency1__) { - "use strict"; - var onLoad = __dependency1__.onLoad; + _didChange: function() { + this.notify(); + }, - var name = 'deferReadiness in `testing` mode'; + _super$destroy: Stream.prototype.destroy, - onLoad('Ember.Application', function(Application) { - if (!Application.initializers[name]) { - Application.initializer({ - name: name, + destroy: function() { + if (this._super$destroy()) { + if (isStream(this.source)) { + this.source.unsubscribe(this._didChange, this); + } - initialize: function(container, application){ - if (application.testing) { - application.deferReadiness(); - } + if (this.obj && typeof this.obj === 'object') { + removeObserver(this.obj, this.key, this, this._didChange); } - }); + + this.source = undefined; + this.obj = undefined; + return true; + } } }); + + __exports__["default"] = KeyStream; + + // The transpiler does not resolve cycles, so we export + // the `_makeChildStream` method onto `Stream` here. + + Stream.prototype._makeChildStream = function(key) { + return new KeyStream(this, key); + }; }); -enifed("ember-testing/setup_for_testing", - ["ember-metal/core","ember-testing/adapters/qunit","ember-views/system/jquery","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { +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"]; - // import Test from "ember-testing/test"; // ES6TODO: fix when cycles are supported - var QUnitAdapter = __dependency2__["default"]; - var jQuery = __dependency3__["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"]; - var Test, requests; + function readViewFactory(object, container) { + var value = read(object); + var viewClass; - function incrementAjaxPendingRequests(_, xhr){ - requests.push(xhr); - Test.pendingAjaxRequests = requests.length; + 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"}}.', !viewClass, { url: 'http://emberjs.com/guides/deprecations/#toc_global-lookup-of-views' }); + } else { + Ember.assert("View requires a container to resolve views not passed in through the context", !!container); + viewClass = container.lookupFactory('view:'+value); + } + } else { + viewClass = value; + } + + Ember.assert(fmt(value+" must be a subclass or an instance of Ember.View, not %@", [viewClass]), View.detect(viewClass) || View.detectInstance(viewClass)); + + return viewClass; } - function decrementAjaxPendingRequests(_, xhr){ - for (var i=0;i') - .css({ position: 'absolute', left: '-1000px', top: '-1000px' }) - .appendTo('body') - .on('click', handler) - .trigger('click') - .remove(); - } + __exports__["default"] = EmberObject.extend({ - $(function() { - /* - Determine whether a checkbox checked using jQuery's "click" method will have - the correct value for its checked property. + /** + 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 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. + This set will be modified by `setup` to also include any events added at that time. + + @property events + @type Object */ - 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; - } - } - }; + 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); } - }); - // 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"]; + rootElement = jQuery(get(this, 'rootElement')); - /** - @module ember - @submodule ember-testing - */ - var slice = [].slice; - var helpers = {}; - var injectHelpersCallbacks = []; + 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); - /** - This is a container for an assortment of testing related functionality: + rootElement.addClass('ember-application'); - * 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. + 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]); + } + } + }, + + /** + Registers an event listener on the rootElement. 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. + + @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; + + rootElement.on(event + '.ember', '.ember-view', function(evt, triggeringManager) { + var view = View.views[this.id]; + var result = true; + + var manager = self.canDispatchToEventManager ? self._findNearestEventManager(view, eventName) : null; + + if (manager && manager !== triggeringManager) { + result = self._dispatchEvent(manager, evt, eventName, view); + } else if (view) { + result = self._bubbleEvent(view, evt, eventName); + } + + return result; + }); + + rootElement.on(event + '.ember', '[data-ember-action]', function(evt) { + var actionId = jQuery(evt.currentTarget).attr('data-ember-action'); + var action = ActionManager.registeredActions[actionId]; + + // 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); + } + }); + }, - @class Test - @namespace Ember - */ - var Test = { - /** - Hash containing all known test helpers. + _findNearestEventManager: function(view, eventName) { + var manager = null; - @property _helpers - @private - @since 1.7.0 - */ - _helpers: helpers, + while (view) { + manager = get(view, 'eventManager'); + if (manager && manager[eventName]) { break; } - /** - `registerHelper` is used to register a test helper that will be injected - when `App.injectTestHelpers` is called. + view = get(view, 'parentView'); + } - The helper method will always be called with the current Application as - the first parameter. + return manager; + }, - For example: + _dispatchEvent: function(object, evt, eventName, view) { + var result = true; - ```javascript - Ember.Test.registerHelper('boot', function(app) { - Ember.run(app, app.advanceReadiness); - }); - ``` + 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); + } - This helper can later be called without arguments because it will be - called with `app` as the first parameter. + return result; + }, - ```javascript - App = Ember.Application.create(); - App.injectTestHelpers(); - boot(); - ``` + _bubbleEvent: function(view, evt, eventName) { + return run.join(view, view.handleEvent, eventName, evt); + }, - @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 } - }; + destroy: function() { + var rootElement = get(this, 'rootElement'); + jQuery(rootElement).off('.ember', '**').removeClass('ember-application'); + return this._super(); }, - /** - `registerAsyncHelper` is used to register an async test helper that will be injected - when `App.injectTestHelpers` is called. + toString: function() { + return '(EventDispatcher)'; + } + }); + }); +enifed("ember-views/system/ext", + ["ember-metal/run_loop"], + function(__dependency1__) { + "use strict"; + /** + @module ember + @submodule ember-views + */ - The helper method will always be called with the current Application as - the first parameter. + var run = __dependency1__["default"]; - For example: + // 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 - ```javascript - Ember.Test.registerAsyncHelper('boot', function(app) { - Ember.run(app, app.advanceReadiness); - }); - ``` + // ES6TODO: the functions on EnumerableUtils need their own exports + var forEach = __dependency2__.forEach; - 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. + /** + Ember Views + @module ember + @submodule ember-views + @requires ember-runtime + @main ember-views + */ - For example: + var jQuery = (Ember.imports && Ember.imports.jQuery) || (this && this.jQuery); + if (!jQuery && typeof eriuqer === 'function') { + jQuery = eriuqer('jquery'); + } - ```javascript - Ember.Test.registerAsyncHelper('deletePost', function(app, postId) { - click('.delete-' + postId); - }); + 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)); - // ... in your test - visit('/post/2'); - deletePost(2); - visit('/post/3'); - deletePost(3); - ``` + /** + @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' + ]; - @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 } + // 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'] }; - }, + }); + } - /** - Remove a previously added helper method. + __exports__["default"] = jQuery; + }); +enifed("ember-views/system/render_buffer", + ["ember-views/system/jquery","morph","ember-metal/core","ember-metal/platform","morph/dom-helper/prop","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-views + */ - Example: + var jQuery = __dependency1__["default"]; + var DOMHelper = __dependency2__.DOMHelper; + var Ember = __dependency3__["default"]; + var create = __dependency4__.create; + var normalizeProperty = __dependency5__.normalizeProperty; - ```javascript - Ember.Test.unregisterHelper('wait'); - ``` + // 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 + //